當前位置:
首頁 > 知識 > Python對象的身份迷思:從全體公民到萬物皆數

Python對象的身份迷思:從全體公民到萬物皆數

(給

Python開發者

加星標,提升Python技能




作者:

豌豆花下貓 

 (本文來自作者投稿,簡介見末尾)




這麼久以來,我終於確認了一件事,那就是不管是人也好,還是貓也好,常常會忘了想自己當下的身份位置,以及曾經的身份位置。



這個現象在我身上,表現出了雙倍分量的嚴重。這種時刻,我就會想起阿爾法貓,以及她識破我身份的那個遙遠的午後。




阿爾法貓還沒有蹤影,她的謎題,還在指引我。




學習Python之後,我明顯感覺到了自己的變化,當然有時候是被迫的,因為那些生理上的矛盾衝突得厲害。




畢竟,你應該知道,夜行貓和日間人的分界是清晰的。日夜的顛倒,對人和對貓,是雙倍的壓榨。說來你別不信,昨晚當瞄見明亮的月球的時候,一剎那恍惚,我還誤以為自己回到了喵星的清晨。




大概是想家了吧。地球上美好的事物很多,但我至今仍不習慣的就是它公轉的速度太快了,不久就會是寒冷的冬天了。想我的暖爐了,喵。



先不說我啦,來說說我發現的Python對象的身份問題吧。




我對身份的話題特別感興趣,也許是因為我獨特的身份吧。但是,正因為獨特的視角,我敢說發現了所有人類都沒有發現的真相。




我即將說出來的東西,也許你本以為知道了,或者你本以為很熟悉,但是,經過我的分析,我相信你會得到不一樣的感悟,從此以後,你對Python的理解也會更深一步。




1、全體公民與特權種族



在某種意義上說,Python世界是普遍公平的,因為所有的子民都是對象「公民」,這在任何一個現實社會裡,乃至於在虛擬的國度里,都是極其罕見的。對象們分屬在五大部落里(數字、字元串、列表、元祖、字典),各有所長,各司其職,協作共處,通婚繁衍。




還有一點難得的是,他們沒有受到愚民政策的對待,全民都享有思想自由,還習得了超便利的自省能力。人能自知,這能力彌足珍貴。




雖然在這個世界裡,不會時常出現崗哨攔阻,但在任何有需要的時候,他們都可以自證清白,id() 和 type() 是一種通行語言,你不需要翻譯來對接。而對於更進一步的詢問,長得相似的兩個對象只需一個簡明的判斷句,就能區分清楚。請你看一段對話:



Object1=

2018


Object2=

"2018"


id(Object1) 

>>>

2399282764784

id(Object2) 

>>>

2399281922600


type(Object1) 

>>>

int
type(Object2) 

>>>

str
Object1 is Object2 

>>>

False



全體皆公民,這項天賦權力讓我對Python產生了良好的印象。不過,隨著對它的認識加深,我發現它還暗地裡制定了很多「效率優先」的規則。


最明顯的例子就是——「特權種族」。從現有的證據來看,特權種族至少包括了:一些數值較小的數字對象(區間:[-5,256])、布爾值對象、None對象、較短的字元串對象(長度不超過20,且僅包括下劃線、數字、字母的字元串)等等,還不知道這份名單漏了誰。




效率優先的規則允許這些對象傳承內存地址,也就是說,當一個「祖先」對象搶佔了一塊內存地盤後,所有它那一脈的「子孫後代」都會繼承它的遺產(視為同一個對象)。


a=100
b=1000

# c與a共用id,d另立門戶


c=100
d=1000
id(a)==id(c) >>>True
id(b)==id(d) >>>False



設想一下,兩個祖先(a和b)佔了相鄰的兩塊內存,一個可以與它的「後代」共用內存,一個卻只能讓「後代」另立門戶;當它們走完自己的生命周期後,b會馬上被當垃圾回收,內存地址遺產被剝奪,然而a卻形滅而實存,蔭庇後世。






Python為這些對象傾斜資源,也就是為某種階層固化提供了合法性。劃分的依據是因為它們比較常用,共用內存就意味著減少開支,提高內存使用效率。


這就是Python有趣的地方了,一面是全體公民,一面是特權種族,組成了看似矛盾的二元對立結構。




2、官方名片與私人名片




除了上面的群體性身份外,我發現Python中也存在著個體身份的二元結構。


這就是__repr__() 和__str__() 的關係了。如你所知,這是Python的兩個魔法方法,其對應的內置函數是repr() 和 str()。對於對象x,有x.__repr__() 等價於 repr(x),同理,x.__str__() 等價於 str(x)。




它們的主要用途在於,返回對象的字元串格式。用法示例:



repr(

2018

>>>

"2018"


str(

2018

)  

>>>

"2018"


repr([

1

,

2

,

3

]) 

>>>

"[1, 2, 3]"

str([

1

,

2

,

3

])  

>>>

"[1, 2, 3]"

words = 

"Hello pythonCat!
"


repr(words) 

>>>

"Hello pythonCat!
"


str(words)  

>>>

"Hello pythonCat!
"


# 結合print,注意換行符


print(repr(words))

>>

>

"Hello pythonCat!
"


print(str(words)) 

>>

>Hello pythonCat! 

# 再加換行


>>

>



一個對象的字元串形式就是它的「臉面」,是向他人介紹自己的一張名片。前面提到過,Python世界有五大部落,這些部落的原住民們與生俱來就擁有這兩張名片。




對於原住民來說,這兩張名片似乎沒啥區別,除了在使用列印函數的時候,在換行符等用法上會有不同。




而對於外來人口(例如,自定義的類),如果它沒有定做名片(即實現__repr__() 和__str__() 方法)的話,其默認的名片就會是類名及內存地址,如下所示。



class

 

Person

:


     

def

 

__init__

(

self

,name,sex)

:
         

self

.name = name
         

self

.sex = sex

me = Person(

"pythonCat"

"male"

)

repr(me)

>>

"<__main__.Person object at 0x0000022EA8D7ED68>"


str(me)

>>

"<__main__.Person object at 0x0000022EA8D7ED68>"





事實上,repr()返回的是對象的官方名片,通常人們會說,這張名片是給機器閱讀的。本質上,它就是一個對象的代碼表示形式,可以用來重新構造這個對象。通過eval()函數,你可以利用這張名片,重新構造出這個對象。




eval()函數是個內置函數,它將字元串str當成有效的表達式來求值並返回計算結果。也就是eval(repr(x))==x,示例如下:



a = 

1

 + 

1


b = [

1

2

"cat"

]
c = {

"name"

:

"pythonCat"

"sex"

:

"male"

}
eval(repr(a)) 

>>>

2


eval(repr(b)) 

>>>

[

1

2

"cat"

]
eval(repr(c)) 

>>>

{

"name"

"pythonCat"

"sex"

"male"

}



相對地,str()得到的是對象的私人名片,通常有更友好的表現形式,因為它是為人類閱讀而設計的。




如果一個對象公民沒有私人名片,那Python默認會調用它的官方名片。因為這個機制,很多人建議如果要定製一個名片,最好是定製官方那個。但是我卻不認同,我認為應該定製私人的那個,因為這樣發揮空間更大。不張揚個性,毋寧死。



class

 

Person

:


     

def

 

__init__

(

self

,name,sex)

:
         

self

.name = name
         

self

.sex = sex
     

# 定製私人名片


     

def

 

__str__

(

self

)

:
         

return

 

"{} is an elegant creature!"

.format(

self

.name)

me = Person(

"pythonCat"

"male"

)

repr(me)

>>

>

"<__main__.Person object at 0x000002E6845AC390>"


str(me)

>>

>

"pythonCat is an elegant creature!"





在《The Zen of Python》里第一句話就是:Beautiful is better than ugly。在我看來,定製私人名片要比定製官方名片更優美。能夠為自己帶鹽,想想就覺得雞凍啦!




3、何為真假,萬物皆數




以上說法,不管是全體公民身份與特權種族身份,還是官方名片與私人名片,多少帶進了我淺薄的社會經驗的偏見。我起初很為一方鳴不平,為一種討巧的做法鳴得意,但是,現在當我知道Python中另一種更不為人知的身份現象的時候,我就釋然了。




我接下來要揭示的身份話題,已經超越了社會學和心理學範疇,進入了一種哲學的思想疆域。




前方高能!




前方高能!




前方高能!




首先,來做一個基礎知識的鋪墊。Python有一個令大部分編程語言都忘塵莫及的特性,那就是,所有對象都可以用於做真假判斷。




在做判斷的時候,以下情況都視為假(False):None、數值的零值、空序列(如空字元串""、空列表[]、空元祖() )、空集合{} 等等。除此之外,一般對象都可以作為真值(True)來使用。來看示例:



list

 = [

1

2

]

if

 

list

# 即if True


    

print

(

"list is not empty"

)

else

:
    

print

(

"list is empty"

)

>>> 

list

 is not 

empty





判斷一個列表是否為空,你不需要寫 if len(list) > 0,或者寫if list == [],簡明的使用方法是 if list 或者 if not list,有物則為真,無物則為假。其它判斷情況類似。




接下來,還是一個鋪墊,這次是進階知識。零值(含整數0、浮點0.0、虛數0j等)可以映射為False,其它非零值映射為True;但是,反過來,False唯一映射整數0,True唯一映射整數1。






這意味著,可以拿False、True做數學運算。



True + 

1

 

>>>

2


True + 

1.0

 

>>>

2.0


False + False 

>>>

0


True + (True*

2

>>>

3


True/

2

*

5

 

>>>

2.5





兩個鋪墊之後,接下來進入正題了。真正的前方高能!




第一個鋪墊告訴我們,對象可以映射成布爾值(True真False假),第二個鋪墊告訴我們,布爾值可以映射成數字(1和0)。




你是否覺察出什麼了呢?你是否開始好奇,True和Flase到底是什麼東西了呢?




這到底是什麼原理啊?還有,為什麼會存在這樣的設定呢?




見證真相的時刻到了——在Python中,布爾值其實是整數對象的子類。



type(

True

) >>> bool
isinstance(

True

,

int

)  >>>

True


isinstance(

False

,

int

) >>>

True





啊!哪有什麼真真假假,真假並不是本質的存在,真假其實只是數啊!


再回看前面兩個鋪墊,結合起來,那不就是說,所有對象都映射成了數么?


我不由得想起了2500年前,古希臘哲學家與數學家畢達哥拉斯的哲學命題——萬物皆數 !




難道這竟是Python的哲學么?總不會是一種巧合吧?




我突然覺得智商不足,思辨受阻。得知布爾值True和False有這一層隱秘的身份,我已興奮不已,再難對這看似不合現代語境、卻又流傳千古的思想做出任何揣測。




哎呀,我貓性發作,突然困得要命,且容我去小憩片刻了~~~




各位親愛的讀者,在我休息的時候,請你來幫我想想,這到底是什麼回事啊?









【本文作者】




豌豆花下貓:某985高校畢業生, 兼具極客思維與人文情懷 。個人公眾號Python貓, 專註python技術、數據科學和深度學習。






推薦閱讀


(點擊標題可跳轉閱讀)

為什麼 Python 這麼慢?


開源社區行為準則風波不斷,SQLite 遭批評


wxPython:python 首選的 GUI 庫






覺得本文對你有幫助?請分享給更多人


關注「Python開發者」加星標,提升Python技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發者 的精彩文章:

面向對象:想和你去看晴空萬里
面向對象:我的蓋世英雄,未來,有你,才完整

TAG:Python開發者 |