4

我编写了以下代码来检查整数是按值传递还是按引用传递。

foo = 1

def f(bar):
    print id(foo) == id(bar)
    bar += 1
    print foo, bar

f(foo)

我得到的输出是

True
1, 2

从 Python 文档中,id(object)返回对象的标识。在CPython实现中,这是对象在内存中的地址。既然函数体中的第一条语句返回了True,就表示foo已经通过引用传递了,但是为什么最后一条语句打印出来1, 2的却不是2, 2呢?

4

4 回答 4

5

在 Python 中,就像在许多现代 OO 语言中一样

foo = 1

实际上使用该值创建一个对象1并分配一个对 alias 的引用foo。foo 的内部类型是PyIntObject。这意味着 Python 不使用 CPU / 硬件 int 类型,它总是使用对象在内部处理数字。正确的术语是“纯整数”,顺便说一句。

但是创建对象非常昂贵。这就是 Python 为一些数字保留内部缓存的原因。意思是:

foo = 1
bar = 1
assert id(foo) == id(bar)

这不能保证,这只是实施的副作用。

Python 中的数字类型也是不可变的。因此,即使bar在您的示例中是缓存 int 数的别名,更改bar也不会修改内部值。相反,bar指向另一个实例,这就是 id 更改的原因。

由于上述优化,这也适用:

foo = 1
bar = 1
assert id(foo) == id(bar)

bar += 1
assert id(foo) != id(bar)

bar -= 1
assert id(foo) == id(bar)
于 2013-10-16T09:24:53.933 回答
3

围绕这个“问题”似乎有很多困惑。Python中的变量名实际上都是对对象的引用。对变量名的赋值实际上并没有改变对象本身,而是将引用设置为新对象。所以在你的情况下:

foo = 1 #

def test(bar):
    # At this point, "bar" points to the same object as foo.
    bar = 2    # We're updating the name "bar" to point an object "int(2)".
    # 'foo' still points to its original object, "int(1)".
    print foo, bar # Therefore we're showing two different things.

test(foo)

Python 的语法类似于 C 的方式以及许多东西都是语法糖的事实可能会令人困惑。记住整数对象实际上是不可变的,这foo += 1可能是一个有效的陈述似乎很奇怪。实际上,foo += 1实际上等价于foo = foo + 1,两者都转换为foo = foo.__add__(1),它实际上返回一个新对象,如下所示:

>>> a = 1
>>> id (a)
18613048
>>> a += 1
>>> id(a)
18613024
>>>
于 2013-10-16T09:40:30.670 回答
1

会发生以下情况:

print id(foo) == id(bar)

身份是一样的。print foo is bar顺便说一句,会产生相同的结果。

bar += 1

这被翻译成:

bar = bar.__iadd__(1)

只有当这不起作用或不存在时,它才会调用:

bar = bar.__add__(1)

(我省略了bar = 1.__radd__(bar)也可以称为的情况。)

Asbar指的是一个不可变的数字,而是返回一个不同的对象,所以bar指的是2现在,保持foo不变。

如果你做任何

print id(foo) == id(bar)
print foo is bar

现在,您会看到它们现在指向不同的对象。

于 2013-10-16T09:49:25.613 回答
0

在 Python 2 中,当前实现为 -5 和 256 之间的所有整数保留一个整数对象数组。因此,如果您分配一个变量 asvar1 = 1和一些其他变量 as var2 = 1,它们都指向同一个对象。

Python“变量”是指向对象的标签,而不是可以填充数据的容器,因此在重新分配时,您的标签指向一个新对象(而不是包含新数据的原始对象)。请参阅 Stack Overflow 问题Python 身份:多重人格障碍,需要代码收缩

在您的代码中,我介绍了更多打印语句,它们将显示变量是按值传递的

foo = 1
def f(bar):
    print id(foo) == id(bar)
    print id(1), id(foo), id(bar) #All three are same
    print foo, bar 
    bar += 1
    print id(bar), id(2)
    print foo, bar

f(foo)
于 2013-10-16T09:48:58.690 回答