我能否将类属性用作实例方法的默认值?

36

我想将类属性作为我的类 __init__ 方法的一个参数的默认值。但这个结构会引发 NameError 异常,我不明白为什么会出现这种情况:

class MyClass():
    __DefaultName = 'DefaultName'
    def __init__(self, name = MyClass.__DefaultName):
        self.name = name

为什么这个失败了,有没有一种方法可以做到这一点?


1
请务必阅读Ethan的回答,它提供了可行的语法,让您可以完成这个任务,不要仅仅阅读不太有用的被采纳的答案。 - Mark Amery
2个回答

30

在Python中需要牢记的一件重要事情是代码的不同部分被执行的时间,以及classdef语句在被查看时就会被执行,而不仅仅是被存储起来。对于def,理解起来要容易一些,因为唯一被执行的是()之间的内容 —— 所以,在你上面的代码中,

def __init__( SELF, name = MyClass.__DefaultName ):
当Python看到MyClass.__DefaultName时,它首先在local命名空间中查找,然后在模块(即global)命名空间中查找,最后在builtins中查找。
当执行class语句时,local命名空间是什么? 这就是有趣的部分 - 它将成为class的命名空间,但此时它是匿名的 - 因此您无法通过名称(即您代码中的MyClass)引用它,但是您可以直接引用已经定义的任何内容...已经定义了什么呢? 在您的类示例中只有一件事 - __DefaultName。 因此,您的__init__应该如下所示:
def __init__( SELF, name=__DefaultName ):

请记住,您无法在稍后更改MyClass._MyClass__DefaultName并使这些更改在新实例中显示。为什么?因为__init__方法只执行一次,并且在那个时刻__DefaultName的任何值都已被存储,并且将始终使用该值 - 除非您在创建新实例时直接指定新值:

my_instance = MyClass(name='new name')

28

这是因为根据文档的描述:

默认参数值在函数定义时被计算。 这意味着表达式只会在函数定义时被计算一次。

当定义__init__()时,MyClass的定义尚不完整,因为它仍在解析中,所以你还不能引用MyClass.__DefaultName。解决这个问题的方法之一是向__init__()传递一个特殊的唯一值,例如None

def __init__(self, name=None):
    if name is None:
        name = MyClass.__DefaultName
    # ...

我宁愿不明确地提到类的名称。从实例方法访问类和类属性的推荐方式是什么?使用 type(self) 可以吗? - Alexey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接