Python中的别名是否保证引用同一对象?
在Python文档中提到,名字其实是对象的别名:
对象是独一无二的,多个名字(在不同的作用域中)可以指向同一个对象。这在其他编程语言中被称为别名。在初学Python时,这一点可能不太容易理解,尤其是在处理不可变的基本类型(比如数字、字符串、元组)时,可以暂时忽略这个概念。不过,当涉及到可变对象(比如列表、字典和大多数其他类型)时,别名可能会对Python代码的含义产生意想不到的影响。通常情况下,这种特性对程序是有利的,因为别名在某些方面表现得像指针。例如,传递一个对象是很便宜的,因为实现上只传递了一个指针;如果一个函数修改了作为参数传入的对象,调用者会看到这个变化——这就省去了像Pascal那样需要两种不同参数传递机制的麻烦。
在一些其他高级语言中,基本类型通常会被特殊处理,为了性能原因,它们会被复制而不是引用。例如,在Java中:
int a = 20000;
int b = a;
上面的代码会复制值20000
,而不是指向值20000
的指针。在这种情况下,a
和b
可能会占用内存中的不同位置。由于在基本类型上,==
被特殊处理用来测试相等性而不是身份,所以我认为在正常代码中无法直接观察到这种行为。
另一方面,对Python 3中的int
和str
等类型进行有限测试显示,确实是复制了指针而不是值,这在文档中有说明:
a = 20000
b = a
a is b # True
这是一个非常好的特性,使得语言非常一致,因为没有对基本类型的特殊处理。所有的赋值都是将一个名字重新指向另一个对象。不过,出于性能原因,Python解释器是否可能会对像int
这样的类型进行特殊处理呢?
因此,我的问题是:这种基本类型的特性是否有保证?换句话说,在b = a
之后,不管使用哪个Python解释器,比较a is b
是否总是会返回True
?
4 个回答
在Python中,所有的变量和表达式其实都是指向对象的引用,简单来说就像是指针。如果你对C++有点了解,可以想象成下面这样:
object *a = new object(20000);
object *b = a;
a == b // true
当你把一个变量赋值给另一个变量时,实际上是复制了一个指向同一个对象的指针。也就是说,赋值之后这两个指针会一直指向同一个对象。
>>> int
<class 'int'>
Python 和 Java 在处理基本数据类型上有些不同。你可以看到,int
实际上是一个类。这个类的实例就是我们所说的 对象,这意味着它有很多特性。值得注意的是,这个类是内置的,而不是在某个 .py
文件中定义的。
可以想象一下 Java 中 Integer
和 int
的区别。
当你写 a = 1; b = a
时,其实是把 b
设置为指向和 a
一样的对象,这样就不会复制值。
至于特殊情况,CPython 会把“较小”的整数(从 -5 到 255)处理成单例,这样就不需要重新创建它们,但这只是实现上的一个细节。
b = a
这行代码让 b
和 a
指向同一个对象。也就是说,a
和 b
其实是同一个东西。所以在 Python 中,a is b
这个判断永远会返回 True。
是的,这在文档中有说明:
如果目标是一个标识符(名字):
如果这个名字在当前代码块中没有出现在全局声明里:这个名字就会和当前本地命名空间中的对象绑定。
否则:这个名字就会和当前全局命名空间中的对象绑定。
(在Python 3中,还有一个和nonlocal
相关的额外情况,但规则是一样的;唯一的区别是绑定发生在哪个命名空间中。)
需要特别注意的是,这个规则只适用于给一个裸名字赋值。如果赋值是像a.foo = b
或者a[blah] = b
这样的情况,那就没什么规律可言了,几乎可以发生任何事情。