私がやろうとしている:
award_dict = {
"url" : "http://facebook.com",
"imageurl" : "http://farm4.static.flickr.com/3431/3939267074_feb9eb19b1_o.png",
"count" : 1,
}
def award(name, count, points, desc_string, my_size, parent) :
if my_size > count :
a = {
"name" : name,
"description" : desc_string % count,
"points" : points,
"parent_award" : parent,
}
a.update(award_dict)
return self.add_award(a, siteAlias, alias).award
しかし、機能が本当に面倒だと感じたら、私はむしろやったでしょう:
return self.add_award({
"name" : name,
"description" : desc_string % count,
"points" : points,
"parent_award" : parent,
}.update(award_dict), siteAlias, alias).award
更新してもオブジェクトが返されないので、チェーンできるのはなぜですか?
JQueryはこれを行って連鎖を行います。なぜPythonでは受け入れられないのですか?
Pythonは、 コマンドとクエリの分離 の実用的な色合いのフレーバーを主に実装しています。ミューテーターは、None
を返します(pop
;-)アクセサーと混同される可能性はありません(同じように、代入は式ではなく、ステートメントと式の分離があります)。
つまり、本当に欲しいときに物事をマージする方法があまりないというわけではありません。たとえば、dict(a, **award_dict)
は.update
が返されることを望むように見えるものと同じような新しい辞書を作成します。本当にそれが重要だと感じたら?
Edit:ところで、特定の場合、途中でa
を作成する必要はありません。
dict(name=name, description=desc % count, points=points, parent_award=parent,
**award_dict)
a.update(award_dict)
とまったく同じセマンティクスで単一の辞書を作成します(競合の場合、award_dict
のエントリが明示的に与えているものをオーバーライドするという事実を含みます;他のセマンティクスを取得する、つまり明示的にするそのような競合に「勝つ」エントリは、award_dict
を唯一のpositional arg、beforeキーワードのもの、**
形式のbereft --dict(award_dict, name=name
など)として渡します。
PythonのAPIは、慣例により、プロシージャと関数を区別します。関数は、パラメーター(ターゲットオブジェクトを含む)から新しい値を計算します。プロシージャはオブジェクトを変更し、何も返しません(つまり、Noneを返します)。したがって、プロシージャには副作用がありますが、関数にはありません。更新はプロシージャであるため、値を返しません。
そうしないと、望ましくない副作用が発生する可能性があります。検討する
bar = foo.reverse()
Reverse(インプレースでリストを逆にする)がリストを返す場合、ユーザーはreverseがbarに割り当てられた新しいリストを返し、fooも変更されることに気付かないかもしれません。リバースリターンをNoneにすると、バーはリバースの結果ではないことをすぐに認識し、リバースの効果が何であるかをより近くに見えます。
>>> dict_merge = lambda a,b: a.update(b) or a
>>> dict_merge({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}
マージされた辞書を返すだけでなく、最初のパラメーターをその場で変更することに注意してください。したがって、dict_merge(a、b)はaを変更します。
または、もちろん、すべてインラインで実行できます。
>>> (lambda a,b: a.update(b) or a)({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}
これは簡単です:
(lambda d: d.update(dict2) or d)(d1)
トップアンサーに残されたコメントの評判が不十分です
@beardcこれはCPythonのものではないようです。 PyPyから「TypeError:キーワードは文字列でなければなりません」と表示されます
**kwargs
を使用したソリューションは、マージされるディクショナリがstring型のキーのみを持つためにのみ機能します。
つまり.
>>> dict({1:2}, **{3:4})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings
対
>>> dict({1:2}, **{'3':4})
{1: 2, '3': 4}
受け入れられないということではなく、dicts
がそのように実装されていないということです。
DjangoのORMを見ると、連鎖を広く利用しています。非推奨ではありません。dict
から継承し、update
のみをオーバーライドして更新とreturn self
、本当に必要な場合。
class myDict(dict):
def update(self, *args):
dict.update(self, *args)
return self
私が得ることができる限りあなたの提案されたソリューションに近い
from collections import ChainMap
return self.add_award(ChainMap(award_dict, {
"name" : name,
"description" : desc_string % count,
"points" : points,
"parent_award" : parent,
}), siteAlias, alias).award
パーティーに遅れて来る人のために、私はいくつかのタイミングをまとめました(Py 3.7)。入力が保存されると.update()
ベースのメソッドが少し(〜5%)速く、そして顕著に(〜30%)見えることを示しましたインプレース更新するだけで高速になります。
いつものように、すべてのベンチマークは一粒の塩で取得する必要があります。
def join2(dict1, dict2, inplace=False):
result = dict1 if inplace else dict1.copy()
result.update(dict2)
return result
def join(*items):
iter_items = iter(items)
result = next(iter_items).copy()
for item in iter_items:
result.update(item)
return result
def update_or(dict1, dict2):
return dict1.update(dict2) or dict1
d1 = {i: str(i) for i in range(1000000)}
d2 = {str(i): i for i in range(1000000)}
%timeit join2(d1, d2)
# 258 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit join(d1, d2)
# 262 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit dict(d1, **d2)
# 267 ms ± 2.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit {**d1, **d2}
# 267 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
インプレース操作のタイミングは少し複雑なので、追加のコピー操作に合わせて変更する必要があります(最初のタイミングは参照用です)。
%timeit dd = d1.copy()
# 44.9 ms ± 495 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit dd = d1.copy(); join2(dd, d2)
# 296 ms ± 2.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit dd = d1.copy(); join2(dd, d2, True)
# 234 ms ± 1.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit dd = d1.copy(); update_or(dd, d2)
# 235 ms ± 1.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Python 3.4でこれを自分で試してみました(したがって、ファンシーな{**dict_1, **dict_2}
構文を使用できませんでした)。
辞書に文字列以外のキーを持ち、任意の量の辞書を提供できるようにしたかったのです。
また、新しい辞書を作成したかったので、collections.ChainMap
を使用しないことにしました(最初はdict.update
を使用したくなかった理由です)。
これが私が書いたものです:
def merge_dicts(*dicts):
all_keys = set(k for d in dicts for k in d.keys())
chain_map = ChainMap(*reversed(dicts))
return {k: chain_map[k] for k in all_keys}
merge_maps({'1': 1}, {'2': 2, '3': 3}, {'1': 4, '3': 5})
# {'1': 4, '3': 5, '2': 2}
import itertools
dict_merge = lambda *args: dict(itertools.chain(*[d.iteritems() for d in args]))