文档章节

Python对象的空间边界:独善其身与开放包容

豌豆花下猫
 豌豆花下猫
发布于 2018/12/21 19:22
字数 2811
阅读 868
收藏 28

导读:Python猫是一只喵星来客,它爱地球的一切,特别爱优雅而无所不能的 Python。我是它的人类朋友豌豆花下猫,被授权润色与发表它的文章。如果你是第一次看到这个系列文章,那我强烈建议,请先看看它写的前两篇文章(链接见文末),相信你一定会爱上这只神秘的哲学+极客猫的。不多说啦,一起来享用今天的“思想盛宴”吧!

睡觉是我最爱做的事——因为可以懒懒地做美梦,不用吃东西,不用跟人吵架,不用关心世界大事。这是除了学 Python 与写作之外,最让我舒服的事了。所以,才刚醒来,我就又困了......

刚才看到了 Python 老爹 Guido 的邮件,他说要“go back to sleep mode”,不参与正在进行的 PEP 投票了。哼,这只懒惰的老头——等等我啊,等写完这篇东西,我也要 go back to sleep mode......

上回说道,我发现 Python 公民的身份竟然暗合毕达哥拉斯的哲学命题(万物皆数),真是百思不得其解。在梦里,我已经想出了答案。可是突然之间,游过来一条大蟒蛇,竟把答案吞掉了。我去找它理论,它就开始耍赖,吞自己的尾巴、屁股、肚子......最后把自己全吞下去了。唉,可怜我的答案就这么消失了。

今天,我继续跟大家聊聊 Python 中跟身份密切相关的一个话题吧,那就是对象的边界问题 。如你所知,我本来是一只猫,现在略具一些人性了,但在此转型期间却十分敏感,总能在细微之处浮想联翩,最后竟然也薄有所获,真是万幸了。希望我的分享,也能启发你收获哪怕一点点的感悟,那我就有万分的开心啦 :)

1、固定边界:自由与孤独

Python 中有一些公民向来我行我素,它们特立独行,与他人之边界划定得清清楚楚。客气的人称它们是定长对象,或者叫不可变对象,然而,懂得一些历史典故的人又叫它们是铁公鸡 。这个典故出自何处呢?亏得猫猫我曾恶补过一段历史知识,知道这指的正是激进的道家弟子杨朱。

损一毫利天下,不与也;悉天下奉一身,不取也;人人不损一毫,人人不利天下,天下治矣! ——春秋·杨朱

对于定长对象,你不能为它增加元素,不能为它减少元素,不能为它修改元素,甚至不能轻易地复制和删除它!(参见本公众号Python猫中关于字符串的系列文章,链接见文末)

这些对象自立于世,也自绝于世,你看它们长得是普普通通的,平平凡凡的,然而其灵魂却是自由自在的,其生命是富有尊严而不可侵犯的。若想与这些公民打交道,你就得依着它们的脾气,不可越雷池半步。

>>> t1 = ('Python', '猫')
>>> t2 = ('Python', '猫')
>>> t1 is t2  # 对象独立
False
>>> t1[1] = '蛇'  # 不可修改元素
TypeError  Traceback (most recent call last)
TypeError: 'tuple' object does not support item assignment

在上一篇文章里,我们见识了 Python 世界中的“特权种族”,而特权种族无一例外地都出身于定长对象。它们是一脉相承的,其存在的合理性也是相似的,那就是便于共用内存资源,提高内存使用效率。

上表就是定长对象的一份名单。可知,它们占据了多数。

定长对象的特性让我不由地想到一种人类,它们严守自己的边界,刻板而严谨,一心只在乎份内之事,默默承担下自己的责任,追求的是内在的自由。虽然也会时常与别人打交道,但是,它们不贪图扩大自己的利益,也不妄想要侵犯别人的领土。独立的个体养成了个人的品牌,它们的不变性成就了外人能有所依赖的确定性。

>>> key1 = 'Python 猫'
>>> key2 = ['someone else']

>>> dict1 = {key1 : '好人'}
 {'Python 猫': '好人'}

>>> dict2 = {key2 : '好人'}
TypeError  Traceback (most recent call last)
TypeError: unhashable type: 'list'

Python 为了维护定长对象的独立性/确定性,在编译机制上做了不少优化,例如 Intern 机制与常量合并机制。其中的好处,我已经多次提及了。

坏处也有,那就是孤独。它们的孤独不在于没有同类,而在于不能(不容易)复制自身。以字符串对象为例,你可以尝试多种多样的手段,然而到头来,却发现唯一通用的方法竟然要先把字符串“碎尸万段”,接着重新组装才行!

s0 = "Python猫"

# 以下7种方法,无法复制s0字符串,id(x)==id(s0)
s1 = s0
s2 = str(s0)
s3 = s0[:]
s4 = s0 + ''
s5 = '%s' % s0
s6 = s0 * 1
import copy
s7 = copy.copy(s0)

# 以下方法可以复制字符串,“打碎”再重组
s8 = "".join(s0)

哲学上有一个著名的脑洞题:假如把一个人粉碎成原子再组合,这个人还是原来的人么?这道题能令古往今来的哲学家打起架来,若是放到现今正火爆的电视节目《奇葩说》上,也能令辩手们“一本正经地胡说八道”个不休。

在 Python 的世界里,不存在这种烦恼,因为判定两个对象是否相同的标准是确定的,也即是看它们的 id 是否相等。因此,借助 Python 来回答这道题,答案会是:如果用 join() 方法把字符串粉碎成字符再组合,新的字符串不再是原来的字符串了。

过程很“残忍”,但总归能稍稍释缓自由个体的孤独感了吧。

2、弹性边界:开放与节制

与定长对象不同,变长对象/可变对象信奉的是另一套哲学。

它们思想开放,采取的是兼容并包的处事观,会因地制宜式伸缩边界。 以列表对象为例,它乐意接纳所有其它的对象,肯花费精力去动态规划,也不惧于拔掉身上所有的“毛”。

>>> l = ['Python', '猫']
>>> l.append('其它猫') # ['Python','猫','其它猫']
>>> l.pop(1)   # ['Python','其它猫']
>>> l.clear()  # []

这些大胆的行为,在定长对象那里,都是不可想象的。在变长对象身上,你似乎能感受到一种海纳百川的风范,相比之下,定长对象的铁公鸡形象则立马显得格局忒小了。

变长对象并非没有边界,相反,它们更在乎自身的边界,不惜花费大量的资源来维持动态的稳定。一旦边界确定下来,它们绝不会允许越界行为。跟某些编程语言动不动就数组越界不同,Python 不存在切片越界,因为切片操作始终被控制为边界范围之内,索引超出的部分会自动被舍弃。

>>> q=[1, 2, 3, 4, 5]

# 不允许索引越界
>>> q[10]
IndexError    Traceback (most recent call last)
IndexError: list index out of range

# 允许切片越界
>>> q[2:10]   # [3, 4, 5]
>>> q[-10:2]  # [1, 2]

变长对象在本质上是一种可伸缩的容器,其主要好处就是支持不断添加或者取出元素。对应到计算机硬件层面,就是不断申请或者释放内存空间。这类操作是代价昂贵的操作,为了减少开销,Python 聪明地设计了一套分配超额空间的机制

以列表为例,在内存足够的前提下,最初创建列表时不分配超额空间,第一次 append() 扩充列表时,Python 会根据下列公式分配超额空间,即分配大于列表实际元素个数的内存空间,此后,每次扩充操作先看是否有超额空间,有则直接使用,没有则重新计算,再次分配一个超额空间。公式如下:

new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6)

其中,new_allocated 指的是超额分配的内存大小,newsize 是扩充元素后的实际长度。举例来说,一个长度为 4 的列表,append() 增加一个元素,此时实际长度为 5(即 newsize 为5),但是,Python 不会只给它分配 5 个内存空间,而是计算后给它超额分配 new_allocated == 3 个内存大小,所以最终加起来,该列表的元素实际占用的内存空间就是 8 。

如此一来,当列表再次扩充时,只要最终长度不大于 8 ,就不需要再申请新的内存空间。当扩充后长度等于 9 时,new_allocated 等于 7 ,即额外获得 7 个内存大小,以此类推。

以列表长度为横轴,以超额分配的内存大小为纵轴,我们就得到了如下美妙的图表:

超额分配的空间就是定长对象的软边界 ,这意味着它们在扩张时是有法度的,意味着它们在发展时是有大胆计划与适度节制的。如此看来,与定长对象的“固步自封”相比,变长对象就显得既开明又理智了。

3、结语

回头看前面提到的定长对象,我佩服它们独善其身的个性,虽然铁公鸡形象略显小气,但对人却无害,反而你能感受到其浓浓的 “富贵不能淫,贫贱不能移,威武不能屈” 的大丈夫气度。

再看变长对象,它们“本来无一物”,却能包容万物,对他人信任,对外部开放,更难得的是,它们张弛有度,孕生出的是无限的可能性。

这两种对象极大地满足了我对于 Python 世界的好奇心,也成为了我理解自己和人类世界的一种参照系。妙哉!妙哉!若你问,我更钦佩哪一类?喵呜,肚子有点饿啦,且容我去觅得一二小鱼干,喂饱肚子再说吧......

Python猫往期作品

有了Python,我能叫出所有猫的名字

Python对象的身份迷思:从全体公民到万物皆数

字符串系列文章

你真的知道Python的字符串是什么吗?

你真的知道Python的字符串怎么用吗?

Python是否支持复制字符串呢?

join()方法的神奇用处与Intern机制的软肋

-----------------

本文原创并首发于微信公号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。

© 著作权归作者所有

共有 人打赏支持
豌豆花下猫
粉丝 5
博文 11
码字总数 32468
作品 0
苏州
私信 提问
加载中

评论(8)

豌豆花下猫
豌豆花下猫

引用来自“久永”的评论

引用来自“豌豆花下猫”的评论

引用来自“久永”的评论

上回说道,。。。
----
在上一篇文章里,。。。
----引用的分割线----
俗话说“有头有尾”,是否有尾是未来之数,不可而知。但是你好歹有个头啊?这篇貌似你此栏第一篇文章,
哪些所谓“上回”“上一篇文章里”的头呢?

我是在微信公众号发的,前面没注册这个啊,注册后懒得发。文末附了链接,可以看

引用来自“久永”的评论

感谢贡献。
为什么以前在 信微公众号上发?
因为可以收钱?

引用来自“豌豆花下猫”的评论

当然一开始是有这个想法,但这很不现实,因为我筛选条件高,至今也没接过商业广告。公众号是我的主阵地,毫无疑问,它目前仍是最好的写作平台。
现在不是有付费阅读吗?现在开源中国都屏蔽“信微公众号”关键字了,你的留言是怎么发出来的?

没建立影响力的话,付费阅读就是摆设。留言不会屏蔽吧,发文时才会。
久永
久永

引用来自“豌豆花下猫”的评论

引用来自“久永”的评论

上回说道,。。。
----
在上一篇文章里,。。。
----引用的分割线----
俗话说“有头有尾”,是否有尾是未来之数,不可而知。但是你好歹有个头啊?这篇貌似你此栏第一篇文章,
哪些所谓“上回”“上一篇文章里”的头呢?

我是在微信公众号发的,前面没注册这个啊,注册后懒得发。文末附了链接,可以看

引用来自“久永”的评论

感谢贡献。
为什么以前在 信微公众号上发?
因为可以收钱?

引用来自“豌豆花下猫”的评论

当然一开始是有这个想法,但这很不现实,因为我筛选条件高,至今也没接过商业广告。公众号是我的主阵地,毫无疑问,它目前仍是最好的写作平台。
现在不是有付费阅读吗?现在开源中国都屏蔽“信微公众号”关键字了,你的留言是怎么发出来的?
豌豆花下猫
豌豆花下猫

引用来自“豌豆花下猫”的评论

引用来自“久永”的评论

上回说道,。。。
----
在上一篇文章里,。。。
----引用的分割线----
俗话说“有头有尾”,是否有尾是未来之数,不可而知。但是你好歹有个头啊?这篇貌似你此栏第一篇文章,
哪些所谓“上回”“上一篇文章里”的头呢?

我是在微信公众号发的,前面没注册这个啊,注册后懒得发。文末附了链接,可以看

引用来自“久永”的评论

感谢贡献。
为什么以前在 信微公众号上发?
因为可以收钱?
当然一开始是有这个想法,但这很不现实,因为我筛选条件高,至今也没接过商业广告。公众号是我的主阵地,毫无疑问,它目前仍是最好的写作平台。
久永
久永

引用来自“豌豆花下猫”的评论

引用来自“久永”的评论

上回说道,。。。
----
在上一篇文章里,。。。
----引用的分割线----
俗话说“有头有尾”,是否有尾是未来之数,不可而知。但是你好歹有个头啊?这篇貌似你此栏第一篇文章,
哪些所谓“上回”“上一篇文章里”的头呢?

我是在微信公众号发的,前面没注册这个啊,注册后懒得发。文末附了链接,可以看
感谢贡献。
为什么以前在 信微公众号上发?
因为可以收钱?
豌豆花下猫
豌豆花下猫

引用来自“久永”的评论

上回说道,。。。
----
在上一篇文章里,。。。
----引用的分割线----
俗话说“有头有尾”,是否有尾是未来之数,不可而知。但是你好歹有个头啊?这篇貌似你此栏第一篇文章,
哪些所谓“上回”“上一篇文章里”的头呢?

我是在微信公众号发的,前面没注册这个啊,注册后懒得发。文末附了链接,可以看
久永
久永
上回说道,。。。
----
在上一篇文章里,。。。
----引用的分割线----
俗话说“有头有尾”,是否有尾是未来之数,不可而知。但是你好歹有个头啊?这篇貌似你此栏第一篇文章,
哪些所谓“上回”“上一篇文章里”的头呢?
豌豆花下猫
豌豆花下猫

引用来自“luwenhua”的评论

阅后受益啊,谢谢作者有想法的文章。

很开心终于有人受益。谢谢留言。
luwenhua
luwenhua
阅后受益啊,谢谢作者有想法的文章。
005-Python 变量类型

Python 变量类型 变量存储在内存中的值。这就意味着在创建变量时会在内存中开辟一个空间。基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中。因此,变量可以指定...

运维小当家
2018/06/29
0
0
Python文学家为Python写的一首词?(附中英文版)

The Zen of Python, by Tim Peters (Python之禅 by Tim Peters) Beautiful is better than ugly. (优美胜于丑陋(Python 以编写优美的代码为目标)) Explicit is better than implicit. ......

柯西带你学编程
2018/06/01
0
0
python基础---面向对象高级

面向对象高级 isinstance(obj,cls) 检查obj是否是类 cls 的对象 class Foo(object): pass obj = Foo() print(isinstance(obj, Foo)) 输出:True issubclass(sub,super) 检查sub类是否是 supe......

迟到的栋子
2018/07/01
0
0
python为什么不需要swap(a,b)

转自:http://blog.csdn.net/ccat/archive/2003/04/30/8349.aspx 致 谢:这是一篇译稿,原作者是一位NASA的程序员,非常感谢他在Python社区及时、详细的为我解答了问题,并在讲解中解说了一些...

flynewton
2010/11/01
0
0
Python基础系列讲解—动态类型语言的特点

前言 在C语言中变量所分配到的地址是内存空间中一个固定的位置,当我们改变变量值时, 对应内存空间中的值也相应改变。在Python中变量存储的机制是完全不一样的,当给一个变量赋值时首先解释...

元宵大师
2018/08/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

大数据教程(11.9)hive操作基础知识

上一篇博客分享了hive的简介和初体验,本节博主将继续分享一些hive的操作的基础知识。 DDL操作 (1)创建表 #建表语法CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name [(col_name ...

em_aaron
今天
0
0
OSChina 周四乱弹 —— 我家猫真会后空翻

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @我没有抓狂 :#今天听这个# 我艇牛逼,百听不厌,太好听辣 分享 Led Zeppelin 的歌曲《Stairway To Heaven》 《Stairway To Heaven》- Led Z...

小小编辑
今天
1
0
node调用dll

先安装python2.7 安装node-gyp cnpm install node-gyp -g 新建一个Electron-vue项目(案例用Electron-vue) vue init simulatedgreg/electron-vue my-project 安装electron-rebuild cnpm ins......

Chason-洪
今天
3
0
scala学习(一)

学习Spark之前需要学习Scala。 参考学习的书籍:快学Scala

柠檬果过
今天
3
0
通俗易懂解释网络工程中的技术,如STP,HSRP等

导读 在面试时,比如被问到HSRP的主备切换时间时多久,STP几个状态的停留时间,自己知道有这些东西,但在工作中不会经常用到,就老是记不住,觉得可能还是自己基础不够牢固,知识掌握不够全面...

问题终结者
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部