自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Python 為什么只需一條語句“a,b=b,a”,就能直接交換兩個(gè)變量?

開發(fā) 后端
從接觸 Python 時(shí)起,我就覺得 Python 的元組解包(unpacking)挺有意思,非常簡(jiǎn)潔好用。

 從接觸 Python 時(shí)起,我就覺得 Python 的元組解包(unpacking)挺有意思,非常簡(jiǎn)潔好用。

[[333771]]

最顯而易見的例子就是多重賦值,即在一條語句中同時(shí)給多個(gè)變量賦值:

 

  1. >>> x, y = 1, 2 
  2. >>> print(x, y)  # 結(jié)果:1 2 

在此例中,賦值操作符“=”號(hào)的右側(cè)的兩個(gè)數(shù)字會(huì)被存入到一個(gè)元組中,即變成 (1,2),然后再被解包,依次賦值給“=”號(hào)左側(cè)的兩個(gè)變量。

如果我們直接寫x = 1,2 ,然后打印出 x,或者在“=”號(hào)右側(cè)寫成一個(gè)元組,就能證實(shí)到這一點(diǎn):

 

  1. >>> x = 1, 2 
  2. >>> print(x)     # 結(jié)果:(1, 2) 
  3. >>> x, y = (1, 2) 
  4. >>> print(x, y)  # 結(jié)果:1 2 

一些博客或公眾號(hào)文章在介紹到這個(gè)特性時(shí),通常會(huì)順著舉一個(gè)例子,即基于兩個(gè)變量,直接交換它們的值:

 

  1. >>> x, y = 1, 2 
  2. >>> x, y = y, x 
  3. >>> print(x, y) # 結(jié)果:2 1 

一般而言,交換兩個(gè)變量的操作需要引入第三個(gè)變量。道理很簡(jiǎn)單,如果要交換兩個(gè)杯子中所裝的水,自然會(huì)需要第三個(gè)容器作為中轉(zhuǎn)。

然而,Python 的寫法并不需要借助中間變量,它的形式就跟前面的解包賦值一樣。正因?yàn)檫@個(gè)形式相似,很多人就誤以為 Python 的變量交換操作也是基于解包操作。

但是,事實(shí)是否如此呢?

我搜索了一番,發(fā)現(xiàn)有人試圖回答過這個(gè)問題,但是他們的回答基本不夠全面。(當(dāng)然,有不少是錯(cuò)誤的答案,還有更多人只是知其然,卻從未想過要知其所以然)

先把本文的答案放出來吧:Python 的交換變量操作不完全基于解包操作,有時(shí)候是,有時(shí)候不是!

有沒有覺得這個(gè)答案很神奇呢?是不是聞所未聞?!

到底怎么回事呢?先來看看標(biāo)題中最簡(jiǎn)單的兩個(gè)變量的情況,我們上dis 大殺器看看編譯的字節(jié)碼:

 

 

上圖開了兩個(gè)窗口,可以方便比較“a,b=b,a”與“a,b=1,2”的不同:

 

 

  • “a,b=b,a”操作:兩個(gè) LOAD_FAST 是從局部作用域中讀取變量的引用,并存入棧中,接著是最關(guān)鍵的 ROT_TWO 操作,它會(huì)交換兩個(gè)變量的引用值,然后兩個(gè) STORE_FAST 是將棧中的變量寫入局部作用域中。
  • “a,b=1,2”操作:第一步 LOAD_CONST 把“=”號(hào)右側(cè)的兩個(gè)數(shù)字作為元組放到棧中,第二步 UNPACK_SEQUENCE 是序列解包,接著把解包結(jié)果寫入局部作用域的變量上。

很明顯,形式相似的兩種寫法實(shí)際上完成的操作并不相同。在交換變量的操作中,并沒有裝包和解包的步驟!

ROT_TWO 指令是 CPython 解釋器實(shí)現(xiàn)的對(duì)于棧頂兩個(gè)元素的快捷操作,改變它們指向的引用對(duì)象。

還有兩個(gè)類似的指令是 ROT_THREE 和 ROT_FOUR,分別是快捷交換三和四個(gè)變量(摘自:ceval.c 文件,最新的 3.9 分支):

 

 

 

 

預(yù)定義的棧頂操作如下:

 

 

 

 

查看官方文檔中對(duì)于這幾個(gè)指令的解釋,其中 ROT_FOUR 是 3.8 版本新加的:

 

  1. ROT_TWO 
  2.  
  3. Swaps the two top-most stack items. 
  4.  
  5.  
  6. ROT_THREE 
  7.  
  8. Lifts second and third stack item one position up, moves top down to position three. 
  9.  
  10.  
  11. ROT_FOUR 
  12.  
  13. Lifts second, third and forth stack items one position up, moves top down to position four. 
  14. New in version 3.8. 

 

CPython 應(yīng)該是以為這幾種變量的交換操作很常見,因此才提供了專門的優(yōu)化指令。就像 [-5,256] 這些小整數(shù)被預(yù)先放到了整數(shù)池里一樣。

對(duì)于更多變量的交換操作,實(shí)際上則會(huì)用到前面說的解包操作:

 

 

 

 

截圖中的 BUILD_TUPLE 指令會(huì)將給定數(shù)量的棧頂元素創(chuàng)建成元組,然后被 UNPACK_SEQUENCE 指令解包,再依次賦值。

值得一提的是,此處之所以比前面的“a,b=1,2”多出一個(gè) build 操作,是因?yàn)槊總€(gè)變量的 LOAD_FAST 需要先單獨(dú)入棧,無法直接被組合成 LOAD_CONST 入棧。也就是說,“=”號(hào)右側(cè)有變量時(shí),不會(huì)出現(xiàn)前文中的 LOAD_CONST 一個(gè)元組的情況。

最后還有一個(gè)值得一提的細(xì)節(jié),那幾個(gè)指令是跟棧中元素的數(shù)量有關(guān),而不是跟賦值語句中實(shí)際交換的變量數(shù)有關(guān)。看一個(gè)例子就明白了:

 

 

 

 

分析至此,你應(yīng)該明白前文中的結(jié)論是怎么回事了吧?

我們稍微總結(jié)一下:

  • Python 能在一條語句中實(shí)現(xiàn)多重賦值,這是利用了序列解包的特性
  • Python 能在一條語句中實(shí)現(xiàn)變量交換,不需引入中間變量,在變量數(shù)少于 4 個(gè)時(shí)(3.8 版本起是少于 5 個(gè)),CPython 是利用了 ROT_* 指令來交換棧中的元素,當(dāng)變量數(shù)超出時(shí),則是利用了序列解包的特性。
  • 序列解包是 Python 的一大特性,但是在本文的例子中,CPython 解釋器在小小的操作中還提供了幾個(gè)優(yōu)化的指令,這絕對(duì)會(huì)超出大多數(shù)人的認(rèn)知

 

責(zé)任編輯:華軒 來源: Python貓
相關(guān)推薦

2022-05-25 08:01:37

WHERESQL 語句

2021-07-12 23:53:22

Python交換變量

2019-12-31 09:33:03

MongoDBB 樹NoSQL

2022-05-31 13:58:09

MySQL查詢語句

2019-09-24 09:33:53

MySQLB+樹InnoDB

2023-12-11 09:23:00

AI模型

2014-03-28 09:21:58

2021-07-22 23:27:45

Python工具算法

2021-08-08 14:22:09

Python開發(fā)技術(shù)

2021-06-11 17:49:29

變量代碼計(jì)算機(jī)

2025-03-11 13:07:58

2023-06-06 09:03:06

InnodbMySQL

2010-01-14 14:38:47

為什么使用三層交換機(jī)

2021-02-15 15:07:45

Windows 10Windows微軟

2024-07-02 12:27:47

2024-01-10 17:10:53

數(shù)據(jù)訓(xùn)練

2020-03-03 11:35:40

PythonMySQL數(shù)據(jù)

2025-03-11 00:22:00

DeepSeekAI圖片

2022-03-28 08:24:52

MySQL聚簇索引非聚簇索引

2024-05-22 09:01:53

InnoDBB+索引
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)