【问题标题】:Python NoneType object is not working as intendedPython NoneType 对象未按预期工作
【发布时间】:2017-08-14 00:02:14
【问题描述】:

我正在尝试将 pass by object None 值重新分配给实际值。有人可以解释为什么这不起作用吗?我认为由于NoneNoneType,它将通过引用传入,当我使用传入的变量时,我会更改原始的None 对象。这是不正确的吗?我在下面提供了一个 sn-p 来演示我的案例:

class MyClass:
    def __init__(self):
        self.value = None

    def assign(self, object):
        object = OtherClass()

example = MyClass()
example.assign(example.value)

结果是example.value 仍然是None。这是为什么?我读过一些SO posts,但没有一个能清楚地解释这一点。

编辑:不是重复的,因为我想知道为什么当我认为它应该通过引用传递时它表现得像传递值。最后,我得出结论,NoneType 对象是不可变的,因此总是按值传递。我需要使用list 类型对象或可变的东西。

再次编辑:我的示例代码只是一般情况。我试图实现一个BST,我的初始根节点是None,当我将根节点分配为Node对象时,它仍然是NoneType,这让我感到困惑并且是导致这个问题。

最后编辑:下面的答案提供了一个非常好的 tl;dr 对象传递摘要。我无法仅通过搜索/阅读论坛来理解。

【问题讨论】:

  • “我想既然 None 是一个 NoneType ,它会通过引用传入” - 你能解释一下你为什么这么想吗?您认为“None is a NoneType”和“因此,它将通过引用传递”之间的联系是什么? Python 中没有任何内容是通过引用传递的。
  • 哦,在此之前我的理解是对象(通常是可变的)在 Python 中是通过引用传递的。我想既然 None 是一个 NoneType object,它必须通过引用传递。我的逻辑让我失望了:p

标签: python pass-by-reference pass-by-value


【解决方案1】:

这正是对象调用(Python 规则)与引用调用(C++ 引用语义)不同的情况。

不同之处在于,分配给非限定名称会重新绑定该名称,它对该名称之前可能绑定的任何内容都没有任何影响(除了可能会破坏它,如果没有其他引用保留)。该名称与可能引用同一对象的任何其他名称无关,因此这些其他名称不会更改。

所以在这种情况下,object 最初设置为指向 example.value 指向的同一对象。但在下一行,您重新绑定 object(非限定名称分配),它指向一个完全不同的对象。

相比之下,如果你有:

def assign_value(self, object):
    object.value = OtherClass()

并用以下方式调用它:

example.assign(example)

那么exampleobject 将引用同一个对象,并且您对限定名称的分配将替换该对象上的value

话虽如此,您的用例不需要任何此类更改。调用example.assign(example.value) 没有任何意义,因为self 是隐式传递的,并且会自动让您访问value(合格访问权限不少于)。似乎您真正想要的只是在根本没有参数的情况下进行延迟初始化:

def assign(self):
    self.value = OtherClass()

称为:

example.assign()

响应您的编辑:Python documents this call behavior explicitly:

函数调用的实参(arguments)在被调用函数的局部符号表中引入;因此,参数是使用按值调用传递的(其中值始终是对象引用,而不是对象的值)。[1]

脚注 1 阐明的地方:

[1] 实际上,通过对象引用调用会更好,因为如果传递了一个可变对象,调用者将看到被调用者对其所做的任何更改(插入到列表中的项目)。

这是有意的;当您没有其他可用的调用语义时,C++ 引用语义是不可行的,因为没有明显的选择退出方式,这意味着沿着巨大的调用链的每个变量最终都会引用同一个对象,从而导致大量的 action-at-a -距离。

【讨论】:

  • 我现在大吃一惊。谢谢!! (另外,确认您的“对比” sn-p 有效)
【解决方案2】:

问题是函数调用传递值,而不是引用。具体来说,传递给函数的引用保持不变,直到它被重新绑定(它的值集),此时在函数的本地创建一个副本。 other = ... 形式的任何行都会导致这种情况发生。如果你想要这种行为,你需要使用一个可变对象,比如一个列表。例如:

class MyClass:
    def __init__(self):
        self.value = [None]

    def assign(self, object):
        object[0] = OtherClass()

example = MyClass()
example.assign(example.value)

希望这会有所帮助!

【讨论】:

  • 哦,我不确定 NoneType 对象是否可变。我想在这种情况下不会。我也想改写我的问题来问这个问题,但问这个问题感觉很愚蠢。
  • @Sticky:None 是否可变并不重要。分配给非限定名称会替换它;旧值是什么并不重要。
  • 有时快速的谷歌搜索可能是最好的编码建议。
猜你喜欢
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 2017-10-01
  • 1970-01-01
  • 2021-10-13
  • 1970-01-01
  • 1970-01-01
  • 2020-08-31
相关资源
最近更新 更多