Это не объявление, это назначение ... переменной внутри класса, в отличие от переменной внутри экземпляра.
Рассмотрим следующий вывод:
>>> class K1(object):
... def __init__(self):
... self.attr = 'value'
...
>>> x = K1()
>>> x.__dict__
{'attr': 'value'}
>>> class K2(object):
... attr = 'value'
... def __init__(self):
... self.another = 'value2'
...
>>> y = K2()
>>> y.__dict__
{'another': 'value2'}
Здесь x
является экземпляром класса K1 и имеет атрибут с именем attr
и y
является экземпляром класса К2 и имеет другой атрибут с именем another
. Но:
>>> y.attr
'value'
Откуда это взялось? Он пришел из класса:
>>> y.__class__.__dict__
dict_proxy({'__module__': '__main__', 'attr': 'value',
'__dict__': <attribute '__dict__' of 'K2' objects>,
'__weakref__': <attribute '__weakref__' of 'K2' objects>,
'__doc__': None, '__init__': <function __init__ at 0x80185b9b0>})
Это своего рода грязный, но вы можете увидеть attr
сидит там. Если вы посмотрите на x.__class__.__dict__
нет attr
:
>>> x.__class__.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'K1' objects>,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'K1' objects>,
'__doc__': None, '__init__': <function __init__ at 0x80185b938>})
Когда вы получаете атрибут экземпляра, как x.attr
или y.attr
, Python сначала ищет что-то прилагается к самому экземпляру. Однако если ничего не найдено, оно «смотрит вверх», чтобы увидеть, определяет ли что-то другое этот атрибут. Для классов с наследованием, которые включают в себя список «порядок принятия решений». В этом случае нет наследования, о которых нужно беспокоиться, но следующим шагом является просмотр самого класса. Здесь, в K2, есть атрибут в классе с именем attr
, так это то, что производит y.attr.
Вы можете изменить атрибут класса, чтобы изменить то, что отображается в y.attr
:
>>> K2.attr = 'newvalue'
>>> y.attr
'newvalue'
И в самом деле, если вы сделаете еще один экземпляр K2(), это тоже подберут новое значение:
>>> z = K2()
>>> z.attr
'newvalue'
Обратите внимание, что при изменении х-х attr
не влияет на новые экземпляры K1():
>>> w = K1()
>>> w.attr = 'private to w'
>>> w.attr
'private to w'
>>> x.attr
'value'
Это потому, что w.attr
действительно w.__dict__['attr']
, и x.attr
действительно x.__dict__['attr']
. С другой стороны, y.attr
и z.attr
оба действительно y.__class__.__dict__['attr']
и z.__class__.__dict__['attr']
, а так как y.__class__
и z.__class__
оба являются K2
, меняется K2.attr
меняется оба.
(Я не уверен, что парень, который написал страницу, указанную в исходном вопросе, все это понимает. Создание атрибута на уровне класса, а затем создание одноуровневого уровня с одним и тем же именем является беспредметным.)
Пример может иметь, но результаты не являются * точно * одинаковыми. – Elazar
«first = str()» создает переменную, связанную с ** классом **, в то время как «self.first = ...» создает новую для экземпляра (это не значит, что это имеет смысл). –
Просто помните: там, наверное, более ужасный * плохой код, чем хороший. – Voo