有關Python2.x版本下的dis模組的使用

Hwan發表於2019-02-16

有關Python2.x版本下的dis模組的使用

  事情源於昨天看書看到一個有意思的部分,就是在Python元組裡面的元素如果存在可變的物件,比如Python裡面的列表,類似下面這種形式:

t = (1, 2, [3, 4])

  那麼如果我執行t[2] += [5, 6]會發生什麼,首先給我的感覺是這個會報錯吧,這個是顯而易見的,但是5能否新增進去呢?哈哈,突然覺得有點意思了,然後答案就是:

t變成(1, 2, [3, 4, 5, 6])
因為tuple不支援對它的賦值,所以會丟擲TypeError異常

  具體的報錯資訊可以在互動式環境中自行測試一下
  這個時候我們可以使用Python的dis模組來反編譯一下,看看操作的位元組碼,如下所示:

In [14]: dis.dis(compile("t[2]+=[5, 6]", " ", "single"))
  1           0 LOAD_NAME                0 (t)
              3 LOAD_CONST               0 (2)
              6 DUP_TOPX                 2
              9 BINARY_SUBSCR
             10 LOAD_CONST               1 (5)
             13 LOAD_CONST               2 (6)
             16 BUILD_LIST               2
             19 INPLACE_ADD
             20 ROT_THREE
             21 STORE_SUBSCR
             22 LOAD_CONST               3 (None)
             25 RETURN_VALUE

  因為我使用的是Python2的版本,所以在使用dis.dis的時候,我們需要先將程式碼編譯一下,即使用compile方法,這裡需要注意的是第三個引數,第三個引數有三個選項,”single”、”eval”、”exec”,具體的可以help檢視一下,下面我們來說一下上面位元組碼的關鍵行的解釋:

BINARY_SUBSCR 表示將t[2]存入棧頂TOS
INPLACE_ADD 表示計算 TOS+[5, 6],這一步是可以完成的,因為TOS指向的是一個可變物件,也就是[3, 4]這個列表
STORE_SUBSCR 這一步失敗,這是因為t是一個元組,是不可變的,對其元素進行賦值是不被允許的

  所以我的建議是:

  • 不要把可變物件放入元組裡面
  • 增量賦值不是一個原子操作,像上面的那樣,雖然丟擲錯誤,但是還是完成了操作
  • 瞭解Python程式碼背後的執行機制很有幫助
  • 不建議這樣,雖然我們可以使用t[2].extend([5, 6])來完成操作,但是我們仍然不建議這樣做,不要將可變物件放入元組中。

相關文章