我正在构建一个框架,并希望允许用户轻松影响对象的构造(例如单例)
但是,无论我尝试什么,我总是会遇到递归。我想知道是否有一种方法可以在不改变 create_instance
def create_instance(cls, *args, **kwargs):
print("CUSTOM LOGIC TO CREATE INSTANCE")
return cls(*args, **kwargs)
class PersonMetaclass(type):
def __call__(cls, *args, **kwargs):
# Delegate creation elsewhere
return create_instance(cls, *args, **kwargs)
class Person(metaclass=PersonMetaclass):
def __init__(self, *args, **kwargs):
print("Person instance created")
person = Person()
输出:
CUSTOM LOGIC TO CREATE INSTANCE
CUSTOM LOGIC TO CREATE INSTANCE
CUSTOM LOGIC TO CREATE INSTANCE
CUSTOM LOGIC TO CREATE INSTANCE
..
E RecursionError: maximum recursion depth exceeded while calling a Python object
!!! Recursion detected (same locals & position)
__call__
方法。
type
的调用负责调用类
__new__
和
__init__
来创建新实例。在您的代码中,元类
__call__
调用一个函数,该函数将尝试通过再次调用该类来构建实例:这将调用相同的
__call__
方法,这就是您的递归。我通常非常反对使用元类来创建单例 - 这通常只是创建所需类的实例来充当单例,并在文档中公开它。或者只是修改类的
__new__
方法 - 不需要元类。 (但是单独更改
__new__
可能会导致
__init__
方法(如果有)被多次调用)。无论如何,如果您不想更改
create_instance
中的任何内容,那就是在元类的
__call__
方法中进行一些控制,以检测何时重新输入它,然后运行
super().__call__()
,而不是调用
create_instance
。如果您为此使用
ContextVar
,它将跨线程和异步代码工作。 (多处理代码,每个进程自然会有一个实例)
from contextvars import copy_context, ContextVar
CREATING_SINGLETON = ContextVar("CREATING_SINGLETON", default=False)
def create_instance(cls, *args, **kwargs):
print("CUSTOM LOGIC TO CREATE INSTANCE")
return cls(*args, **kwargs)
class PersonMetaclass(type):
def __call__(cls, *args, **kwargs):
# Delegate creation elsewhere
if CREATING_SINGLETON.get():
return super().__call__(*args, **kwargs)
ctx = copy_context()
# Do not look at me: I didn't create
# the contextvars API!
# this is the way to set a variable in the copied cotnext
ctx.run(lambda: CREATING_SINGLETON.set(True))
instance = ctx.run(create_instance, cls, *args, **kwargs)
del ctx
return instance
class Person(metaclass=PersonMetaclass):
def __init__(self, *args, **kwargs):
print("Person instance created")
person = Person()