I just tried an augmented assignment on a frozenset, and the result surprised me:
>>> x = frozenset(['foo', 'bar', 'baz'])
>>> x
frozenset({'foo', 'baz', 'bar'})
>>> x &= {'baz', 'qux', 'quux'}
>>> x
frozenset({'baz'})
This isn't supposed to happen, is it? Aren't frozensets immutable?
Why are you surprised?
You knew the term "augmented assignment" so there is no problem finding the "Python Data Model on augmented arithmetic assignments" (emphasis mine):
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__'
So it has no __iand__
method so the code you perform is:
>>> x = x & {'baz', 'qux', 'quux'}
The __and__
method however is defined by frozenset
:
>>> x & {'baz', 'qux', 'quux'}
frozenset({'baz'})
However you lost your reference to the original 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'}))
But that just follows the "Principle of least astonishment". You wanted the __and__
and you made it clear that you didn't want to keep your original x
- an in-place operation also would have altered it!
So again: Why did that surprise you?