Пути Pythonic для инициализации (сложных) элементов статических данных
У меня есть класс со сложным элементом данных, который я хочу сохранить "статическим". Я хочу инициализировать его один раз, используя функцию. Как Pythonic что-то вроде этого:
def generate_data():
... do some analysis and return complex object e.g. list ...
class Coo:
data_member = generate_data()
... rest of class code ...
Функция generate_data
требует много времени для завершения и возвращает данные, которые остаются постоянными в области выполняемой программы. Я не хочу, чтобы он запускался каждый раз, когда создается экземпляр класса Coo.
Кроме того, для проверки, если я не присваиваю ничего data_member
в __init__
, он останется "статическим"? Что делать, если метод в Coo добавляет некоторое значение в data_member
(при условии, что он является списком) - будет ли это добавление доступным для остальных экземпляров?
Спасибо
Ответы
Ответ 1
Вы правы во всех отношениях. data_member
будет создан один раз и будет доступен для всех экземпляров coo
. Если какой-либо экземпляр изменяет его, эта модификация будет видна для всех других экземпляров.
Вот пример, демонстрирующий все это, с его результатом, показанным в конце:
def generate_data():
print "Generating"
return [1,2,3]
class coo:
data_member = generate_data()
def modify(self):
self.data_member.append(4)
def display(self):
print self.data_member
x = coo()
y = coo()
y.modify()
x.display()
# Output:
# Generating
# [1, 2, 3, 4]
Ответ 2
Как другие ответили, что вы правы - я добавлю еще одну вещь, о которой нужно знать: если экземпляр изменяет сам объект coo.data_member
, например
self.data_member.append('foo')
то модификация рассматривается остальными экземплярами. Однако если вы делаете
self.data_member = new_object
затем создается новый экземпляр, который переопределяет член класса и доступен только этому экземпляру, а не другим. Разница не всегда легко обнаружить, например self.data_member += 'foo'
vs. self.data_member = self.data_member + 'foo'
.
Чтобы избежать этого, вы, вероятно, всегда должны ссылаться на объект как coo.data_member
(не через self
).
Ответ 3
Оператор data_member = generate_data()
будет выполняться только один раз, когда выполняется class coo: ...
. В большинстве случаев утверждения класса выполняются на уровне модуля и выполняются при импорте модуля. Таким образом, data_member = generate_data()
будет выполняться только один раз, когда вы впервые импортируете модуль с классом coo
.
Все экземпляры класса coo
будут делиться data_member
и могут получить к нему доступ, написав coo.data_member
. Любые изменения, внесенные в coo.data_member
, будут немедленно видны любому экземпляру coo
. Экземпляр может иметь свой собственный атрибут data_member
. Этот атрибут можно установить, набрав self.data_member = ...
и будет виден только этому экземпляру. "Статический" data_member
можно получить, набрав coo.data_member
.