Почему ссылки на атрибуты действуют так, как наследование Python?
Похоже, странно. В принципе, атрибут somedata кажется общим для всех классов, унаследованных от the_base_class
.
class the_base_class:
somedata = {}
somedata['was_false_in_base'] = False
class subclassthing(the_base_class):
def __init__(self):
print self.somedata
first = subclassthing()
{'was_false_in_base': False}
first.somedata['was_false_in_base'] = True
second = subclassthing()
{'was_false_in_base': True}
>>> del first
>>> del second
>>> third = subclassthing()
{'was_false_in_base': True}
Определение self.somedata
в функции __init__
, очевидно, является правильным способом обойти это (поэтому у каждого класса есть собственный somedata
dict) - но когда такое поведение желательно?
Ответы
Ответ 1
Вы правы, somedata
разделяется между всеми экземплярами класса и его подклассов, потому что он создается во время определения класса. Линии
somedata = {}
somedata['was_false_in_base'] = False
выполняются, когда класс определен, т.е. когда интерпретатор встречает оператор class
- не, когда экземпляр создан (подумайте о статических инициализационных блоках в Java). Если атрибут не существует в экземпляре класса, объект класса проверяется для атрибута.
Во время определения класса вы можете запустить произвольный код, например:
import sys
class Test(object):
if sys.platform == "linux2":
def hello(self):
print "Hello Linux"
else:
def hello(self):
print "Hello ~Linux"
В системе Linux Test().hello()
будет печатать Hello Linux
, а во всех остальных системах будет напечатана другая строка.
В constrast объекты в __init__
создаются во время создания экземпляра и принадлежат только экземпляру (когда они назначены self
):
class Test(object):
def __init__(self):
self.inst_var = [1, 2, 3]
Объекты, определенные для объекта класса, а не экземпляра, могут быть полезны во многих случаях. Например, вы можете захотеть кэшировать экземпляры вашего класса, чтобы экземпляры с одинаковыми значениями элементов могли быть разделены (при условии, что они должны быть неизменными):
class SomeClass(object):
__instances__ = {}
def __new__(cls, v1, v2, v3):
try:
return cls.__insts__[(v1, v2, v3)]
except KeyError:
return cls.__insts__.setdefault(
(v1, v2, v3),
object.__new__(cls, v1, v2, v3))
В основном, я использую данные в телах классов в сочетании с метаклассами или общими методами factory.
Ответ 2
Обратите внимание, что часть видимого вами поведения связана с тем, что somedata
является dict
, в отличие от простого типа данных, такого как bool
.
Например, см. этот другой пример, который ведет себя по-разному (хотя очень похож):
class the_base_class:
somedata = False
class subclassthing(the_base_class):
def __init__(self):
print self.somedata
>>> first = subclassthing()
False
>>> first.somedata = True
>>> print first.somedata
True
>>> second = subclassthing()
False
>>> print first.somedata
True
>>> del first
>>> del second
>>> third = subclassthing()
False
Причина, по которой этот пример ведет себя иначе, чем тот, который задан в вопросе, заключается в том, что здесь first.somedata
присваивается новое значение (объект True
), тогда как в первом примере объект dict, на который ссылается first.somedata
( а также другими экземплярами подкласса).
См. комментарий Torsten Mareks к этому ответу для дальнейшего уточнения.
Ответ 3
Я думаю, что самый простой способ понять это (чтобы вы могли предсказать поведение) - понять, что ваш somedata
является атрибутом класса, а не экземпляром этого класса, если вы его определяете.
В действительности существует только один somedata
, потому что в вашем примере вы не назначали это имя, но использовали его для поиска дикта, а затем назначали ему элемент (ключ, значение). Это результат, который является следствием работы интерпретатора python и может сбить с толку вначале.