使用frozenset进行增强分配

时间:2017-02-05 03:15:07

标签: python set immutability augmented-assignment

我刚试过一个关于冻结集的增强赋值,结果让我感到惊讶:

objectGUID

这不应该发生,是吗?不是不可变的吗?

2 个答案:

答案 0 :(得分:2)

Frozensets 不可变的,除了你的赋值没有改变原始的冻结集 - 你只是将变量x重新分配给二元运算符&的结果。正如评论中的user2357112所述,x &= {'baz', 'qux', 'quux'}在找不到__iand__方法后会回到x = x & {'baz', 'qux', 'quux'},让您进行非变异操作。

对于不提供__iand__的不可变类型的其他扩充操作,可以看到此行为,例如:

In[1]: x = (1, 2, 3)
In[2]: id(x)
Out[2]: 761782862328
In[3]: x += (4, 5)
In[4]: id(x)   # we now have a different id
Out[4]: 761780182888
In[5]: x[2] = 3  # an actual mutating operation
TypeError: 'tuple' object does not support item assignment

答案 1 :(得分:2)

你为什么感到惊讶?

你知道术语"增加了作业"所以找到"Python Data Model on augmented arithmetic assignments"(强调我的)是没有问题的:

  

这些[__i***__]方法应该尝试就地执行操作(修改self)并返回结果(可能是,但不一定是self)。 如果未定义特定方法,则扩充分配将回退到常规方法。例如,如果x是具有__iadd__()方法的类的实例,则x + = y相当于x = x.__iadd__(y)。否则,x.__add__(y)y.__radd__(x)被视为[...]

>>> x = frozenset(['foo', 'bar', 'baz'])
>>> x.__iand__
[...]
AttributeError: 'frozenset' object has no attribute '__iand__'

因此它没有__iand__方法,因此您执行的代码是:

>>> x = x & {'baz', 'qux', 'quux'}

__and__方法由frozenset定义:

>>> x & {'baz', 'qux', 'quux'}
frozenset({'baz'})

但是,您丢失了对原始frozenset的引用:x

>>> y = x   # that doesn't do a copy, it's just to check if `x` has changed"
>>> x &= {'baz', 'qux', 'quux'}
>>> x is y  # check if they reference the same object!
False
>>> x, y
(frozenset({'baz'}), frozenset({'bar', 'baz', 'foo'}))

但这只是"Principle of least astonishment"之后。您想要__and__,并且您明确表示您不想保留原始x - 就地操作也会改变它!

再说一遍:为什么这让你感到惊讶?