我正在尝试传递对象一个
None
值,希望将其重新分配给实际值。有人可以解释一下为什么这不起作用吗?我认为由于 None
是一个 NoneType
,它将通过引用传入,当我使用传入的变量时,我会更改原始 None
对象。这是不正确的吗?我在下面提供了一个片段来演示我的案例:
class MyClass:
def __init__(self):
self.value = None
def assign(self, object):
object = OtherClass()
example = MyClass()
example.assign(example.value)
结果是
example.value
还是None
。这是为什么?我读过一些SO帖子,但没有一个能清楚地解释这一点。
编辑:不是重复的,因为我想知道为什么当我认为应该通过引用传递时,它的行为就像按值传递。最后,我得出的结论是
NoneType
对象是不可变的,因此总是按值传递。我需要使用 list
类型对象或可变的东西。
再次编辑:我的示例代码只是一般情况。我试图实现一个 BST,我的初始根节点是
None
,当我将根节点指定为 Node
对象时,它仍然是 NoneType
,这让我感到困惑,并且是这个问题。
最后编辑:下面的答案提供了非常好的 tl;dr 传递对象的摘要。仅通过搜索/阅读论坛我无法理解。
这正是按对象调用(Python 规则)与按引用调用(C++ 引用语义)不同的情况。
区别在于,分配给不合格的名称会重新绑定该名称,它对该名称之前可能绑定的任何内容都没有任何影响(除了可能会破坏它,如果没有其他引用保留的话)。该名称与可能引用同一对象的任何其他名称没有任何联系,因此这些其他名称不会更改。
因此在这种情况下,
object
最初被设置为指向example.value
指向的同一个对象。但在下一行,您重新绑定 object
(非限定名称分配),它指向一个完全不同的对象。
相比之下,如果您有:
def assign_value(self, object):
object.value = OtherClass()
并用以下方式调用它:
example.assign(example)
那么
example
和 object
将引用同一个对象,并且您对限定名称的分配将替换该对象上的 value
。
尽管如此,您的用例不需要任何此类更改。调用
example.assign(example.value)
没有任何意义,因为 self
是隐式传递的,并且会自动让您访问 value
(合格访问)。似乎您真正想要的只是在完全不带参数的情况下请求时进行延迟初始化:
def assign(self):
self.value = OtherClass()
称为:
example.assign()
响应您的编辑:Python 明确记录此调用行为:
函数调用的实际参数(实参)在调用时被引入到被调用函数的局部符号表中;因此,参数是使用按值调用来传递的(其中值始终是对象引用,而不是对象的值)。[1]
脚注 1 澄清:
[1] 实际上,通过对象引用调用会是一个更好的描述,因为如果传递可变对象,调用者将看到被调用者对其所做的任何更改(插入列表中的项目)。
这是有意为之;当没有其他可用的调用语义时,C++ 引用语义是行不通的,因为没有明显的选择退出方式,这意味着巨大调用链上的每个变量最终都会引用同一个对象,从而导致大量的操作-距离。
问题在于函数调用传递值,而不是引用。具体来说,传递给函数的引用保持不变,直到它被反弹(它的值设置),此时在函数本地创建一个副本。任何
other = ...
形式的行都会导致这种情况发生。如果您想要这种行为,则需要使用可变对象,例如列表。例如:
class MyClass:
def __init__(self):
self.value = [None]
def assign(self, object):
object[0] = OtherClass()
example = MyClass()
example.assign(example.value)
希望这有帮助!