Python对原始类型的引用传递
我有使用命令式编程语言的经验(最常用的是C),这可能是我在学习Python时感到困惑的原因。
我想实现一个简单的位标志设置函数。在C语言中,它的写法大概是这样的:
void set_state(int *state, int var, int set)
{
(set)? *state |= var : *state &= ~var
}
int is_state(int *state, int var)
{
return (*state & var > 0)
}
这里的int *state指的是我用来跟踪的状态标志,而var是我想要设置或清除的标志(由int set决定)。
所以我试着在Python 3.2中做同样的事情,结果是...
def setState(nState, nVar, bSet):
if bSet:
nState |= nVar
else:
nState &= ~var
print(nState)
然后我运行了...
>>> state
1024
>>> nfirst
1
>>> nsecond
2
>>> setState(state, nfirst, True)
1025
>>> state
1024
调试器告诉我,'state'的值被复制到了nState中,现在nState在自己的局部范围内被改变了(这意味着它在自己的作用域内发生了变化)。难道Python不是说一切都是对象,都是按引用传递的吗?
如果是这样,为什么我看不到任何副作用呢?
我感到很迷茫。有人能解释一下这里发生了什么吗?
3 个回答
http://mail.python.org/pipermail/tutor/2002-November/018828.html 这个链接基本上回答了你的问题。
我们可以把Python里的所有“变量名”看作是指向对象的指针。实际上,事情就是这样的。
[...]
这里的关键是,在Python中进行算术运算(或者对“不可变”数据类型进行任何操作)时,并不会修改原来的对象,而是动态地构建新的对象:
因为列表、字典和自定义对象通常是可变的,所以对它们调用方法时不会创建新对象,而是会修改现有的对象,因此对于这些类型,你会看到类似引用传递的行为,这也是你可能期待的。
像数字或字符串这样的基本类型是不可变的,也就是说你不能改变一个数字,即使是通过引用传递过来的。当你改变这个值时,其实是创建了一个新的数字,然后把这个新数字的引用赋值给了nState
变量。
在Python中,int
类型的数字是不可变的,这意味着当你传递一个数字的引用时,实际上是无法改变这个数字的值的。你可以理解为,局部变量只是这个引用的一个拷贝,所以它们都指向同一个整数。但是,当你改变局部变量的值时,实际上是创建了一个新的整数对象,然后这个局部变量就指向了这个新的对象。
举个例子:
>>> def setState(nState, nVar, bSet):
... print((nState, id(nState)))
... if bSet:
... nState |= nVar
... else:
... nState &= ~var
... print((nState, id(nState)))
...
>>> nState = 1024
>>> nState, id(nState)
(1024, 3077840368)
>>> setState(nState, 1, True)
(1024, 3077840368) # local nState is bound to the same object
(1025, 3077840448) # now the local nState is bound to the new `int` object
也许你可以从函数中返回nState,然后再把它赋值回去,比如:
>>> def setState(nState, nVar, bSet):
... if bSet:
... nState |= nVar
... else:
... nState &= ~var
... return nState
...
>>> nState = 1024
>>> nState = setState(nState, 1, True)
>>> nState
1025