听说90%的人都答不对这道Python题

作者: liuzhijun | 来源:发表于2017-11-21 10:47 被阅读53次

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

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

    # 第一组
    >>> 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的值了,就重用了这个对象。>博客:foofish.net > 公众号:Python之禅

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

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

    相关文章

      网友评论

      • 9fc9a43bd61a:有深度,虽然一般用不上,但是学到了深度。

      本文标题:听说90%的人都答不对这道Python题

      本文链接:https://www.haomeiwen.com/subject/tbvivxtx.html