詳解python中的 is 操作符
大家可以與Java中的 == 操作符相互印證一下,加深一下對(duì)引用和對(duì)象的理解。原問(wèn)題: Python為什么直接運(yùn)行和在命令行運(yùn)行同樣語(yǔ)句但結(jié)果卻不同,他們的緩存機(jī)制不同嗎?
其實(shí),高票答案已經(jīng)說(shuō)得很詳細(xì)了。我只是再補(bǔ)充一點(diǎn)而已。
is 操作符是Python語(yǔ)言的一個(gè)內(nèi)建的操作符。它的作用在于比較兩個(gè)變量是否指向了同一個(gè)對(duì)象。
與 == 的區(qū)別
class A(): def __init__(self, v): self.value = v def __eq__(self, t): return self.value == t.value a = A(3) b = A(3) print a == b print a is b
這個(gè)結(jié)果是True,F(xiàn)alse。因?yàn)槲覀冎貙懥薩_eq__方法就使得a, b在比較的時(shí)候,只比較它們的value即可。只要它們的value相等,那么a, b就是相等的。
而 is 操作符是判斷兩個(gè)變量是否引用了同一個(gè)對(duì)象。
同一個(gè)對(duì)象?
is 的用法說(shuō)起來(lái)其實(shí)挺簡(jiǎn)單的,但是真正用起來(lái),它的難點(diǎn)恰恰就在于判斷哪些對(duì)象是同一個(gè)對(duì)象。
看下面的幾個(gè)測(cè)試,先不看結(jié)果,自己能答對(duì)多少?
a = 10 b = 10 print a is b a = 10.0 b = 10.0 print a is b a = 10 def f(): return 10 print f() is a a = 1000 def f(): return 1000 print f() is a a = 10.0 def f(): return 10.0 print f() is a
嗯。這個(gè)結(jié)果是True, True, True, False, False。你答對(duì)了嗎?
這個(gè)結(jié)果中牽扯到兩個(gè)問(wèn)題:第一,就是小整數(shù)的緩存,第二,就是pyc文件中CodeObject的組織問(wèn)題。
Python中把-127到128這些小整數(shù)都緩存了一份。這和Java的Integer類是一樣的。所以,對(duì)于-127到128之間的整數(shù),整個(gè)Python虛擬機(jī)中就只有一個(gè)實(shí)例。不管你什么時(shí)候,什么場(chǎng)景下去使用 is 進(jìn)行判斷,都會(huì)是True,所以我們知道了這兩個(gè)測(cè)試一定會(huì)是True:
a = 10 b = 10 print a is b a = 10 def f(): return 10 print f() is a
接著,我們重點(diǎn)看下,這兩個(gè)測(cè)試:
a = 10.0 b = 10.0 print a is b a = 10.0 def f(): return 10.0 print f() is a
為什么一個(gè)是True,一個(gè)是False。要探究這個(gè)問(wèn)題,就要從字節(jié)碼的角度去分析了。我們先把這個(gè)文件編譯一下:
python -m compileall testis.py
然后再使用這個(gè)工具查看一下字節(jié)碼文件:
https:// github.com/hinus/railgu n/blob/master/src/main/python/rgparser/show.py
得到這樣的輸出:
<argcount> 0 </argcount> <nlocals> 0</nlocals> <stacksize> 2</stacksize> <flags> 0040</flags> <code> 6400005a00006400005a01006500006501006b080047486400005a000064 01008400005a02006502008300006500006b0800474864020053 </code> <dis> 1 0 LOAD_CONST 0 (10.0) 3 STORE_NAME 0 (a) 2 6 LOAD_CONST 0 (10.0) 9 STORE_NAME 1 (b) 3 12 LOAD_NAME 0 (a) 15 LOAD_NAME 1 (b) 18 COMPARE_OP 8 (is) 21 PRINT_ITEM 22 PRINT_NEWLINE 5 23 LOAD_CONST 0 (10.0) 26 STORE_NAME 0 (a) 6 29 LOAD_CONST 1 (<code object f>) 32 MAKE_FUNCTION 0 35 STORE_NAME 2 (f) 8 38 LOAD_NAME 2 (f) 41 CALL_FUNCTION 0 44 LOAD_NAME 0 (a) 47 COMPARE_OP 8 (is) 50 PRINT_ITEM 51 PRINT_NEWLINE 52 LOAD_CONST 2 (None) 55 RETURN_VALUE </dis> <names> ('a', 'b', 'f')</names> <varnames> ()</varnames> <freevars> ()</freevars> <cellvars> ()</cellvars> <filename> 'testis.py'</filename> <name> '<module>'</name> <firstlineno> 1</firstlineno> <consts> 10.0 <code> <argcount> 0 </argcount> <nlocals> 0</nlocals> <stacksize> 1</stacksize> <flags> 0043</flags> <code> 64010053</code> <dis> 7 0 LOAD_CONST 1 (10.0) 3 RETURN_VALUE </dis> <names> ()</names> <varnames> ()</varnames> <freevars> ()</freevars> <cellvars> ()</cellvars> <filename> 'testis.py'</filename> <name> 'f'</name> <firstlineno> 6</firstlineno> <consts> None 10.0 </consts> <lnotab> 0001</lnotab> </code> None </consts> <lnotab> 060106010b0206010902</lnotab>
大家注意看,整個(gè)python文件其實(shí)就是一個(gè)大的<code>對(duì)象,f 所對(duì)應(yīng)的那個(gè)函數(shù)也是一個(gè)<code>對(duì)象,這個(gè)code對(duì)象做為整體是大的<code>對(duì)象的consts域里的一個(gè)const項(xiàng)。再注意,在大<code>對(duì)象里,有10.0這樣的一個(gè)const項(xiàng),f 這個(gè)<code>對(duì)象所對(duì)應(yīng)的conts里呢,也有一個(gè)10.0這個(gè)浮點(diǎn)數(shù)。
當(dāng)python在加載這個(gè)文件的時(shí)候,就會(huì)完成主<code>里的10.0這個(gè)浮點(diǎn)數(shù)的加載,生成一個(gè)PyFloatObject。也就是說(shuō)靜態(tài)的pyc文件的常量表在被加載以后,就變成了內(nèi)存中的常量表,文件的表里的10.0就變成了內(nèi)存中的一個(gè)PyFloatObject。所以,a, b兩個(gè)變量都會(huì)引用這個(gè)PyFloatObject。
但是 f 里的那個(gè)10.0呢?它是要等到MAKE_FUNCTION被調(diào)用的時(shí)候才會(huì)真正地初始化。做為 f 方法的返回值,它必然與我們之前所說(shuō)的主<code>里的10.0不是同一個(gè)對(duì)象了。
本質(zhì)上講,這是Python的一個(gè)設(shè)計(jì)缺陷(例如Java以一個(gè)文件為編譯單元,共享同一個(gè)常量池就會(huì)減輕這個(gè)問(wèn)題。但如果跨文件使用 == 操作符,也會(huì)出現(xiàn)同樣的問(wèn)題。仍然沒(méi)有解決這個(gè)問(wèn)題。實(shí)際上,我自己也不知道該怎么解決這個(gè)問(wèn)題。)我們應(yīng)該盡量避免 is 的這種用法。始終把 is 的用法限制在本文的第一個(gè)例子中。這樣相對(duì)會(huì)安全一些。
相關(guān)文章
使用OpenCV circle函數(shù)圖像上畫圓的示例代碼
這篇文章主要介紹了使用OpenCV circle函數(shù)圖像上畫圓的示例代碼,本文內(nèi)容簡(jiǎn)短,給大家突出重點(diǎn)內(nèi)容,需要的朋友可以參考下2019-12-12Python+pandas計(jì)算數(shù)據(jù)相關(guān)系數(shù)的實(shí)例
今天小編就為大家分享一篇Python+pandas計(jì)算數(shù)據(jù)相關(guān)系數(shù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07Python利用contextvars實(shí)現(xiàn)管理上下文變量
Python?在?3.7?的時(shí)候引入了一個(gè)模塊:contextvars,從名字上很容易看出它指的是上下文變量。所以本文就來(lái)和大家詳細(xì)講講如何使用contextvars實(shí)現(xiàn)管理上下文變量,需要的可以參考一下2022-07-071 行 Python 代碼快速實(shí)現(xiàn) FTP 服務(wù)器
FTP 服務(wù)器,在此之前我都是使用Linux的vsftpd軟件包來(lái)搭建FTP服務(wù)器的,現(xiàn)在發(fā)現(xiàn)了利用pyftpdlib可以更加簡(jiǎn)單的方法即可實(shí)現(xiàn)FTP服務(wù)器的功能。下面小編給大家?guī)?lái)了1 行 Python 代碼快速實(shí)現(xiàn) FTP 服務(wù)器,需要的朋友參考下2018-01-01Python數(shù)據(jù)分析matplotlib折線圖案例處理
這篇文章主要介紹了Python數(shù)據(jù)分析matplotlib折線圖案例處理,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08