辞書を「分解」して、値をそのキーの後の変数名に関連付けようとしています。何かのようなもの
params = {'a':1,'b':2}
a,b = params.values()
ただし、辞書は順序付けられていないparams.values()
ため、(a, b)
。の順序で値を返すという保証はありません。これを行うための良い方法はありますか?
辞書を「分解」して、値をそのキーの後の変数名に関連付けようとしています。何かのようなもの
params = {'a':1,'b':2}
a,b = params.values()
ただし、辞書は順序付けられていないparams.values()
ため、(a, b)
。の順序で値を返すという保証はありません。これを行うための良い方法はありますか?
let {a, b} = params
。それは読みやすさを向上させ、あなたが話したい禅と完全に一致します。
let {a: waffles} = params
。あなたがそれに慣れていても、それを理解するのに数秒かかります。
回答:
ローカル辞書の使用に伴う問題を恐れて、元の戦略に従うことを好む場合は、Python2.7および3.1コレクションのOrderedDictionaries.OrderedDictsを使用すると、辞書アイテムを最初に挿入された順序で復元できます。
from operator import itemgetter
params = {'a': 1, 'b': 2}
a, b = itemgetter('a', 'b')(params)
手の込んだラムダ関数や辞書の理解の代わりに、組み込みのライブラリを使用することもできます。
attrgetter
オブジェクト属性(obj.a
)で機能する同じ標準ライブラリモジュールから使用するように回答を拡張することもできます。これは、JavaScriptとの大きな違いobj.a === obj["a"]
です。
KeyError
例外が発生します
a, b = [d[k] for k in ('a','b')]
いるように、はるかに自然で読みやすいです(フォームははるかに一般的です)。これはまだ興味深い答えですが、それは最も簡単な解決策ではありません。
get_id = itemgetter(KEYS)
、後で使用するようなものの方serial, code, ts = get_id(document)
が簡単です。確かに、高階関数に慣れている必要がありますが、Pythonは一般的にそれらに非常に慣れています。たとえば、のようなデコレータのドキュメントを参照してください@contextmanager
。
Jochenの提案よりも少ない繰り返しでこれを行う1つの方法は、ヘルパー関数を使用することです。これにより、変数名を任意の順序で一覧表示し、dictにあるもののサブセットのみを分解する柔軟性が得られます。
pluck = lambda dict, *args: (dict[arg] for arg in args)
things = {'blah': 'bleh', 'foo': 'bar'}
foo, blah = pluck(things, 'foo', 'blah')
また、joaquinのOrderedDictの代わりに、キーを並べ替えて値を取得することもできます。唯一の落とし穴は、変数名をアルファベット順に指定し、dict内のすべてを非構造化する必要があることです。
sorted_vals = lambda dict: (t[1] for t in sorted(dict.items()))
things = {'foo': 'bar', 'blah': 'bleh'}
blah, foo = sorted_vals(things)
lambda
、変数にaを割り当てる場合は、通常の関数構文をdef
。で使用することをお勧めします。
itemgetter
から実装しoperator
ました。:)
Pythonはシーケンスを「分解」することしかできず、辞書はできません。したがって、必要なものを作成するには、必要なエントリを適切なシーケンスにマップする必要があります。私の場合、私が見つけた最も近い一致は(あまりセクシーではありません)です:
a,b = [d[k] for k in ('a','b')]
これはジェネレーターでも機能します。
a,b = (d[k] for k in ('a','b'))
完全な例を次に示します。
>>> d = dict(a=1,b=2,c=3)
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> a, b = [d[k] for k in ('a','b')]
>>> a
1
>>> b
2
>>> a, b = (d[k] for k in ('a','b'))
>>> a
1
>>> b
2
たぶんあなたは本当にこのようなことをしたいですか?
def some_func(a, b):
print a,b
params = {'a':1,'b':2}
some_func(**params) # equiv to some_func(a=1, b=2)
JSで破壊的代入が機能するのと同様にそれを行う別の方法は次のとおりです。
params = {'b': 2, 'a': 1}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
私たちが行ったのは、paramsディクショナリをキー値にアンパックすることでした(**を使用)(Jochenの回答のように)、ラムダ署名でそれらの値を取得し、キー名に従って割り当てました-そしてここにボーナスがあります-私たちはまた、ラムダの署名に含まれていないものの辞書を取得します。
params = {'b': 2, 'a': 1, 'c': 3}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
ラムダが適用された後、rest変数には次のものが含まれます:{'c':3}
辞書から不要なキーを省略するのに便利です。
お役に立てれば。
警告1:ドキュメントに記載されているように、これはすべてのPython実装で機能することが保証されているわけではありません。
CPython実装の詳細:この関数は、インタープリターでのPythonスタックフレームのサポートに依存しています。これは、Pythonのすべての実装に存在することが保証されているわけではありません。Pythonスタックフレームサポートのない実装で実行している場合、この関数はNoneを返します。
警告2:この関数はコードを短くしますが、可能な限り明示的にするというPythonの哲学と矛盾する可能性があります。さらに、コメントでジョン・クリストファー・ジョーンズが指摘した問題には対処していませんが、キーの代わりに属性を使用して機能する同様の関数を作成することはできます。これは、本当に必要な場合にそれを実行できることを示す単なるデモンストレーションです。
def destructure(dict_):
if not isinstance(dict_, dict):
raise TypeError(f"{dict_} is not a dict")
# the parent frame will contain the information about
# the current line
parent_frame = inspect.currentframe().f_back
# so we extract that line (by default the code context
# only contains the current line)
(line,) = inspect.getframeinfo(parent_frame).code_context
# "hello, key = destructure(my_dict)"
# -> ("hello, key ", "=", " destructure(my_dict)")
lvalues, _equals, _rvalue = line.strip().partition("=")
# -> ["hello", "key"]
keys = [s.strip() for s in lvalues.split(",") if s.strip()]
if missing := [key for key in keys if key not in dict_]:
raise KeyError(*missing)
for key in keys:
yield dict_[key]
In [5]: my_dict = {"hello": "world", "123": "456", "key": "value"}
In [6]: hello, key = destructure(my_dict)
In [7]: hello
Out[7]: 'world'
In [8]: key
Out[8]: 'value'
このソリューションでは、JavaScriptのように、すべてではなく一部のキーを選択できます。ユーザー提供の辞書にも安全です
さて、クラスでこれらが必要な場合は、いつでもこれを行うことができます:
class AttributeDict(dict):
def __init__(self, *args, **kwargs):
super(AttributeDict, self).__init__(*args, **kwargs)
self.__dict__.update(self)
d = AttributeDict(a=1, b=2)
辞書はPython> = 3.7で挿入順序を維持することが保証されているためでこれは、今日これを行うだけで完全に安全で慣用的なことを意味します。
params = {'a': 1, 'b': 2}
a, b = params.values()
print(a)
print(b)
出力:
1 2
b, a = params.values()
名前ではなく順序を使用しているため、実行できないことです。
itemgetter
これを行うための最もPython的な方法です。これはPython3.6以下では機能せず、順序に依存しているため、最初は混乱する可能性があります。
いいスタイルかどうかはわかりませんが
locals().update(params)
トリックを行います。次に、がありa
、辞書にあるb
ものはすべてparams
、対応するローカル変数として使用できます。