Python对原始类型的引用传递

1 投票
3 回答
6488 浏览
提问于 2025-04-16 20:25

我有使用命令式编程语言的经验(最常用的是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 个回答

1

http://mail.python.org/pipermail/tutor/2002-November/018828.html 这个链接基本上回答了你的问题。

我们可以把Python里的所有“变量名”看作是指向对象的指针。实际上,事情就是这样的。
[...]
这里的关键是,在Python中进行算术运算(或者对“不可变”数据类型进行任何操作)时,并不会修改原来的对象,而是动态地构建新的对象:

因为列表、字典和自定义对象通常是可变的,所以对它们调用方法时不会创建新对象,而是会修改现有的对象,因此对于这些类型,你会看到类似引用传递的行为,这也是你可能期待的。

5

像数字或字符串这样的基本类型是不可变的,也就是说你不能改变一个数字,即使是通过引用传递过来的。当你改变这个值时,其实是创建了一个新的数字,然后把这个新数字的引用赋值给了nState变量。

8

在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

撰写回答