程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Four problems of tuple+= assignment in Python

編輯:Python

Recently, I occasionally read 《Fluent Python》, When you come across something interesting, write it down . The following is in PyCon2013 A question about tuple Of Augmented Assignment That is, the problem of incremental assignment . And based on this problem , It also extends 3 Variant questions .

problem
First, let's look at the first question , Such as the following code snippet :

>>> t = (1,2, [30,40])
>>> t[2] += [50,60]

What will happen ? Four options are given :

1. `t` become `[1,2, [30,40,50,60]`
2. `TypeError is raised with the message 'tuple' object does not support item assignment`
3. Neither 1 nor 2
4. Both 1 and 2

As previously understood , tuple The elements inside cannot be modified , So I will choose 2.

If so , This note is not necessary ,《Fluent Python》 So I won't take out a section .

The right answer is 4 :

>>> t = (1,2,[30,40])
>>> t[2] += [50,60]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, [30, 40, 50, 60])

The problem is coming. , Why do all the anomalies come out ,t Still changed ?

Look at the second case , A little change , take += Turn into = :

>>> t = (1,2, [30,40])
>>> t[2] = [50,60]

It turns out to be dark brown :

>>> t = (1,2, [30,40])
>>> t[2] = [50,60]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, [30, 40])

Look at the third case , Only += Replace with extend perhaps append :

>>> t = (1, 2, [30,40])
>>> t[2].extend([50,60])
>>> t
(1, 2, [30, 40, 50, 60])
>>> t[2].append(70)
>>> t
(1, 2, [30, 40, 50, 60, 70])

It's normal again , No exception was thrown ?

Finally, the fourth case , In the form of variables :

>>> a = [30,40]
>>> t = (1, 2, a)
>>> a+=[50,60]
>>> a
[30, 40, 50, 60]
>>> t
(1, 2, [30, 40, 50, 60])
>>> t[2] += [70,80]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, [30, 40, 50, 60, 70, 80])

Another situation , Let's explore the reasons .

reason
First, we need to review += This operator , Such as a+=b:

  • For mutable objects (mutable object) Such as list, += The result of the operation will be directly in a Modify the corresponding variable , and a The corresponding address remains unchanged .
  • For immutable objects (imutable object) Such as tuple, += Is equivalent to a = a+b New variables will be generated , Then bind to a It's just .

The following code snippet , You can see it :

>>> a = [1,2,3]
>>> id(a)
53430752
>>> a+=[4,5]
>>> a
[1, 2, 3, 4, 5]
>>> id(a)
53430752 # The address hasn't changed
>>> b = (1,2,3)
>>> id(b)
49134888
>>> b += (4,5)
>>> b
(1, 2, 3, 4, 5)
>>> id(b)
48560912 # The address has changed

In addition, it should be noted that , python Medium tuple As immutable objects , That is, the elements we usually say cannot be changed , In fact, from the error message TypeError: 'tuple' object does not support item assignment Look at , More precisely, the element does not support assignment =(assignment).

Let's first look at the simplest second case , Its result is in line with our expectations , because = Produced assign The operation of .( From an example to python The namespace of Assignment operations are indicated in = Is to create a new variable ), therefore s[2]=[50,60] Will throw an exception .

Look at the third case , contain extend/append Of , result tuple The list value in has changed , But no exception was thrown . This is also relatively easy to understand . Because we know tuple What is stored in is actually the address corresponding to the element (id), So if there is no assignment and tuple Of elements in id unchanged , that will do , and list.extend/append Just changed the elements of the list , And the list itself id There is no change , Take a look at the following example :

>>> a=(1,2,[30,40])
>>> id(a[2])
140628739513736
>>> a[2].extend([50,60])
>>> a
(1, 2, [30, 40, 50, 60])
>>> id(a[2])
140628739513736

At present, the second and third problems have been solved , Let's sort it out first , Actually, there are two points :

  • tuple Internal elements do not support assignment operations
  • On the basis of Article 1 , If the element's id There is no change , Elements can actually be changed .

Now let's look at the first question : t[2] += [50,60] According to the above conclusion , You shouldn't throw exceptions , Because in our opinion += For mutable objects t[2] Come on , Belong to in-place operation , That is, you can directly modify your own content , id It doesn't change , To confirm the id There is no change :

>>> a=(1,2,[30,40])
>>> id(a[2])
140628739587392
>>> a[2]+=[50,60]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> a
(1, 2, [30, 40, 50, 60])
>>> id(a[2]) # ID Nothing has changed
140628739587392

With the third question, just from t[2].extend Changed to t[2]+=, Throw an exception , So the problem should be += Yes . The following is used dis The module looks at the steps that both of them perform , Execute... On the following code block dis:

t = (1,2, [30,40])
t[2] += [50,60]
t[2].extend([70, 80])

perform python -m dis test.py, give the result as follows , Only the... Is reserved below 2,3 Line code execution process , And notes on key steps are as follows :

2 21 LOAD_NAME 0 (t)
24 LOAD_CONST 1 (2)
27 DUP_TOPX 2
30 BINARY_SUBSCR
31 LOAD_CONST 4 (50)
34 LOAD_CONST 5 (60)
37 BUILD_LIST 2
40 INPLACE_ADD
41 ROT_THREE
42 STORE_SUBSCR
3 43 LOAD_NAME 0 (t)
46 LOAD_CONST 1 (2)
49 BINARY_SUBSCR
50 LOAD_ATTR 1 (extend)
53 LOAD_CONST 6 (70)
56 LOAD_CONST 7 (80)
59 BUILD_LIST 2
62 CALL_FUNCTION 1
65 POP_TOP
66 LOAD_CONST 8 (None)
69 RETURN_VALUE

Explain the key statements :

  • 30 BINARY_SUBSCR: It means that you will t[2] The value is placed in TOS(Top of Stack), Here means [30, 40] This list
  • 40 INPLACE_ADD: Express TOS += [50,60] This step can be successful , Revised TOS The list of is [30,40,50,60]
  • 42 STORE_SUBSCR: Express s[2] = TOS That's the problem , Here comes an assignment operation , Therefore, exceptions will be thrown ! However, the above changes to the list have been completed , This explains the first question at the beginning .

Look again extend The process of , The front is the same , Only this line :

  • 62 CALL_FUNCTION: This direct call is built-in extend Function completes the modification of the original list , There is no assign operation , So it can be executed normally .

Now it is becoming clear , let me put it another way ,+= It's not an atomic operation , It is equivalent to the following two steps :

t[2].extend([50,60])
t[2] = t[2]

The first step can be performed correctly , But the second step is =, It's bound to throw exceptions . This can also explain the use of += When , why t[2] Of id Obviously there is no change , But it still throws an exception .

Now let's sum it up in one sentence :

tuple Element in does not support assign operation , But for elements that are mutable objects, such as lists , Dictionary, etc , In the absence of assign On the basis of operation , For example, some in-place operation , The content can be modified

You can use the fourth question to simply verify , Use a pointer to [30,40] The name of a As the value of the element , Then on a do in-place Modification of , There is no reference to tuple Of assign operation , That must be normal .

The above is all the content shared this time , Want to know more python Welcome to official account :Python Programming learning circle , send out “J” Free access to , Daily dry goods sharing


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved