it-swarm.asia

为什么python dict.update()没有返回对象?

我想做:

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不接受它?

111
Paul Tarjan

Python主要实现 command-query separation 的实用主义风格:mutators返回None(带有实际引发的异常,例如pop ;-),因此它们不可能与访问器混淆(同样,分配是不是表达式,语句表达式分离就在那里,等等。

这并不意味着当你真正想要的时候没有很多方法可以合并,例如,dict(a, **award_dict)使得一个新的dict非常像你希望.update返回的那个 - 所以为什么不使用那个如果你真的觉得这一点很重要?

编辑 :顺便说一下,在你的具体情况下,不需要在整个过程中创建a

dict(name=name, description=desc % count, points=points, parent_award=parent,
     **award_dict)

创建一个与a.update(award_dict)具有完全相同语义的单个dict(如果发生冲突,则包括award_dict中的条目覆盖您明确给出的条目;获取其他语义,即显式条目“获胜”等冲突,传递award_dict作为唯一的位置 arg,before关键字1,以及**形式 - dict(award_dict, name=name等等)。

185
Alex Martelli

按照惯例,Python的API区分了过程和函数。函数从其参数(包括任何目标对象)中计算新值;过程修改对象并且不返回任何内容(即它们返回None)。所以程序有副作用,功能没有。 update是一个过程,因此它不返回值。

这样做的动机是否则会产生不良副作用。考虑

bar = foo.reverse()

如果反向(就地反转列表)也会返回列表,用户可能会认为反向返回一个新的列表,该列表被分配给bar,并且从未注意到foo也被修改。通过反向返回None,他们立即意识到bar不是反转的结果,并且看起来更接近反向的效果。

32
Martin v. Löwis
>>> 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之外,它还会就地修改第一个参数。所以dict_merge(a,b)会修改一个。

或者,当然,你可以全部内联:

>>> (lambda a,b: a.update(b) or a)({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}
13
Crispin Wellington

没有足够的评论声誉留在最佳答案

@beardc这似乎不是CPython的事情。 PyPy给我“TypeError:关键字必须是字符串”

只有**kwargs的解决方案才有效,因为要合并的字典只有 类型为字符串的键

即.

>>> dict({1:2}, **{3:4})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

vS

>>> dict({1:2}, **{'3':4})
{1: 2, '3': 4}
7
Stephan Scheller

它不是不可接受的,而是dicts没有以这种方式实现。

如果你看看Django的ORM,它会广泛使用链接。它没有气馁,你甚至可以从dict继承而只覆盖update来做更新和return self,如果你真的想要的话。

class myDict(dict):
    def update(self, *args):
        dict.update(self, *args)
        return self
5
Esteban Küber

这很简单:

(lambda d: d.update(dict2) or d)(d1)
4
Kostya Goloveshko

尽可能接近你提出的解决方案

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
2
Matus

刚刚在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}
0
freebie
import itertools
dict_merge = lambda *args: dict(itertools.chain(*[d.iteritems() for d in args]))
0
Matt