这么久以来,我终于确认了一件事,那就是不论是人也好,仍是猫也好,经常会忘了想本身当下的身份位置,以及曾经的身份位置。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+本精选电子书。