Можно ли использовать атрибут класса как значение по умолчанию для метода экземпляра?
Я хотел бы использовать атрибут класса как значение по умолчанию для одного из аргументов метода class __init__
. Однако эта конструкция вызывает исключение NameError
, и я не понимаю, почему:
class MyClass():
__DefaultName = 'DefaultName'
def __init__(self, name = MyClass.__DefaultName):
self.name = name
Почему это не удается, и есть ли способ сделать это, который работает?
Ответы
Ответ 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')