2016-03-12 4 views
1

Я прочитал много вопросов, размещенных в ссылках на переменные в Python, но все еще происходит, когда я играю с кодом, который я бы не ожидал, и его довольно разочаровывающим. Возьмите этот кусок кода:Ссылка на Python для переменных класса и экземпляра

class MyClass(object) : 

    class_var = "original message" # same for all objects 

    def __init__(self, instance_var) : 
     self.instance_var = instance_var #particular to the object 

my_object = MyClass("my instance variable") 
untouched_object = MyClass("another instance_var") 

# Values of class_var and instance_var as expected 

MyClass.class_var = "changed message" 

# attribute class_var has changed for both objects 

# This changes attribute class_var for my_object only 
my_object.class_var = "I am my_object" 

# THIS ONLY CHANGES ATTRIBUTE CLASS_VAR FOR UNTOUCHED_OBJECT ONLY! 
MyClass.class_var = "Last message" 

print MyClass.class_var #prints Last Message 
print my_object.class_var #prints I am my_object ??? 
print untouched_object.class_var #prints Last Message 

Что здесь происходит? Когда я создаю объект некоторого класса с переменной класса, кажется, что object.class_attribute изначально содержит указатель на Class.class_var, и я могу изменить атрибут для всех объектов, назначив Class.class_var. Но если я явно присваиваю атрибуту object.class какое-то другое значение, указатель исчезает, а изменение Class.class_var больше не влияет на этот конкретный объект, а только на все остальные экземпляры. Это что здесь происходит? Кроме того, почему мы могли бы получить доступ и изменить атрибут класса таким образом и потенциально потерять доступ к значению, которое должно быть известно всем документам Python Documentation?

ответ

3

Когда вы

my_object.class_var = "I am my_object" 

создании экземпляра атрибут my_object.class_var, что тени атрибута класса MyClass.class_var, поэтому кажется, что последующее назначение MyClass.class_var не оказывает никакого влияния на my_object.

Это может ввести в заблуждение, чтобы попытаться понять модель данных Python с точки зрения понятий с других языков, например, ссылок и указателей. Попытайтесь подумать с точки зрения имен и привязки объектов к именам.

Итак, в вашем примере MyClass.class_var и my_object.class_var изначально оба имени привязаны к атрибуту класса, но присваивание связывает новый строковый объект с именем my_object.class_var.

Я нахожу, что он помогает думать о парах (имя, объект) как (ключ, значение) в словаре. В некоторых случаях это то, что происходит «под капотом» (например, с обычными классами, которые вы создаете сами), но в других случаях (например, встроенные типы Python) вещи реализуются немного по-разному по соображениям эффективности.

Вы можете найти эту статью полезной: Facts and myths about Python names and values, которая была написана ветеранкой СО Ned Batchelder.

+0

Спасибо, это начинает иметь смысл. Одна вещь, однако, я вижу много кода, ссылающегося на переменные класса внутри класса как 'self.class_var'. Почему бы им не использовать «Class.class_var»? – bmcentee148

+0

Ну, 'self.class_var' делает то, что вам нужно, когда вы просто читаете атрибут класса. И 'self' легче вводить, чем' MyClass' :) Я предполагаю, что соглашение должно явно использовать «MyClass.class_var» только в редких случаях, когда вы на самом деле пытаетесь изменить атрибут класса. –

2

Каждый объект имеет переменную с именем __dict__, которая хранит значения своих переменных-членов, индексированных по имени. Каждый класс также имеет переменную с именем __dict__, которая хранит значения своих переменных класса, индексированных по имени. Когда вы обращаетесь к переменной v экземпляра a с синтаксисом a.v, Python сначала ищет a.__dict__['v']. Если такого элемента нет, он выглядит в словаре классов, т. Е. a.__class__.__dict__['v']. Вот как переменные класса становятся видимыми для каждого экземпляра этого класса.

Но когда вы назначаете переменную, процесс отличается. Оператор вроде a.v = 12 заставляет Python выполнять a.__dict__['v'] = 12. Он не затрагивает словарь классов. Поэтому другие экземпляры класса не затронуты, как и следовало ожидать. Теперь, когда вы получаете доступ к a.v, вы получаете значение 12, заданное только для экземпляра значение, а не значение переменной класса. И вы больше не можете изменять a.v, перепутав с переменной класса, поскольку a теперь имеет свою собственную переменную с именем 'v'.

В результате вы должны использовать переменные класса в основном для установки «константы», аналогичные тому, как вы используете перечисление или #define в C. Это значение, которое вы не собираетесь изменять.Если вы ограничиваете переменные класса этим стилем использования, они весьма удобны и понятны.

+0

Хороший ответ. Но не у каждого класса есть '__dict__', у некоторых есть [' __slots__'] (https://docs.python.org/3/reference/datamodel.html#slots) –

+0

Правда, но я не хотел усложнять информация слишком много. –

Смежные вопросы