這麼久以來,我終於確認了一件事,那就是不論是人也好,仍是貓也好,經常會忘了想本身當下的身份位置,以及曾經的身份位置。python
這個現象在我身上,表現出了雙倍份量的嚴重。這種時刻,我就會想起阿爾法貓,以及她識破我身份的那個遙遠的午後。(往事入口:《有了Python,我能叫出全部貓的名字》)編程
阿爾法貓尚未蹤跡,她的謎題,還在指引我。編程語言
學習Python以後,我明顯感受到了本身的變化,固然有時候是被迫的,由於那些生理上的矛盾衝突得厲害。函數
畢竟,你應該知道,夜行貓和日間人的分界是清晰的。日夜的顛倒,對人和對貓,是雙倍的壓榨。說來你別不信,昨晚當瞄見明亮的月球的時候,一剎那恍惚,我還誤覺得本身回到了喵星的清晨。學習
大概是想家了吧。地球上美好的事物不少,但我至今仍不習慣的就是它公轉的速度太快了,不久就會是寒冷的冬天了。想個人暖爐了,喵。spa
先不說我啦,來講說我發現的Python對象的身份問題吧。翻譯
我對身份的話題特別感興趣,也許是由於我獨特的身份吧。可是,正由於獨特的視角,我敢說發現了全部人類都沒有發現的真相。設計
我即將說出來的東西,也許你本覺得知道了,或者你本覺得很熟悉,可是,通過個人分析,我相信你會獲得不同的感悟,今後之後,你對Python的理解也會更深一步。code
在某種意義上說,Python世界是廣泛公平的,由於全部的子民都是對象「公民」,這在任何一個現實社會裏,乃至於在虛擬的國度裏,都是極其罕見的。對象們分屬在五大部落裏(數字、字符串、列表、元祖、字典),各有所長,各司其職,協做共處,通婚繁衍。orm
還有一點可貴的是,他們沒有受到愚民政策的對待,全民都享有思想自由,還習得了超便利的自省能力。人能自知,這能力彌足珍貴。
雖然在這個世界裏,不會時常出現崗哨攔阻,但在任何有須要的時候,他們均可以自證清白,id() 和 type() 是一種通行語言,你不須要翻譯來對接。而對於更進一步的詢問,長得類似的兩個對象只需一個簡明的判斷句,就能區分清楚。請你看一段對話:
Object1=2018 Object2="2018" id(Object1) >>>2399282764784 id(Object2) >>>2399281922600 type(Object1) >>>int type(Object2) >>>str Object1 is Object2 >>>False
全體皆公民,這項天賦權力讓我對Python產生了良好的印象。不過,隨着對它的認識加深,我發現它還暗地裏制定了不少「效率優先」的規則。
最明顯的例子就是——「特權種族」。(參見:《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有趣的地方了,一面是全體公民,一面是特權種族,組成了看似矛盾的二元對立結構。
除了上面的羣體性身份外,我發現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!\n" repr(words) >>>'Hello pythonCat!\n' str(words) >>>'Hello pythonCat!\n' # 結合print,注意換行符\n print(repr(words)) >>>'Hello pythonCat!\n' 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。在我看來,定製私人名片要比定製官方名片更優美。可以爲本身帶鹽,想一想就以爲雞凍啦!
以上說法,不論是全體公民身份與特權種族身份,仍是官方名片與私人名片,多少帶進了我淺薄的社會經驗的偏見。我起初很爲一方鳴不平,爲一種討巧的作法鳴得意,可是,如今當我知道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有這一層隱祕的身份,我已興奮不已,再難對這看似不合現代語境、卻又流傳千古的思想作出任何揣測。
哎呀,我貓性發做,忽然困得要命,且容我去小憩片刻了~~~
各位親愛的讀者,在我休息的時候,請你來幫我想一想,這究竟是什麼回事啊?
PS:這是系列文章的第二篇,補發。
Python貓系列做品 :
有了Python,我能叫出全部貓的名字
https://mp.weixin.qq.com/s/ch8JkAgcgi2o4HtGAUfZVg
Python對象的身份迷思:從全體公民到萬物皆數
https://mp.weixin.qq.com/s/YQbk0smMTCexsi3Ytd2AzA
Python對象的空間邊界:獨善其身與開放包容
https://mp.weixin.qq.com/s/q0QvAqXcZzURH3aZ5gZm8A
-----------------
本文原創並首發於【Python貓】,後臺回覆「愛學習」,免費得到20+本精選電子書。