Шаг 80.
Python: тонкости программирования. Классы и ООП. Переменные класса против переменных экземпляра: подводные камни. Пример без собак

    На этом шаге мы рассмотрим еще один пример использования переменных класса и объекта.

    Рассмотрим еще один практический пример полезных штук, которые вы можете сделать с переменными класса. То, что будет немного ближе к реальным приложениям с переменными класса.

    Итак, вот этот пример. Приведенный ниже класс CountedObject отслеживает, сколько раз он использовался для создания экземпляров на протяжении жизни программы (что на деле может обеспечить интересный метрический показатель производительности):

>>> class CountedObject:
	num_instances = 0
	def __init__(self):
		self.__class__.num_instances += 1

    Класс CountedObject содержит переменную класса num_instances, которая служит в качестве общего счетчика. Когда класс объявлен, он инициализирует счетчик нулем, а затем оставляет его в покое.

    Всякий раз, когда вы создаете новый экземпляр этого класса, он увеличивает общий счетчик на единицу во время выполнения конструктора __init__:

>>> CountedObject.num_instances
0
>>> CountedObject().num_instances
1
>>> CountedObject().num_instances
2
>>> CountedObject().num_instances
3
>>> CountedObject.num_instances
3

    Обратите внимание, как этот фрагмент кода должен проскакивать через небольшой обруч, чтобы обеспечить увеличение переменной счетчика в классе. Легко можно было бы сделать ошибку, если бы конструктор был написан следующим образом:

# ПРЕДУПРЕЖДЕНИЕ: Эта реализация содержит ошибку
>>> class BuggyCountedObject:
	num_instances = 0
	def __init__(self):
		self.num_instances += 1 # !!!

    Как вы увидите, эта (плохая) реализация никогда не будет увеличивать общую переменную счетчика:

>>> BuggyCountedObject.num_instances 
0
>>> BuggyCountedObject().num_instances 
1
>>> BuggyCountedObject().num_instances 
1
>>> BuggyCountedObject().num_instances 
1
>>> BuggyCountedObject.num_instances 
0

    Надеемся, что вы увидели, где допущен промах. Эта (ошибочная) реализация не увеличивает общий счетчик, потому что была сделана ошибка, которая была объяснена на предыдущем шаге с псом Джеком. Эта реализация не будет работать, потому что непредумышленно "затенена" переменная класса num_instance созданием в конструкторе переменной экземпляра с тем же именем.

    Она правильно вычисляет новое значение счетчика (перейдя от 0 к 1), но затем сохраняет результат в переменной экземпляра, а это означает, что другие экземпляры класса никогда не увидят обновленное значение счетчика.

    Как вы видите, допустить эту ошибку очень легко. Во время работы с разделяемым состоянием в классе следует быть осторожным и перепроверять области действия. Автоматизированные тесты и контроль качества работы со стороны коллег существенно помогают в этом.

    Однако надеемся, что вы видите, почему и как переменные класса (несмотря на их подводные камни) могут оказаться полезными инструментами на практике. Удачи!

    На следующем шаге мы подитожим изученный материал.




Предыдущий шаг Содержание Следующий шаг