This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Augmented assigment to mutable objects in tuples fail
Type: Stage:
Components: Interpreter Core Versions: Python 2.4
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: georg.brandl, mwh, pje, yorick
Priority: normal Keywords:

Created on 2005-09-28 10:59 by yorick, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
PySequence_SetItem.diff georg.brandl, 2005-09-28 12:17
Messages (9)
msg26423 - (view) Author: Mattias Engdegård (yorick) Date: 2005-09-28 10:59
>>> t=(set([2]),)
>>> t[0] |= set([7])
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: object does not support item assignment

but the contained set is mutable, and in fact:
>>>  t[0].__ior__(set([7]))
set([2, 7])
>>> t
(set([2, 7]),)

If I use a mutable container (a list) in the first
case, it works:
>>> u=[set([2])]
>>> u[0] |= set([7])
>>> u
[set([2, 7])]

But note that the list has not been mutated - only the
set, so there would be no need for a mutable container.
This is highly counter-intuitive - augmented assigment
should do in-place operations on mutable types (which
it does) and should therefore pose no restriction on
the mutability of the container (which fails).
msg26424 - (view) Author: Michael Hudson (mwh) (Python committer) Date: 2005-09-28 11:50
Logged In: YES 
user_id=6656

Yuck, I agree that that's pretty icky.  But the disassembly makes things 
clear:

>>> dis.dis(compile('t[0] |= a', '', 'single'))
  1           0 LOAD_NAME                0 (t)
              3 LOAD_CONST               0 (0)
              6 DUP_TOPX                 2
              9 BINARY_SUBSCR       
             10 LOAD_NAME                1 (a)
             13 INPLACE_OR          
             14 ROT_THREE           
             15 STORE_SUBSCR        
             16 LOAD_CONST               1 (None)
             19 RETURN_VALUE        

In fact...

>>> s = set([1])
>>> t = (s,)
>>> t[0] |= set([2])
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: object does not support item assignment
>>> s
set([1, 2])
>>> 

Oof.

Not sure what to do about this.
msg26425 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2005-09-28 12:17
Logged In: YES 
user_id=1188172

Generally there are two possibilites for the __ixxx__ methods:
1) Modify self and return self
2) Create a new instance with desired attributes and return it
    (necessary for e.g. integers)

The second case cannot be handled by immutable containers.

Hmm, maybe PySequence_SetItem should check whether the
assigned item is already there and then succeed.

Attaching a minimal patch for PySequence_SetItem (not sure
about PyObject_SetItem).
msg26426 - (view) Author: Mattias Engdegård (yorick) Date: 2005-09-28 12:24
Logged In: YES 
user_id=432579

Thank you for your analysis.
I'm not intimitely familiar with the bytecodes, but one way
would be to omit the writeback (STORE_SUBSCR) if the result
of INPLACE_OR is identical to its input. This could probably
be done without changing the bytecodes but might profit from
some changes for speed and compactness.

That is (pseudocode):
_t1 = t[i]
_t2 = inplace_or(_t1, a)
if _t2 is not _t1:
    t[i] = _t2

Another variant would be to add indexed variants of the
augmented assigment methods; that is, augmented variants of
__setitem__, but that has other drawbacks. However, it might
be useful in its own regard in some cases.
msg26427 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2005-09-28 12:41
Logged In: YES 
user_id=1188172

The bytecode generation happens before any code is executed,
so the generated bytecode for x |= y is always the same
(except, perhaps, when constants are involved).
msg26428 - (view) Author: Mattias Engdegård (yorick) Date: 2005-09-28 12:52
Logged In: YES 
user_id=432579

Certainly, but I meant that we could emit bytecode to
compare the result of INPLACE_OR and do a conditional writeback.
msg26429 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2005-09-28 13:13
Logged In: YES 
user_id=1188172

I don't know. This way, __setitem__ would not be called even
if it exists. That may pose b/w compatibility problems. I'm
asking python-dev.
msg26430 - (view) Author: PJ Eby (pje) * (Python committer) Date: 2005-09-28 14:27
Logged In: YES 
user_id=56214

This is not a bug.  The language-defined behavior of
augmented assignment is that:

  lvalue |= rvalue

translates to:

  lvalue = lvalue.__ior__(rvalue)

Since in this example the lvalue cannot be assigned to, it
is your code that has the bug. Note that some objects'
__ior__ method may not return the same object!  Therefore,
using a tuple to store the target of an in-place update
would only work with objects whose __ior__ returns self.  If
you know that this will always be the case in your code,
then simply do this:

  ts = t[0]
  ts |= set([7])

Again, note that this means that if t[0].__ior__ does not
return self, then ts will not be the same object as t[0],
leaving the tuple un-updated.  In short, you can't use
augmented assignments to tuple items in the general case.

Note, by the way, that sets have other methods for in-place
updating, and your code would probably be clearer using one
of those, since no lvalue confusion arises.  The whole
purpose of augmented assignment is to deal with the
possibility that an in-place update might or might not
result in the same object. If an object offers a method that
unequivocally mutates it, your code will be clearer using that.
msg26431 - (view) Author: Mattias Engdegård (yorick) Date: 2005-09-28 16:22
Logged In: YES 
user_id=432579

Then maybe the language definition is in error since its
consequences are counter-intuitive and impractical. Would
any reasonable code break if it was modified to what a naïve
user would expect? For instance, the conditional writeback
suggested earlier.
History
Date User Action Args
2022-04-11 14:56:13adminsetgithub: 42425
2005-09-28 10:59:42yorickcreate