Другие ответы охватывают, почему вам не нужен метакласс здесь. Этот ответ должен кратко объяснить, что делает метакласс __new__
: он создает ваш класс - он вызывается самой средой выполнения Python, когда обрабатывает оператор class <name>(bases, ...): <body>
. Нет, у вас не может быть класса без вызова метода метакласса __new__
- или хотя бы "метода root всех метаклассов", type
* __new__
.
Тем не менее, если по какой-то причине вам понадобится длинная задача, которая может быть запущена только один раз для всех создаваемых вами классов, все, что вам нужно будет сделать, - это кэшировать результаты длинной задачи и использовать кэшированные значения в последующих вызовах. .
Если по какой-то причине значение не может быть кэшировано и должно быть выполнено в метаклассе, вам нужно будет организовать, чтобы сами тела ваших классов выполнялись в разных шагах или с использованием асинхронного l * 1025. *. Более элегантные формы могут включать создание экземпляра concurrent.futures.ThreadPoolExecutor внутри метакласса __new__
, когда он вызывается впервые (и удерживает его в качестве атрибута метакласса), и после вызова type.__new__
для каждого класса передают часть, которая занимает много времени в будущем.
Как видите, поскольку вам нужно просто установить атрибут класса, вам следует избегать делать это как метакласс.
Тем не менее, конструкция может выглядеть так:
from concurrent.futures import ThreadPoolExecutor
TIMEOUT = 5
class PostInitParallelMeta(type):
executor = None
def __init__(cls, name, bases, ns, **kw):
super().__init__(name, bases, ns, **kw)
mcls = cls.__class__
if not mcls.executor:
mcls.executor = ThreadPoolExecutor(20)
mcls.executor.submit(cls._post_init)
class Base(metaclass=PostInitParallelMeta):
_initalized = False
def _post_init(cls, url):
# do long calculation and server access here
result = ...
cls.url = result
cls._initialized = True
def __init__(self, *args, **kw):
super.__init__(*args, **kw)
counter = 0
while not self.__class__._initialized:
time.sleep(0.2)
counter += 0.2
if counter > TIMEOUT:
raise RunTimeError(f"failed to initialize {self.__class__}")
class Foo(Base):
# set any parameters needed to the initilization task
# as class attributes
...
PS - Я только что написал это и понял, что код в метаклассе можно безопасно поместить в метод __init_subclass__
Base - метакласс вообще не нужен, даже для этого.