Пути 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.