Подтвердить что ты не робот

Можно ли использовать атрибут класса как значение по умолчанию для метода экземпляра?

Я хотел бы использовать атрибут класса как значение по умолчанию для одного из аргументов метода class __init__. Однако эта конструкция вызывает исключение NameError, и я не понимаю, почему:

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

Почему это не удается, и есть ли способ сделать это, который работает?

4b9b3361

Ответ 1

Это потому, что в соответствии с документацией:

Значения параметров по умолчанию оцениваются, когда определение функции. Это означает, что выражение оценивается один раз, когда функция определена

Когда __init__() определено, определение MyClass является неполным, поскольку оно все еще анализируется, поэтому вы еще не можете ссылаться на MyClass.__DefaultName. Один из способов обойти это - передать особое значение __init__(), например None:

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

Ответ 2

Важно помнить о Python, когда выполняются разные биты кода, а также то, что операторы class и def выполняются, когда они видны, а не просто сохраняются на потом. С def это немного легче понять, потому что единственное, что выполняется, - это то, что находится между (), поэтому в вашем коде выше

def __init__( SELF, name = MyClass.__DefaultName ):

когда Python видит MyClass.__DefaultName, он пытается найти его сначала в пространстве имен local, затем в пространстве имен модуля (aka global) и, наконец, в builtins.

Каково пространство имен local при выполнении инструкции class? Что забавная часть - это то, что станет пространством имен class, но на данный момент оно анонимно, поэтому вы не можете ссылаться на него по имени (aka MyClass в своем коде), но вы можете напрямую ссылаться на что-либо что уже определено... и что было определено? В вашем примере класса только одно - __DefaultName. Таким образом, ваш __init__ должен выглядеть так:

def __init__( SELF, name=__DefaultName ):

Имейте в виду, что вы не можете изменить MyClass._MyClass__DefaultName позже, и эти изменения появятся в новых экземплярах. Зачем? Поскольку __init__ def выполняется только один раз, и любое значение __DefaultName было в этот момент сохранено и всегда будет использоваться - если вы не укажете новое значение при создании нового экземпляра:

my_instance = MyClass(name='new name')