可以使用类属性作为实例方法的默认值吗?

36 投票
2 回答
10541 浏览
提问于 2025-04-16 06:13

我想在我的类的 __init__ 方法中,使用一个类属性作为某个参数的默认值。但是这样做会出现一个 NameError 的错误,我不明白为什么会这样:

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

为什么会失败呢?有没有什么方法可以做到这一点呢?

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
    # ...

撰写回答