A puzzle about + =

Posted by jimdavidson on Mon, 28 Feb 2022 05:40:58 +0100

Original link: A puzzle about + =

I found a problem in reading today. It's very interesting to share with you.

What are the results of the following two Python expressions?

t = (1, 2, [3, 4])
t[2] += [5, 6]

Give four alternative answers:

  1. t becomes (1, 2, [3, 4, 5, 6]).
  2. Because tuple does not support assigning values to its elements, it throws a TypeError exception.
  3. Neither of the above.
  4. Both of the above are right.

Seeing this problem at that time, the first reaction was to choose 2. Because tuple is an immutable object, it does not support the assignment of its elements, and an error will be reported.

But in fact, the positive solution of this problem is 4.

Verify in the terminal:

Python 3.8.2 (default, Oct  2 2020, 10:45:42)
[Clang 12.0.0 (clang-1200.0.32.27)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> t = (1, 2, [3, 4])
>>> t[2] += [5, 6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

The result is no problem. t has been modified, but it also reported an error.

You can also analyze it on Python Tutor:

Website address: https://pythontutor.com/

This website can visually analyze the running process and principle of Python.

Execute the first expression:

Execute the second expression:

Why? It can be explained from two aspects:

1, Object type

Objects in Python can be divided into two categories, variable objects and immutable objects, such as some built-in types:

  1. Variable objects: list, set, dict.
  2. Immutable objects: int, float, bool, string, tuple.

For example:

Variable object:

>>> a = [1, 2, 3]
>>> id(a)
2139167246856
>>> b = a
>>> id(b)
2139167246856
>>> a[1] = 4
>>> a
[1, 4, 3]
>>> b
[1, 4, 3]
>>> id(a)
2139167246856
>>> id(b)
2139167246856

As you can see, when you change a, b also changes, because they always point to the same address.

Immutable object:

>>> a = (1, 2, 3)
>>> id(a)
2139167074776
>>> b = a
>>> a = (4, 5, 6)
>>> a
(4, 5, 6)
>>> b
(1, 2, 3)
>>> id(a)
2139167075928
>>> id(b)
2139167074776

It can be seen that after the value of a changes, its address also changes, while b is still the original address, and the content of the original address does not change.

2, Bytecode

First, explain what bytecode is?

When Python executes a program, it will compile the source file into a bytecode file and store it in the__ In the pycahe directory, the files are End of pyc. After that, if the source code file is not modified, it will be used directly at runtime pyc files are compiled into machine code, which not only runs fast, but also supports multiple operating systems.

Bytecode is actually an intermediate code.

Let's use the dis module to see the execution process of the expression s[a] += b:

>>> import dis
>>> dis.dis('s[a] += b')
  1           0 LOAD_NAME                0 (s)
              2 LOAD_NAME                1 (a)
              4 DUP_TOP_TWO
              6 BINARY_SUBSCR
              8 LOAD_NAME                2 (b)
             10 INPLACE_ADD
             12 ROT_THREE
             14 STORE_SUBSCR
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE
>>>

By analyzing bytecode, we can see three key steps:

  1. 4 DUP_TOP_TWO: store s[a] in TOS (Top Of Stack).
  2. 10 INPLACE_ADD: execute TOS += b and bring it into the expression at the beginning of the article, which is equivalent to adding elements to t[2]. Because t[2] is a list and a variable object, there is no problem with this operation.
  3. 14 STORE_ Subcr: save the result back to s[a] = TOS, which is equivalent to re assigning the result back to T. since t is a tuple and immutable object, an error is reported.

Although this problem may not be common in normal development, there are still many knowledge points that can be deeply explored through analysis.

Briefly summarize the following three points:

  1. Don't put mutable objects in tuples.
  2. Incremental assignment is not an atomic operation. As we saw just now, although it threw an exception, it still completed the operation.
  3. It's not difficult to look at Python's bytecode, and it's very helpful for us to understand the operation mechanism behind the code.

The above is the whole content of this article. If you think it's good, you're welcome to praise and forward it. Thank you

Recommended reading:

  • Computer classic books (including download method)
  • Technology blog : hard core back-end development technology dry goods, including Python, Django, Docker, Go, Redis, ElasticSearch, Kafka, Linux, etc.
  • Go programmer : Go learning roadmap, including a series of resources such as basic column, advanced column, source code reading, practical development, interview question brushing, required reading list, etc.
  • Summary of interview questions : including Python, Go, Redis, MySQL, Kafka, data structure, algorithm, programming, network and other frequently asked questions.

Topics: Python data structure list