What the fuck Python

By liuzhijun, 2017-11-18, 分类: Python技术

python

每种编程语言都有一些不为人知的陷阱,有些实际工作中会踩到,有些可能根本排不上用场,但弄明白这些陷阱有利于我们更好的去了解这门语言的实现机制。

下面这个题,你是否能一眼看出问题的本质。

第一题

# 第一组
>>> a = 256
>>> b = 256
>>> a is b
True
# 第二组
>>> a = 257
>>> b = 257
>>> a is b
False
# 第三组
>>> a = 257; b = 257
>>> a is b
True

不管是 Python2 还是 Python3 环境下,只要你是在 CPython 的交互式命令行 REPL 中执行,结果没什么不同。

我们知道 is 比较的是两个对象的内存地址是否一样( id 函数返回一个和对象的内存地址相关的值),言外之意就是看a,b两个变量是否指向同一个对象。我们来看看每个变量的 id 值。

>>> a = 256
>>> id(a)
1721788128
>>> b = 256
>>> id(a)
1721788128

>>> a = 257
>>> id(a)
14947024
>>> b = 257
>>> id(b)
14947104

>>> a = 257; b=257
>>> id(a)
14947136
>>> id(b)
14947136
>>>

不出所料,前后两组 a,b的 id 值是相同的,只有中间这组 id 值不一样,我们可以对其简单分析一下原因。在 Python 中,一切皆为对象,理论上任意两个对象的 id 值都是不一样的,例如:

>>> nums = [1,2,3,4]
>>> id(nums)
15148936

>>> nums2 = [1,2,3]
>>> id(nums2)
15160824

>>> nums3 = [1,2,3]
>>> id(nums3)
15160864

看得出每个对象的 id 值是不同的,哪怕两个对象的值(内容)相同,他们的 id 值也是不一样的(nums2和nums3)。那为什么前面第一组两个对象的id值相同呢?可能有些同学已经知道了

因为在 Python 中,我们需要使用对象的时候 Python 就会为我们创建好,当不需要了它就会进行回收,就好比屋子里面的东西用完之后,要及时清理,否则整个屋子很快就会堆满,最终导致房间再也塞不进任何东西。

同样的,为了提高性能,Python 就把一些常用的整数专门缓存起来,就像屋子里面有些东西总是每天都要频繁使用,比如床,你不能说睡完之后,就把床搬出去,要用了再搬回来,这样的效率太低,因为这个搬运过程实在是太耗时了。于是,我们可以专门拿一块空间用来放置这个床。

Python 中也是同样的道理,因为整数是我们经常使用的对象,为了避免重复的创建、回收,干脆就把那些常用的整数缓存起来,每次需要使用时直接从缓存中拿,而不是重新创建(重新创建的话,肯定是一个全新的对象)。这些整数的范围是[-5, 256],当然这个数字范围是Python之父决定的,你要改,必须重新编译Python环境。

现在我们就能解释第一组为什么是True,第二组为什么是False了。为什么第三组结果又是 True 了?,不是说好大于256的整数不再缓存,每次使用都是新对象吗?别急,再听我啰嗦一下。

还是出于性能考虑,Python内部做了进一步优化,怎么优化呢?但凡是在同一个代码块中的代码,如果出现两个值相同的整数,那么它们将被重用,来看下面这个代码:

# test.py
# -*- coding: utf-8 -*-
a = 257
b = 257

def func():
    c = 257
    print(a is c)  # False

print(a is b)  # True

func()

上面代码是在一个 test.py 文件中,运行时,a和b的id值相同,而c的id值与a不一样,因为a、b 在同一个代码块,属于模块级别,而 c 是在函数里面,属于局部变量,他们不属于同一代码块中,因此函数里面的 257 这个对象时会重新创建,而创建 b 的时候,发现同级代码块中有个257的值了,就重用了这个对象。

再回到前面讲的第三组值,在 Python 的交互式命令行 REPL 中,每单独一行都视为一个代码块,同一行中的代码属于同一个代码块,因此不难理解,第三组中的a和b处在同一个代码块中,所以后者重用了前者,因此,两个变量的id是相同的。

有没有觉得这是一个坑。虽然我们实际场景中并不一定能用上,但是至少我们知道了Python为我们做的一些优化工作。


关注公众号「Python之禅」(id:vttalk)获取最新文章 python之禅

猜你喜欢

2017-10-24
自学Python编程怎么学才不那么孤独
2015-12-08
Python 整数对象实现原理
2015-06-17
如何在Python中正确使用static、class、abstract方法
2017-02-05
Python 表达式 i += x 与 i = i + x 等价吗?
2017-10-29
Python每日一题:第1题
2017-09-05
Python:动态语言与鸭子类型
2017-07-19
如何正确理解@classmethod与@staticmethod
2015-12-16
Python列表对象实现原理
2017-03-15
Python 函数中,参数是传值,还是传引用?
2017-11-03
Python每日一题:第3题:统计一个文件中每个单词出现的次数