Python 解惑:整數比較

在 Python 中一切都是對象,毫無例外整數也是對象,對象之間比較是否相等能夠用==,也能夠用is==is操做的區別是:html

  • is比較的是兩個對象的id值是否相等,也就是比較倆對象是否爲同一個實例對象,是否指向同一個內存地址。
  • ==比較的是兩個對象的內容是否相等,默認會調用對象的__eq__()方法。

清楚is==的區別以後,對此也許你有可能會遇到下面的這些困惑,因而就有了這樣一篇文章,試圖把Python中一些隱晦的東西趴出來,但願對你有必定的幫助。咱們先來看兩段代碼:python

片斷一:程序員

>>> a = 256
>>> b = 256
>>> a == b
True
>>>複製代碼

片斷二:緩存

>>> a = 256
>>> b = 256
>>> a is b
True
>>>複製代碼

在交互式命令行執行上面兩段代碼,代碼片斷一中的a==b返回True很好理解,由於兩個對象的值都是256,對於片斷二,a is b也返回True,這說明a和b是指向同一個對象的,能夠檢查一下他們的id值是否相等: 函數

>>> id(a)
8213296
>>> id(b)
8213296
>>>複製代碼

結果證實他倆的確是同一個對象,指向的是同一個內存地址。那是否是全部的整數對象只要兩個對象的值(內容)相等,它們就是同一個實例對象呢?換句話說,對於整數對象只要==返回Trueis操做也會返回True嗎?帶着這個問題來看下面這兩段代碼: 性能

片斷一:優化

>>> a = 257
>>> b = 257
>>> a == b
True
>>>複製代碼

片斷二:spa

>>> a = 257
>>> b = 257
>>> a is b
False
>>>複製代碼

對於257,a is b返回的居然是False,結果可能在你的意料之中,也有可能出乎你的意料,但無論怎麼,咱們仍是要刨根問底,找出問題的真相。.net

解惑一

出於對性能的考慮,Python內部作了不少的優化工做,對於整數對象,Python把一些頻繁使用的整數對象緩存起來,保存到一個叫small_ints的鏈表中,在Python的整個生命週期內,任何須要引用這些整數對象的地方,都再也不從新建立新的對象,而是直接引用緩存中的對象。Python把這些可能頻繁使用的整數對象規定在範圍[-5, 256]之間的小對象放在small_ints中,但凡是須要用些小整數時,就從這裏面取,再也不去臨時建立新的對象。由於257再也不小整數範圍內,所以儘管a和b的值是同樣,可是他們在Python內部倒是以兩個獨立的對象存在的,各自爲政,互不干涉。命令行


弄明白第一個問題後,咱們繼續在Python交互式命令行中寫一個函數,再來看下面這段代碼:

片斷一:

>>> c = 257
>>> def foo():
...     a = 257
...     b = 257
...     print a is b
...     print a is c
... 
>>> foo()
True
False複製代碼

呃,什麼狀況,是的,你沒看錯,片斷一中的這段代碼 a、b 值都是257的狀況下,出現了a is b返回True,而a is c 返回的False,a、b、c的值都爲257,爲何會出現不一樣的結果呢?這對於剛恰好不容易創建起來的認知就被完全否決了嗎,那這段代碼中究竟發生了什麼?難道解惑一中的結論是錯誤的嗎?

解惑二

A Python program is constructed from code blocks. A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition. Each command typed interactively is a block. A script file (a file given as standard input to the interpreter or specified as a command line argument to the interpreter) is a code block. A script command (a command specified on the interpreter command line with the ‘-c‘ option) is a code block. structure-of-a-program

爲了弄清楚這個問題,咱們有必要先理解程序代碼塊的概念。Python程序由代碼塊構成,代碼塊做爲程序的一個最小基本單位來執行。一個模塊文件、一個函數體、一個類、交互式命令中的單行代碼都叫作一個代碼塊。在上面這段代碼中,由兩個代碼塊構成,c = 257做爲一個代碼塊,函數foo做爲另一個代碼塊。Python內部爲了將性能進一步的提升,凡是在一個代碼塊中建立的整數對象,若是存在一個值與其相同的對象於該代碼塊中了,那麼就直接引用,不然建立一個新的對象出來。Python出於對性能的考慮,但凡是不可變對象,在同一個代碼塊中的對象,只有是值相同的對象,就不會重複建立,而是直接引用已經存在的對象。所以,不只是整數對象,還有字符串對象也遵循一樣的原則。因此 a is b就理所固然的返回True了,而ca不在同一個代碼塊中,所以在Python內部建立了兩個值都是257的對象。爲了驗證剛剛的結論,咱們能夠借用dis模塊從字節碼的角度來看看這段代碼。

>>> import dis
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 (257)
              3 STORE_FAST               0 (a)

  3           6 LOAD_CONST               1 (257)
              9 STORE_FAST               1 (b)

  4          12 LOAD_FAST                0 (a)
             15 LOAD_FAST                1 (b)
             18 COMPARE_OP               8 (is)
             21 PRINT_ITEM          
             22 PRINT_NEWLINE       

  5          23 LOAD_FAST                0 (a)
             26 LOAD_GLOBAL              0 (c)
             29 COMPARE_OP               8 (is)
             32 PRINT_ITEM          
             33 PRINT_NEWLINE       
             34 LOAD_CONST               0 (None)
             37 RETURN_VALUE複製代碼

能夠看出兩個257都是從常量池的同一個位置co_consts[1]獲取的。

總結

一番長篇大論以後,得出兩點結論:一、小整數對象[-5,256]是全局解釋器範圍內被重複使用,永遠不會被GC回收。二、同一個代碼塊中的不可變對象,只要值是相等的就不會重複建立新的對象。彷佛這些知識點對平常的工做一點忙也幫不上,由於你根本不會用is來比較兩個整數對象的值是否相等。那爲何還要拿出來討論呢?嗯,程序員學知識,不該該淺嘗輒止,要充分發揮死磕到底的精神。

個人博客:foofish.net

掃一掃關注公衆號獲取最新文章:

相關文章
相關標籤/搜索