#python #python-2.7 #class-instance-variables
#питон #python-2.7 #class-instance-переменные
Вопрос:
У меня есть класс Python , для атрибута class которого установлено значение , отличное от None
. При создании нового экземпляра изменения, внесенные в этот атрибут, сохраняются во всех экземплярах.
Вот некоторый код, чтобы разобраться в этом:
class Foo(object):
a = []
b = 2
foo = Foo()
foo.a.append('item')
foo.b = 5
Использование foo.a
возвратов ['item']
и foo.b
возвратов 5
, как и следовало ожидать.
Когда я создаю новый экземпляр (мы назовем его bar
), используя bar.a
returns ['item']
и bar.b
return 5
, тоже! Однако, когда я изначально устанавливаю все атрибуты класса None
, а затем устанавливаю для них значение whatever in __init__
, например:
class Foo(object):
a = None
b = None
def __init__(self):
self.a = []
self.b = 2
Использование bar.a
возвратов []
и bar.b
возвратов 2
при foo.a
возврате ['item']
и foo.b
возврате 5
.
Так вот как это должно работать? По-видимому, я никогда не сталкивался с этой проблемой за те 3 года, что программировал Python, и хотел бы получить некоторые разъяснения. Я также не могу найти его нигде в документации, поэтому было бы замечательно дать мне ссылку, если это возможно. 🙂
Ответ №1:
Да, именно так это и должно работать.
Если a
и b
принадлежат экземпляру Foo
, то правильный способ сделать это:
class Foo(object):
def __init__(self):
self.a = []
self.b = 2
Следующее создает a
и b
принадлежит самому классу, поэтому все экземпляры используют одни и те же переменные:
class Foo(object):
a = []
b = 2
Когда вы смешиваете два метода — как вы делали во втором примере — это не добавляет ничего полезного и просто вызывает путаницу.
Стоит упомянуть одно предостережение: когда вы делаете следующее в своем первом примере:
foo.b = 5
вы не меняете Foo.b
, вы добавляете совершенно новый атрибут к foo
этим «теням» Foo.b
. Когда вы это сделаете, ни bar.b
то, ни Foo.b
другое не изменится. Если вы впоследствии это сделаете del foo.b
, это удалит этот атрибут и foo.b
снова будет ссылаться на Foo.b
.
Комментарии:
1. Ах, я вижу свет! До сих пор я никогда не понимал разницы между атрибутом класса и экземпляра! Я всегда предполагал, что атрибуты класса определены внутри самого класса (например, в примерах), и экземпляр будет вызываться с чем-то вроде
foo.c = 2
(определение на лету, если хотите). Имеет смысл! Спасибо!
Ответ №2:
Да, это именно то, чего вы должны ожидать. Когда вы определяете переменную в классе, эти атрибуты присоединяются к классу, а не к экземпляру. Возможно, вы не всегда замечаете, что при присвоении атрибута экземпляру экземпляр получает новое значение, маскируя атрибут класса. Методы, которые изменяются на месте, например list.append
, не будут давать экземпляру новый атрибут, поскольку они просто изменяют существующий объект, который является атрибутом класса.
Каждый раз, когда каждый экземпляр класса должен иметь свое собственное уникальное значение для атрибута, обычно его следует устанавливать в __init__
методе, чтобы быть уверенным, что оно отличается для каждого экземпляра.
устанавливать атрибуты для класса следует только в том случае, если у класса есть атрибут, имеющий разумное значение по умолчанию, и это значение имеет неизменяемый тип (который нельзя изменить на месте), например, int
или str
.
Комментарии:
1. Спасибо! Как я уже сказал в ответе выше, я, очевидно, не знал разницы между атрибутами класса и экземпляра! Я думаю, это, вероятно, одно из предостережений от самоучки и нетерпения. 😉
Ответ №3:
Да, вначале это кажется удивительным, но именно так работает python, как было сказано в других ответах. Три года python, не будучи сокращенным этим? Вам повезло, очень повезло. На вашем месте я бы проверил чувствительный прошлый код на предмет маленьких животных…
Будьте также осторожны с вызовами функций: def func(list=[])
имеет аналогичное поведение, что может быть удивительно.
И, если возможно, подключите синтаксический анализ pylint в ваших перехватах фиксации. Или, по крайней мере, запустите pylint когда-нибудь в своем коде, это очень поучительно, и, вероятно, рассказал бы вам об этом трюке (который на самом деле не трюк, он очень логичен и ортогональен, просто по-python).
Ответ №4:
Вы путаете атрибут класса с атрибутом экземпляра. В вашем первом примере есть только атрибуты класса a
и b
, но во втором у вас также есть атрибуты экземпляра с теми же именами.
Похоже, что Python будет передавать ссылки на атрибут из экземпляра в класс, если атрибута экземпляра нет (сегодня я узнал кое-что новое!).
Вы можете найти этот код поучительным:
class Foo:
a = 1
b = 2
def __init__(self):
self.a=3
print Foo.a # => 1
print Foo().a # => 3
print Foo.b # => 2
print Foo().b # => 2
Комментарии:
1. Спасибо! Я определенно путал два!