Как создавать и хранить экземпляры класса при инициализации?

Используя Python 3.3, я хочу привязать Test класса к другому классу TestManager чтобы менеджер создавал экземпляры Test и сохранял их для последующего доступа к ним.

В двух словах (я имею в виду, Python shell …), я хочу иметь возможность сделать это (предполагая, что name является атрибутом Test ):

 > t = Test.objects.get(id=3) > t.name # Returns 'Name 3' 

Фокус в том, что моя коллекция объектов является «статической» коллекцией, в некотором смысле, что она создается сначала (а не каким-либо пользователем), а затем никогда не изменялась и не удалялась, ни ее записи не удалялись и не редактировались. Это фиксированная. Итак, вот код, который я пробовал:

 class TestManager: def __init__(self, *args, **kwargs): self._tests = [Test(name='Name {}'.format(i)) for i in range(100)] def get(self, id): return self._tests[id] class Test: objects = TestManager() def __init__(self, name=''): self.name = name 

Aaaand, как и ожидалось, NameError: global name 'Test' is not defined из-за циклической инициализации. В идеале, у меня должен быть метод create() в менеджере, который позаботится о добавлении элементов в список (вместо __init__() ), но это будет означать, что создание не выполняется в менеджере, а в другом месте.

«Лучшее» решение, с которым я столкнулся, до сих пор заключается в том, чтобы проверить сначала метод get() если список пуст, и, таким образом, вызвать метод fill_the_damn_list() , но для меня это кажется очень хакерским. Другой способ сделать это – использовать dict вместо списка и создавать экземпляры «на лету» сперва get() . Преимущество последнего состоит в том, что он не создает бесполезных / никогда не get() экземпляров, но всего лишь сотню из них в целом, я не уверен, что это действительно имеет значение, и хакерство этого решения выглядит вполне то же самое для меня…

Поскольку я довольно новичок в Python (если это недостаточно ясно …), мне интересно, есть ли лучший способ сделать это и сохранить его простым. Я также согласен на рефакторинг, если это необходимо, но я еще не нашел лучшего решения …

Ваш дизайн кажется немного странным – непонятно, почему классу Test требуется ссылка на экземпляр TestManger . Несмотря на это, я думаю, что следующее произойдет. Он использует метакласс для создания атрибута objects класса Test и добавляет атрибут _tests вы хотите TestManger экземпляр TestManger он создал, – и все это делает это довольно своеобразным ответом … я полагаю. 😉

 class TestManager: def __init__(self, *args, **kwargs): print('creating TestManager') def get(self, id): return self._tests[id] class TestMetaClass(type): def __new__(mcl, name, bases, classdict): # add an "objects" attribute to the class being created classdict['objects'] = tm = TestManager() cls = type.__new__(mcl, name, bases, classdict) # add a "_tests" attribute to the TestManager instance just created # (can't use class's name, since it hasn't been returned yet) tm._tests = [cls(name='Name {}'.format(i)) for i in range(100)] return cls class Test(metaclass=TestMetaClass): def __init__(self, name=''): self.name = name t = Test.objects.get(3) print(t.name) 

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

Простой способ:

 class TestManager: def __init__(self, *args, **kwargs): self._tests = [Test(name='Name {}'.format(i)) for i in range(100)] def get(self, id): return self._tests[id] class Test: objects = None def __init__(self, name=''): self.name = name Test.objects = TestManager() 

Другой подход может использовать декоратор:

 >>> class TestManager(object): ... def __init__(self, *args, **kwargs): ... self._tests = [] ... def init_class(self, cls): ... self._tests = [cls(name='Name {}'.format(i)) for i in range(100)] ... cls.objects = self ... return cls ... def get(self, id): ... return self._tests[id] ... >>> manager = TestManager() >>> @manager.init_class ... class Test(object): ... def __init__(self, name=''): ... self.name = name ... >>> manager.get(5) <__main__.Test object at 0x7f4319db8110> 

Вышеупомянутый рецепт работает, если TestManager является Singleton, но если он не является одиночным, вам просто нужно запомнить вызов TestManager.init_class(TheClass) перед доступом к экземплярам класса, и это можно сделать в любом месте вашего кода.

Вы также можете использовать геттеры для этого:

 >>> class TheGetter(object): ... def __init__(self, cls): ... self._cls = cls ... self._inst = None ... def __get__(self, inst, owner): ... if self._inst is None: ... self._inst = self._cls() ... return self._inst ... >>> class Test(object): ... objects = TheGetter(TestManager) ... def __init__(self, name): ... self.name = name ... >>> Test.objects.get(5) <__main__.Test object at 0x7f431690c0d0>