如果您具有C或C++背景,那么理解Python中的所有变量实际上都是指针可能更加简单。因此,这个语句:
a = 1
确实大致相似
Object *a = new Integer(1);
is
运算符检查指针相等性,而 ==
运算符则涉及取决于对象类型的计算。
这个方案的一个小复杂之处在于,如果对象是不可变的(例如整数),那么出于效率原因,上面的代码确实有点像:
int *a = getFromCacheOrCreateNewInteger(1);
有时候(但这只是实现细节),不可变对象可能从逻辑上独立创建,但在
is
操作下仍然是同一对象。(例如,可能会出现
1+1 is 2-1
的情况,但不保证一定如此):
>>> 1+2 is 2+1
True
>>> 99999+1 is 1+99999
False
>>>
为了增加一点混淆,即使 Python 中的所有变量确实都是指针,令人惊讶的是 Python 中没有指针的概念,换句话说,没有办法传递一个函数来存储你的变量中的某些内容。
要做到这一点,你需要传递一个名称(如果变量是全局的)或者传递一个 setter 函数以便调用(如果变量是本地的)。这并不是一个真正的大烦恼,因为在大多数情况下,你只想要多个返回值,Python 已经很好地处理了这个问题:
def foo(x):
return x+1, x-1
a, b = foo(12)
另一个额外的烦恼是,如果你真的需要传递一个没有名称的局部变量(例如列表的元素)的 setter,它不能是匿名
lambda
,因为赋值是一个
语句,而
lambda
只允许单个
表达式。但是,你可以为此定义局部函数...
def foo(x, setter):
setter(x + 1)
def bar():
mylocal = [1,2,3]
def setFirst(value):
mylocal[0] = value
foo(32, setFirst)
好吧,我承认……实际上可以使用lambda value: mylocal.__setitem__(0, value)
,但这更多的是一个不必要的事件;在Python中,lambda
是如此被憎恶,以至于一旦他们发现这是可能的,语言中可能会添加另一个限制来禁止它;-))。
如果您想改变命名的本地变量,那么在Python 2.x中就不可能(但在Python 3.x和nonlocal
中是可能的)。
关于何时执行副本以及何时仅复制指针的问题,答案非常简单。Python从不自动进行复制……如果您想进行复制,则必须明确地执行。这就是为什么例如经常看到像下面这样的代码:
class Polygon:
def __init__(pointlist):
self.pointlist = pointlist[:]
[:]
符号表示类实例希望存储传递的列表的副本,因此如果您使用点列表创建Polygon
实例,然后稍后修改此列表,则几何图形不会更改。