我最近偶然发现了Python中的元类,并决定使用它们来简化一些特性。(使用Python3.5)
简而言之,我正在编写一个模块,定义必须注册和初始化的类,例如“组件”(components)(我的意思是我需要初始化实际的类,而不是实例)。在
我可以轻松注册课程:
class MetaComponent(type):
def __init__(cls, *args, **kargs):
super().__init__(*args, **kargs)
RegisterComponent(cls)
class BaseComponent(metaclass=MetaComponent):
pass
class Component(BaseComponent):
"""This is the actual class to use when writing components"""
在本例中,我注册了组件的类,因为它允许我以后引用它们,而不需要实际引用它们。在
但是类缺乏初始化自身的能力(至少在Python3.5中是这样),并且会导致一些问题,例如:
^{pr2}$现在,这是一个问题,因为我们的想法是每个经理有一个唯一的子经理列表。但是SubManager
字段在每个子类中共享。。。所以把自己的名单加在每个人的名单上都会增加。在
所以下一个想法是实现一种初始化器:
class BaseManager(Component):
@classmethod
def classinit(cls):
cls.SubManagers = []
但是现在,我需要一种在类创建之后调用这个方法的方法。因此,让我们再次对元类执行此操作:
class MetaComponent(type):
def __init__(cls, *args, **kargs):
super().__init__(*args, **kargs):
cls.classinit(**kargs)
RegisterComponent(cls)
class BaseComponent(metaclass=MetaComponent):
@classmethod
def classinit(cls, **kargs):
print('BASE', cls)
class Component(BaseComponent):
@classmethod
def classinit(cls, **kargs):
super().classinit(**kargs) # Being able to use super() is the goal
print('COMPONENT', cls)
我认为我已经结束了这件事。在我看来,这是一种优雅的方法。将从创建的每个类中调用classinit()
(不同于在父类上调用的3.6__init_subclass__
)。至少我喜欢它,直到Python3.5在RuntimeError: super(): empty __class__ cell
中大受欢迎。。。在
我读到它是因为我从元类的__init__
方法中调用了一个方法,并且尽管类是创建的(因此我希望将代码放入__init__
,以初始化已经创建的内容),但它缺少这个__class__
单元格,至少在那时是这样的。。。在
我试着在Python3.6中运行完全相同的代码,但它起作用了,所以我猜出了问题,但得到了修复。。。在
我真正的问题是:
- Can we truly initialize classes with metaclasses in Python 3.5 ?
- Is there a way to avoid the use-restriction of super() in the initialization procedure?
- Why is it working in 3.6?
- If everything should fail, what would be the best course of action to still provide the class initialization, and allowing for super(...) calls? (Like do I need to refer to the super class explicitly ?)
提前谢谢你的帮助。在
编辑:
每个类的目标都是“能够很容易地从一个类中派生出一个组件:
class Manager(Component):
def classinit(cls, **kargs):
cls.SubManagers = []
@classmethod
def RegisterSubManager(cls, manager):
cls.SubManagers.append(manager)
return manager
@Manager.RegisterSubManager
class EventManager(Manager):
def classinit(cls, **kargs):
super().classinit(**kargs) # keep the old behaviour
cls.Events = []
# ...
@EventManager.RegisterSubManager
class InputManager(EventManager):
def classinit(cls, **kargs):
super().classinit(**kargs) # again, keep old behaviour
cls.Inputs = []
# use parts of EventManager, but define specialized methods
# for input management
管理者是一个问题,我有多个依赖于组件和初始化类的能力的概念。在
如果您希望您的
Manager
类型具有额外的行为,也许您希望它们拥有自己的、更精细的元类,而不仅仅是使用从Component
继承的元类。尝试编写从MetaComponent
继承的MetaManager
元类。您甚至可以将类方法从Manager1
移到元类中(在那里它们成为普通方法):好的,经过一些实验后,我设法提供了一个“修复”来允许类初始化,允许使用
super()
。在首先,模块要“修复”初始化方法:
现在,组件代码:
^{pr2}$说句公道话,我很高兴能完成。希望这能帮助那些想要初始化类的人(至少在Python3.6之前的环境中……)。在
TL;DR-如果您尝试从元类
__new__
或__init__
方法使用对super
的空调用,您确实会得到RuntimeError: super(): empty __class__ cell...
:在这个阶段,super
内部使用的隐式“magic”变量__class__
尚未创建。(在验证这一点时,我发现Python3.6中已经修复了这个问题——也就是说,在Python3.6中,使用无参数super
的类方法可以从元类的__init__
中调用,但在3.5中会产生这个错误)如果这是您目前唯一遇到的问题,只需硬编码对超类方法的调用,就像在Python中创建
super
之前所需要的那样。(使用super
的冗长形式将不起作用)。在在
使用classmethods作为注册的类装饰器,可以通过使用一个简单的Python名称修改来自动创建每个manager类唯一的
SubManagers
属性来实现,方法是检查一个类在其__dict__
中自己的名称空间,从而自动创建每个管理器类唯一的SubManagers
属性也可以不用元类)使用元类,只需在元类的末尾添加以下两行
__init__
:如果您的类修饰符方法排除了元类,则不需要为此使用元类-更改register方法以执行上述“own”子管理器列表创建:
^{pr2}$相关问题 更多 >
编程相关推荐