Есть ли значимое различие между:
class A(object):
foo = 5 # some default value
против
class B(object):
def __init__(self, foo=5):
self.foo = foo
Если вы создаете много экземпляров, есть ли разница в производительности или требованиях к пространству для двух стилей? Когда вы читаете код, считаете ли вы, что значения этих двух стилей существенно различаются?
python
attributes
member-variables
Дэн Хомерик
источник
источник
Ответы:
Помимо соображений производительности, существует значительная семантическая разница. В случае атрибута класса упоминается только один объект. В экземпляре-атрибут-установлен-при-создании может быть ссылка на несколько объектов. Например
источник
int
иstr
они по-прежнему связаны с каждым экземпляром, а не с классом.int
иstr
являются также общими, точно таким же образом. Вы можете легко это проверить с помощьюis
илиid
. Или просто посмотрите на каждый экземпляр__dict__
и класс__dict__
. Просто обычно не имеет большого значения, являются ли неизменяемые типы общими или нет.a.foo = 5
, то в обоих случаях вы увидитеb.foo
возврат[]
. Это потому, что в первом случае вы перезаписываете атрибут классаa.foo
новым атрибутом экземпляра с тем же именем.Разница в том, что атрибут класса используется всеми экземплярами. Атрибут экземпляра уникален для этого экземпляра.
Если исходить из C ++, атрибуты класса больше похожи на статические переменные-члены.
источник
>>> class A(object): foo = 5
>>> a, b = A(), A()
>>> a.foo = 10
>>> b.foo
5
a.foo.append(5)
изменяет значение, на котороеa.foo
ссылается,a.foo = 5
превращаетсяa.foo
в новое имя для значения5
. Итак, вы получаете атрибут экземпляра, который скрывает атрибут класса. Попробуйте то же самоеa.foo = 5
в версии Алекса, и вы увидите, что ничегоb.foo
не изменилось.Вот очень хороший пост и резюмируйте его, как показано ниже.
И в наглядной форме
Назначение атрибута класса
Если атрибут класса установлен путем доступа к классу, он переопределит значение для всех экземпляров
Если переменная класса установлена путем доступа к экземпляру, она переопределит значение только для этого экземпляра . Это существенно переопределяет переменную класса и превращает ее в переменную экземпляра, доступную интуитивно только для этого экземпляра .
Когда бы вы использовали атрибут класса?
Хранение констант . Поскольку к атрибутам класса можно получить доступ как к атрибутам самого класса, часто полезно использовать их для хранения общеклассовых, зависящих от класса констант.
Определение значений по умолчанию . В качестве тривиального примера мы можем создать ограниченный список (т. Е. Список, который может содержать только определенное количество элементов или меньше) и выбрать ограничение по умолчанию в 10 элементов.
источник
Поскольку люди в комментариях здесь и в двух других вопросах, отмеченных как дубликаты, все, похоже, не понимают этого, я думаю, что стоит добавить дополнительный ответ поверх ответа Алекса Ковентри .
Тот факт, что Алекс присваивает значение изменяемого типа, например списка, не имеет ничего общего с тем, являются ли вещи общими или нет. Мы можем увидеть это с помощью
id
функции илиis
оператора:(Если вам интересно, почему я использовал
object()
вместо, скажем,5
это, чтобы избежать столкновения с двумя целыми другими проблемами, о которых я не хочу здесь вдаваться; по двум разным причинам полностью отдельно созданные5
s могут оказаться тот же экземпляр числа5
. Но полностью отдельно созданныеobject()
s не могут.)Итак, почему
a.foo.append(5)
в примере Алекса влияетb.foo
, аa.foo = 5
в моем - нет? Ну, попробуйтеa.foo = 5
в примере Алекса, и обратите внимание , что это не влияетb.foo
там либо .a.foo = 5
просто превращаетсяa.foo
в имя для5
. Это не влияетb.foo
ни на какое другое имя для старого значения, котороеa.foo
использовалось для обозначения. * Немного сложно создать атрибут экземпляра, скрывающий атрибут класса, ** но как только вы это получите, ничего сложного не будет происходит здесь.Надеюсь, теперь очевидно, почему Алекс использовал список: тот факт, что вы можете изменить список, означает, что легче показать, что две переменные называют один и тот же список, а также означает, что в реальном коде более важно знать, есть ли у вас два списка или два имени для одного и того же списка.
* Путаница для людей, пришедших с такого языка, как C ++, заключается в том, что в Python значения не хранятся в переменных. Ценности живут в мире ценностей сами по себе, переменные - это просто имена значений, а присваивание просто создает новое имя для значения. Если это помогает, думайте о каждой переменной Python как о файле, а
shared_ptr<T>
не оT
.** Некоторые люди пользуются этим, используя атрибут класса как «значение по умолчанию» для атрибута экземпляра, который экземпляры могут или не могут устанавливать. В некоторых случаях это может быть полезно, но также может сбивать с толку, поэтому будьте осторожны.
источник
Есть еще одна ситуация.
Атрибуты класса и экземпляра - это дескриптор .
Выше будет вывод:
Один и тот же тип доступа к экземпляру через класс или экземпляр возвращает другой результат!
И я нашел в определении c.PyObject_GenericGetAttr, и отличный пост .
объяснять
источник