Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #99764 > unrolled thread

Re: Could you explain this rebinding (or some other action) on "nums = nums"?

Started byfl <rxjwg98@gmail.com>
First post2015-11-30 18:14 -0800
Last post2015-12-01 17:37 -0500
Articles 10 — 6 participants

Back to article view | Back to comp.lang.python

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: Could you explain this rebinding (or some other action) on "nums = nums"? fl <rxjwg98@gmail.com> - 2015-11-30 18:14 -0800
    Re: Could you explain this rebinding (or some other action) on "nums = nums"? MRAB <python@mrabarnett.plus.com> - 2015-12-01 03:32 +0000
      Re: Could you explain this rebinding (or some other action) on "nums = nums"? Denis McMahon <denismfmcmahon@gmail.com> - 2015-12-01 20:32 +0000
        Re: Could you explain this rebinding (or some other action) on "nums = nums"? Ian Kelly <ian.g.kelly@gmail.com> - 2015-12-01 14:44 -0600
          Re: Could you explain this rebinding (or some other action) on "nums = nums"? Denis McMahon <denismfmcmahon@gmail.com> - 2015-12-01 21:37 +0000
            Re: Could you explain this rebinding (or some other action) on "nums = nums"? Erik <python@lucidity.plus.com> - 2015-12-01 22:34 +0000
            Re: Could you explain this rebinding (or some other action) on "nums = nums"? Erik <python@lucidity.plus.com> - 2015-12-01 22:44 +0000
        Re: Could you explain this rebinding (or some other action) on "nums = nums"? Terry Reedy <tjreedy@udel.edu> - 2015-12-01 16:18 -0500
          Re: Could you explain this rebinding (or some other action) on "nums = nums"? Denis McMahon <denismfmcmahon@gmail.com> - 2015-12-01 21:36 +0000
            Re: Could you explain this rebinding (or some other action) on "nums = nums"? Terry Reedy <tjreedy@udel.edu> - 2015-12-01 17:37 -0500

#99764 — Re: Could you explain this rebinding (or some other action) on "nums = nums"?

Fromfl <rxjwg98@gmail.com>
Date2015-11-30 18:14 -0800
SubjectRe: Could you explain this rebinding (or some other action) on "nums = nums"?
Message-ID<b48d4b38-a28a-4408-8271-f79e850f453f@googlegroups.com>
On Wednesday, June 24, 2015 at 8:17:08 PM UTC-4, Chris Angelico wrote:
> On Thu, Jun 25, 2015 at 9:52 AM, fl <rxjgmail.com> wrote:
> > The reason is that list implements __iadd__ like this (except in C, not Python):
> >
> > class List:
> >     def __iadd__(self, other):
> >         self.extend(other)
> >         return self
> > When you execute "nums += more", you're getting the same effect as:
> >
> > nums = nums.__iadd__(more)
> > which, because of the implementation of __iadd__, acts like this:
> >
> > nums.extend(more)
> > nums = nums
> > So there is a rebinding operation here, but first, there's a mutating operation, and the rebinding operation is a no-op.
> 
> It's not a complete no-op, as can be demonstrated if you use something
> other than a simple name:
> 
> >>> tup = ("spam", [1, 2, 3], "ham")
> >>> tup[1]
> [1, 2, 3]
> >>> tup[1].extend([4,5])
> >>> tup[1] = tup[1]
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> TypeError: 'tuple' object does not support item assignment
> >>> tup
> ('spam', [1, 2, 3, 4, 5], 'ham')
> >>> tup[1] += [6,7]
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> TypeError: 'tuple' object does not support item assignment
> >>> tup
> ('spam', [1, 2, 3, 4, 5, 6, 7], 'ham')
> 
> The reason for the rebinding is that += can do two completely
> different things: with mutable objects, like lists, it changes them in
> place, but with immutables, it returns a new one:
> 
> >>> msg = "Hello"
> >>> msg += ", world!"
> >>> msg
> 'Hello, world!'
> 
> This didn't change the string "Hello", because you can't do that.
> Instead, it rebound msg to "Hello, world!". For consistency, the +=
> operator will *always* rebind, but in situations where that's not
> necessary, it rebinds to the exact same object.
> 
> Does that answer the question?
> 
> ChrisA

I have revisit the past post. In the example code snippet:

type(tup[1])
Out[162]: list

'list' is mutable. Why does the following line have errors?
In practical Python code, error is not acceptable. Then, what purpose is
for the following code here to show?

Thanks,


>>> tup[1] += [6,7] 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
TypeError: 'tuple' object does not support item assignment 

[toc] | [next] | [standalone]


#99765

FromMRAB <python@mrabarnett.plus.com>
Date2015-12-01 03:32 +0000
Message-ID<mailman.51.1448940760.14615.python-list@python.org>
In reply to#99764
On 2015-12-01 02:14, fl wrote:
> On Wednesday, June 24, 2015 at 8:17:08 PM UTC-4, Chris Angelico wrote:
>> On Thu, Jun 25, 2015 at 9:52 AM, fl <rxjgmail.com> wrote:
>> > The reason is that list implements __iadd__ like this (except in C, not Python):
>> >
>> > class List:
>> >     def __iadd__(self, other):
>> >         self.extend(other)
>> >         return self
>> > When you execute "nums += more", you're getting the same effect as:
>> >
>> > nums = nums.__iadd__(more)
>> > which, because of the implementation of __iadd__, acts like this:
>> >
>> > nums.extend(more)
>> > nums = nums
>> > So there is a rebinding operation here, but first, there's a mutating operation, and the rebinding operation is a no-op.
>>
>> It's not a complete no-op, as can be demonstrated if you use something
>> other than a simple name:
>>
>> >>> tup = ("spam", [1, 2, 3], "ham")
>> >>> tup[1]
>> [1, 2, 3]
>> >>> tup[1].extend([4,5])
>> >>> tup[1] = tup[1]
>> Traceback (most recent call last):
>>   File "<stdin>", line 1, in <module>
>> TypeError: 'tuple' object does not support item assignment
>> >>> tup
>> ('spam', [1, 2, 3, 4, 5], 'ham')
>> >>> tup[1] += [6,7]
>> Traceback (most recent call last):
>>   File "<stdin>", line 1, in <module>
>> TypeError: 'tuple' object does not support item assignment
>> >>> tup
>> ('spam', [1, 2, 3, 4, 5, 6, 7], 'ham')
>>
>> The reason for the rebinding is that += can do two completely
>> different things: with mutable objects, like lists, it changes them in
>> place, but with immutables, it returns a new one:
>>
>> >>> msg = "Hello"
>> >>> msg += ", world!"
>> >>> msg
>> 'Hello, world!'
>>
>> This didn't change the string "Hello", because you can't do that.
>> Instead, it rebound msg to "Hello, world!". For consistency, the +=
>> operator will *always* rebind, but in situations where that's not
>> necessary, it rebinds to the exact same object.
>>
>> Does that answer the question?
>>
>> ChrisA
>
> I have revisit the past post. In the example code snippet:
>
> type(tup[1])
> Out[162]: list
>
> 'list' is mutable. Why does the following line have errors?
> In practical Python code, error is not acceptable. Then, what purpose is
> for the following code here to show?
>
> Thanks,
>
>
>>>> tup[1] += [6,7]
> Traceback (most recent call last):
>    File "<stdin>", line 1, in <module>
> TypeError: 'tuple' object does not support item assignment
>
When you write:

     x += y

Python tries to do:

     x = x.__iadd__(y)

If x doesn't have the "__iadd__" method, Python then tries to do:

     x = x.__add__(y)

The "__iadd__" method should mutate the object in-place and then return
itself.

Here's an example where it returns something else instead:

# Example

class Test:
     def __init__(self):
         self.string = ''

     def __iadd__(self, other):
         self.string += other
         return "Surprise!"

t = Test()
t += 'foo'
print(t)

# End of example

In the case of:

     tup[1] += [6, 7]

what it's trying to do is:

     tup[1] = tup[1].__iadd__([6, 7])

tup[1] refers to a list, and the __iadd__ method _does_ mutate it, but
then Python tries to put the result that the method returns into
tup[1]. That fails because tup itself is a tuple, which is immutable.

[toc] | [prev] | [next] | [standalone]


#99815

FromDenis McMahon <denismfmcmahon@gmail.com>
Date2015-12-01 20:32 +0000
Message-ID<n3l050$l7a$4@dont-email.me>
In reply to#99765
On Tue, 01 Dec 2015 03:32:31 +0000, MRAB wrote:

> In the case of:
> 
>      tup[1] += [6, 7]
> 
> what it's trying to do is:
> 
>      tup[1] = tup[1].__iadd__([6, 7])
> 
> tup[1] refers to a list, and the __iadd__ method _does_ mutate it, but
> then Python tries to put the result that the method returns into tup[1].
> That fails because tup itself is a tuple, which is immutable.

I think I might have found a bug:

$ python
Python 2.7.3 (default, Jun 22 2015, 19:33:41) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> tup = [1,2,3],[4,5,6]
>>> tup
([1, 2, 3], [4, 5, 6])
>>> tup[1]
[4, 5, 6]
>>> tup[1] += [7,8,9]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> tup[1]
[4, 5, 6, 7, 8, 9]
>>> tup
([1, 2, 3], [4, 5, 6, 7, 8, 9])
>>> quit()

-- 
Denis McMahon, denismfmcmahon@gmail.com

[toc] | [prev] | [next] | [standalone]


#99816

FromIan Kelly <ian.g.kelly@gmail.com>
Date2015-12-01 14:44 -0600
Message-ID<mailman.83.1449002726.14615.python-list@python.org>
In reply to#99815
On Tue, Dec 1, 2015 at 2:32 PM, Denis McMahon <denismfmcmahon@gmail.com> wrote:
> On Tue, 01 Dec 2015 03:32:31 +0000, MRAB wrote:
>
>> In the case of:
>>
>>      tup[1] += [6, 7]
>>
>> what it's trying to do is:
>>
>>      tup[1] = tup[1].__iadd__([6, 7])
>>
>> tup[1] refers to a list, and the __iadd__ method _does_ mutate it, but
>> then Python tries to put the result that the method returns into tup[1].
>> That fails because tup itself is a tuple, which is immutable.
>
> I think I might have found a bug:
>
> $ python
> Python 2.7.3 (default, Jun 22 2015, 19:33:41)
> [GCC 4.6.3] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
>>>> tup = [1,2,3],[4,5,6]
>>>> tup
> ([1, 2, 3], [4, 5, 6])
>>>> tup[1]
> [4, 5, 6]
>>>> tup[1] += [7,8,9]
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> TypeError: 'tuple' object does not support item assignment
>>>> tup[1]
> [4, 5, 6, 7, 8, 9]
>>>> tup
> ([1, 2, 3], [4, 5, 6, 7, 8, 9])
>>>> quit()

No, that's the expected result. As MRAB wrote, the list *is* mutated
when its __iadd__ method is called. The TypeError happens afterward
when the assignment is attempted.

[toc] | [prev] | [next] | [standalone]


#99821

FromDenis McMahon <denismfmcmahon@gmail.com>
Date2015-12-01 21:37 +0000
Message-ID<n3l3vk$l7a$6@dont-email.me>
In reply to#99816
On Tue, 01 Dec 2015 14:44:38 -0600, Ian Kelly wrote:

> On Tue, Dec 1, 2015 at 2:32 PM, Denis McMahon <denismfmcmahon@gmail.com>
> wrote:
>> On Tue, 01 Dec 2015 03:32:31 +0000, MRAB wrote:
>>
>>> In the case of:
>>>
>>>      tup[1] += [6, 7]
>>>
>>> what it's trying to do is:
>>>
>>>      tup[1] = tup[1].__iadd__([6, 7])
>>>
>>> tup[1] refers to a list, and the __iadd__ method _does_ mutate it, but
>>> then Python tries to put the result that the method returns into
>>> tup[1].
>>> That fails because tup itself is a tuple, which is immutable.
>>
>> I think I might have found a bug:
>>
>> $ python Python 2.7.3 (default, Jun 22 2015, 19:33:41)
>> [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license"
>> for more information.
>>>>> tup = [1,2,3],[4,5,6]
>>>>> tup
>> ([1, 2, 3], [4, 5, 6])
>>>>> tup[1]
>> [4, 5, 6]
>>>>> tup[1] += [7,8,9]
>> Traceback (most recent call last):
>>   File "<stdin>", line 1, in <module>
>> TypeError: 'tuple' object does not support item assignment
>>>>> tup[1]
>> [4, 5, 6, 7, 8, 9]
>>>>> tup
>> ([1, 2, 3], [4, 5, 6, 7, 8, 9])
>>>>> quit()
> 
> No, that's the expected result. As MRAB wrote, the list *is* mutated
> when its __iadd__ method is called. The TypeError happens afterward when
> the assignment is attempted.

The assignment succeeds. That's imo a bug. If it's a TypeError to try and 
assign a value to tup[1], then tup[1] should not allow the mutated list 
to be assigned.

-- 
Denis McMahon, denismfmcmahon@gmail.com

[toc] | [prev] | [next] | [standalone]


#99824

FromErik <python@lucidity.plus.com>
Date2015-12-01 22:34 +0000
Message-ID<mailman.88.1449009296.14615.python-list@python.org>
In reply to#99821
On 01/12/15 21:37, Denis McMahon wrote:
> The assignment succeeds. That's imo a bug. If it's a TypeError to try and
> assign a value to tup[1], then tup[1] should not allow the mutated list
> to be assigned.

Nothing got assigned.

That original list object remains in that slot. However, it has been 
mutated since it was originally assigned.

But I can see what you're getting at - what you're asking for is that 
the *container* object whose element is being assigned to is first 
queried as to whether it will accept a mutated element being assigned to 
it before that element is mutated.

This looks to be a fundamental issue with the augmented assignment 
mechanism.

E.

[toc] | [prev] | [next] | [standalone]


#99827

FromErik <python@lucidity.plus.com>
Date2015-12-01 22:44 +0000
Message-ID<mailman.91.1449009930.14615.python-list@python.org>
In reply to#99821
Apologies for self-replying,

On 01/12/15 22:34, Erik wrote:
>  what you're asking for is that
> the *container* object whose element is being assigned to is first
> queried as to whether it will accept a mutated element being assigned to
> it before that element is mutated.

What I said above is rubbish. The situation is approximately similar to:

a = [4, 5, 6]
t = ([1, 2, 4], a)
a.append(7)
a.append(8)
a.append(9)

The point is, you're mutating something that an immutable object 
contains. In the example you give, that's caught because of the 
subsequent explicit assignment.

In the example above, it's not caught. But it's the same thing.

E.

[toc] | [prev] | [next] | [standalone]


#99818

FromTerry Reedy <tjreedy@udel.edu>
Date2015-12-01 16:18 -0500
Message-ID<mailman.84.1449004772.14615.python-list@python.org>
In reply to#99815
On 12/1/2015 3:32 PM, Denis McMahon wrote:
> On Tue, 01 Dec 2015 03:32:31 +0000, MRAB wrote:
>
>> In the case of:
>>
>>       tup[1] += [6, 7]
>>
>> what it's trying to do is:
>>
>>       tup[1] = tup[1].__iadd__([6, 7])
>>
>> tup[1] refers to a list, and the __iadd__ method _does_ mutate it, but
>> then Python tries to put the result that the method returns into tup[1].
>> That fails because tup itself is a tuple, which is immutable.
>
> I think I might have found a bug:

What you found is an specific example of what MRAB said in general above.

> $ python
> Python 2.7.3 (default, Jun 22 2015, 19:33:41)
> [GCC 4.6.3] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
>>>> tup = [1,2,3],[4,5,6]
>>>> tup
> ([1, 2, 3], [4, 5, 6])
>>>> tup[1]
> [4, 5, 6]
>>>> tup[1] += [7,8,9]
> Traceback (most recent call last):
>    File "<stdin>", line 1, in <module>
> TypeError: 'tuple' object does not support item assignment

The bug is trying to replace a member of a tuple.  The correct code, to 
avoid the exception while extending the list, is

tup[1].extend([7,8,9])

>>>> tup[1]
> [4, 5, 6, 7, 8, 9]

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#99820

FromDenis McMahon <denismfmcmahon@gmail.com>
Date2015-12-01 21:36 +0000
Message-ID<n3l3si$l7a$5@dont-email.me>
In reply to#99818
On Tue, 01 Dec 2015 16:18:49 -0500, Terry Reedy wrote:

> On 12/1/2015 3:32 PM, Denis McMahon wrote:
>> On Tue, 01 Dec 2015 03:32:31 +0000, MRAB wrote:
>>
>>> In the case of:
>>>
>>>       tup[1] += [6, 7]
>>>
>>> what it's trying to do is:
>>>
>>>       tup[1] = tup[1].__iadd__([6, 7])
>>>
>>> tup[1] refers to a list, and the __iadd__ method _does_ mutate it, but
>>> then Python tries to put the result that the method returns into
>>> tup[1].
>>> That fails because tup itself is a tuple, which is immutable.
>>
>> I think I might have found a bug:
> 
> What you found is an specific example of what MRAB said in general
> above.
> 
>> $ python Python 2.7.3 (default, Jun 22 2015, 19:33:41)
>> [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license"
>> for more information.
>>>>> tup = [1,2,3],[4,5,6]
>>>>> tup
>> ([1, 2, 3], [4, 5, 6])
>>>>> tup[1]
>> [4, 5, 6]
>>>>> tup[1] += [7,8,9]
>> Traceback (most recent call last):
>>    File "<stdin>", line 1, in <module>
>> TypeError: 'tuple' object does not support item assignment
> 
> The bug is trying to replace a member of a tuple.  The correct code, to
> avoid the exception while extending the list, is
> 
> tup[1].extend([7,8,9])
> 
>>>>> tup[1]
>> [4, 5, 6, 7, 8, 9]

You snipped the important bit of my original post, which was the state of 
tup after the TypeError occurred.

After the error, 

>>> tup[1]
[4, 5, 6, 7, 8, 9]
>>> tup
([1, 2, 3], [4, 5, 6, 7, 8, 9])

The "bug" I refer to is that despite giving the TypeError, the tuple 
allowed the assignment of the mutated list to replace the original list.

-- 
Denis McMahon, denismfmcmahon@gmail.com

[toc] | [prev] | [next] | [standalone]


#99825

FromTerry Reedy <tjreedy@udel.edu>
Date2015-12-01 17:37 -0500
Message-ID<mailman.89.1449009497.14615.python-list@python.org>
In reply to#99820
On 12/1/2015 4:36 PM, Denis McMahon wrote:
> On Tue, 01 Dec 2015 16:18:49 -0500, Terry Reedy wrote:
>
>> On 12/1/2015 3:32 PM, Denis McMahon wrote:
>>> On Tue, 01 Dec 2015 03:32:31 +0000, MRAB wrote:
>>>
>>>> In the case of:
>>>>
>>>>        tup[1] += [6, 7]
>>>>
>>>> what it's trying to do is:
>>>>
>>>>        tup[1] = tup[1].__iadd__([6, 7])
>>>>
>>>> tup[1] refers to a list, and the __iadd__ method _does_ mutate it, but
>>>> then Python tries to put the result that the method returns into
>>>> tup[1].
>>>> That fails because tup itself is a tuple, which is immutable.
>>>
>>> I think I might have found a bug:
>>
>> What you found is an specific example of what MRAB said in general
>> above.
>>
>>> $ python Python 2.7.3 (default, Jun 22 2015, 19:33:41)
>>> [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license"
>>> for more information.
>>>>>> tup = [1,2,3],[4,5,6]
>>>>>> tup
>>> ([1, 2, 3], [4, 5, 6])
>>>>>> tup[1]
>>> [4, 5, 6]
>>>>>> tup[1] += [7,8,9]
>>> Traceback (most recent call last):
>>>     File "<stdin>", line 1, in <module>
>>> TypeError: 'tuple' object does not support item assignment
>>
>> The bug is trying to replace a member of a tuple.  The correct code, to
>> avoid the exception while extending the list, is
>>
>> tup[1].extend([7,8,9])
>>
>>>>>> tup[1]
>>> [4, 5, 6, 7, 8, 9]
>
> You snipped the important bit of my original post, which was the state of
> tup after the TypeError occurred.

No I did not.  The change to tup[1] right there above, after giving the 
correct way to make the change.

> After the error,

>>>> tup[1]
> [4, 5, 6, 7, 8, 9]

This is exactly what I posted.

>>>> tup
> ([1, 2, 3], [4, 5, 6, 7, 8, 9])

This is a repeat of the same thing and adds no new info.

> The "bug" I refer to is that despite giving the TypeError, the tuple
> allowed the assignment of the mutated list to replace the original list.

No it did not.  As MRAB noted, the list is mutated and the attempted 
assignment causes an exeption. This has been discussed before more than 
once ever since augmented *assignment* was introduced.

 >>> tup = ([],[])
 >>> id(tup[1])
711662188872

 >>> tup[1] += [1]
Traceback (most recent call last):
   File "<pyshell#2>", line 1, in <module>
     tup[1] += [1]
TypeError: 'tuple' object does not support item assignment
 >>> tup[1]
[1]
 >>> id(tup[1])
711662188872

 >>> tup[1] = tup[1].extend([2])
Traceback (most recent call last):
   File "<pyshell#7>", line 1, in <module>
     tup[1] = tup[1].extend([2])
TypeError: 'tuple' object does not support item assignment
 >>> tup[1]
[1, 2]
 >>> id(tup[1])
711662188872

Reading the augmented assignment doc carefully should make it clear that 
"tup[1] += [1]" is the same as "tup[1] = tup[1].extend([2])" except that 
evaluation of "tup[1]" happens just once instead of twice.

-- 
Terry Jan Reedy

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web