委托类构造导致递归错误

问题描述 投票:0回答:1

我正在构建一个框架,并希望允许用户轻松影响对象的构造(例如单例)

但是,无论我尝试什么,我总是会遇到递归。我想知道是否有一种方法可以在不改变 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)
    
python python-3.x recursion constructor metaclass
1个回答
0
投票
这里的问题是,当创建类的实例时,Python 所做的正是调用元类的

__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()
    
© www.soinside.com 2019 - 2024. All rights reserved.