使用 frozenset 的增强赋值
Augmented assignment with frozenset
我刚刚在冻结集上尝试了增广赋值,结果让我感到惊讶:
>>> x = frozenset(['foo', 'bar', 'baz'])
>>> x
frozenset({'foo', 'baz', 'bar'})
>>> x &= {'baz', 'qux', 'quux'}
>>> x
frozenset({'baz'})
这不应该发生,是吗? frozensets 不是不可变的吗?
Frozensets 是不可变的,除了你的赋值没有改变原始的 frozenset - 你只是将变量 x
重新分配给你的二元运算符 &
。正如 user2357112 在评论中指出的那样,在找不到 __iand__
方法后,x &= {'baz', 'qux', 'quux'}
退回到 x = x & {'baz', 'qux', 'quux'}
,留下 non-mutating 操作。
对于不提供 __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
你为什么感到惊讶?
您知道 "augmented assignment" 这个词,所以找到 "Python Data Model on augmented arithmetic assignments"(强调我的)没有问题:
These [__i***__
] methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self). If a specific method is not defined, the augmented assignment falls back to the normal methods. For instance, if x
is an instance of a class with an __iadd__()
method, x += y is equivalent to x = x.__iadd__(y)
. Otherwise, x.__add__(y)
and y.__radd__(x)
are considered, [...]
>>> 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
- in-place 操作也会改变它!
再说一遍:为什么这让您感到惊讶?
我刚刚在冻结集上尝试了增广赋值,结果让我感到惊讶:
>>> x = frozenset(['foo', 'bar', 'baz'])
>>> x
frozenset({'foo', 'baz', 'bar'})
>>> x &= {'baz', 'qux', 'quux'}
>>> x
frozenset({'baz'})
这不应该发生,是吗? frozensets 不是不可变的吗?
Frozensets 是不可变的,除了你的赋值没有改变原始的 frozenset - 你只是将变量 x
重新分配给你的二元运算符 &
。正如 user2357112 在评论中指出的那样,在找不到 __iand__
方法后,x &= {'baz', 'qux', 'quux'}
退回到 x = x & {'baz', 'qux', 'quux'}
,留下 non-mutating 操作。
对于不提供 __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
你为什么感到惊讶?
您知道 "augmented assignment" 这个词,所以找到 "Python Data Model on augmented arithmetic assignments"(强调我的)没有问题:
These [
__i***__
] methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self). If a specific method is not defined, the augmented assignment falls back to the normal methods. For instance, ifx
is an instance of a class with an__iadd__()
method, x += y is equivalent tox = x.__iadd__(y)
. Otherwise,x.__add__(y)
andy.__radd__(x)
are considered, [...]
>>> 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
- in-place 操作也会改变它!
再说一遍:为什么这让您感到惊讶?