为什么 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

我想做的是:

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

但是,如果在函数中感到非常麻烦,我宁愿这样做:

        return self.add_award({
"name" : name,
"description" : desc_string % count,
"points" : points,
"parent_award" : parent,
}.update(award_dict), siteAlias, alias).award

为什么 update 不返回对象以便您可以链接?

为什么 update 不返回对象以便您可以链接?

JQuery 这样做是为了做链接。为什么在 python 中不能接受它?

JQuery 这样做是为了做链接。为什么在 python 中不能接受它?

83468 次浏览

如果您查看 Django 的 ORM,就会发现它广泛地使用了链接。这并不令人沮丧,您甚至可以从 dict继承,并且只覆盖 update来执行 update 和 return self,如果您真的想要它的话。

class myDict(dict):
def update(self, *args):
dict.update(self, *args)
return self
= foo.return ()

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

如果 return (就地反转列表)也会返回列表,用户可能会认为 return 返回一个分配给 bar 的新列表,而从来没有注意到 foo 也被修改了。通过使反向返回无,他们立即认识到酒吧不是反转的结果,并将看到更接近的效果是什么反向。

这样做的动机是,否则,你可能会得到不想要的副作用

bar = foo.reverse()

如果 return (就地反转列表)也会返回列表,用户可能会认为 return 返回一个分配给 bar 的新列表,而从来没有注意到 foo 也被修改了。通过使反向返回无,他们立即认识到酒吧不是反转的结果,并将看到更接近的效果是什么反向。

分数 = 分数,

Python 主要实现了一种带有实用色彩的 命令-查询分离: mutator 返回 None(带有实用导致的异常,如 pop; ——) ,因此它们不可能与访问器混淆(同样,赋值不是一个表达式,存在语句-表达式分离,等等)。

* * 获奖评选)

这并不意味着没有很多方法可以在你真正需要的时候把东西合并起来,例如,dict(a, **award_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作为唯一的 位置参数,将 之前作为关键字参数,并删除 **表单—— dict(award_dict, name=name等等)。

>>> dict_merge = lambda a,b: a.update(b) or a
>>> dict_merge({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

创建一个具有与 a.update(award_dict)完全相同的语义的单个 dict (包括,在冲突情况下,award_dict中的条目覆盖您显式给出的那些条目; 为了获得其他语义,即,让显式条目“赢得”这样的冲突,将 award_dict作为唯一的 位置参数,将 之前作为关键字参数,并删除 **表单—— dict(award_dict, name=name等等)。

或者,当然,你也可以做到这一切:

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

没有足够的声誉评论留在顶部的回答

资料单张

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

TypeError: 关键字参数必须是字符串

>>> dict({1:2}, **{'3':4})
{1: 2, '3': 4}
import itertools
dict_merge = lambda *args: dict(itertools.chain(*[d.iteritems() for d in args]))

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

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

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

我自己刚刚在 Python 3.4中尝试过(所以不能使用花哨的 {**dict_1, **dict_2}语法)。

Ted 不使用 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}

下面是我最后写的:

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}

就地操作的计时比较复杂,因此需要根据额外的复制操作对其进行修改(第一个计时仅供参考) :

%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)

对于那些迟到的人,我已经把一些时间放在一起(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)

通过连接项目列表合并:

d1 = {1: "one"}
d2 = {2: "two"}
dict(list(d1.items()) + list(d2.items()))
# {1: 'one', 2: 'two'}

就地操作的计时比较复杂,因此需要根据额外的复制操作对其进行修改(第一个计时仅供参考) :

%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)

像往常一样,所有的基准测试都应该持保留态度。

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)