Python3.5 : Class initialization using inheritance

标签 python metaprogramming python-3.5 metaclass

我最近偶然发现了 Python 中的元类,并决定使用它们来简化一些功能。 (使用Python 3.5)

简而言之,我正在编写一个模块,定义必须注册和初始化的类,例如“组件”(我的意思是我需要初始化实际的类,而不是实例)。

我可以轻松注册类(class):

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"""

在本例中,我正在注册组件的类,因为它允许我稍后引用它们,而无需它们的实际引用。

但是类缺乏初始化自身的能力(至少在 Python 3.5 中),并且可能会导致一些问题,例如:

class Manager(Component):
    SubManagers = []

    @classmethod
    def ListSubManagers(cls):
        for manager in cls.SubManagers:
            print(manager)

    @classmethod
    def RegisterSubManager(cls, manager):
        cls.SubManagers.append(manager)
        return manager

@Manager1.RegisterSubManager
class Manager2(Manager):
    pass

@Manager2.RegisterSubManager
class Manager3(Manager):
    pass

# Now the fun:
Manager1.ListSubManagers()

# Displays:
# > Manager2
# > Manager3

现在,这是一个问题,因为我们的想法是为每个经理提供一个唯一的子经理列表。但是 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__ 不同)。至少我喜欢它,直到 Python 3.5 在 RuntimeError: super():empty __class__ cell...

中哭泣

我读到它是因为我从元类的 __init__ 方法调用一个方法,并且尽管创建了该类(因此我将代码放入 __init__ 中,以初始化已经创建的东西),它缺少这个 __class__ 单元,至少在那一刻......

我尝试在 Python 3.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

管理器是一个问题,我有多个依赖于组件及其初始化类的能力的概念。

最佳答案

TL;DR - 如果您尝试从元类 __new____init__ 方法:在此阶段,由 super 内部使用的隐式“魔法”变量 __class__尚未创建。 (在验证这一点时,我刚刚发现这个问题已在 Python 3.6 中修复 - 即:使用无参数 super 的类方法可以从 Python 3.6 中的元类的 __init__ 调用,但是在 3.5 中产生此错误)

如果这是目前唯一阻碍您的事情,只需硬编码对父类(super class)方法的调用,就像在 Python 中创建 super 之前需要的那样。 (使用 super 的详细形式也不起作用)。

--

您的倒数第二个想法,即使用类方法作为注册的类装饰器,可以通过使用简单的 Python 名称重整自动创建带有元类的 SubManagers 属性来实现。通过检查一个类在其 __dict__ 中自己的命名空间来创建每个管理器类唯一的 SubManagers 属性(并且也可以在没有元类的情况下完成)

使用元类,只需在元类末尾添加以下两行__init__:

if getattr(cls, "SubManagers") and not "SubManagers" in cls.__dict__:
    cls.SubManagers = []

如果您的类装饰器方法以其他方式排除元类,则您不需要为此使用元类 - 更改您的注册方法以执行上述“自己的”子管理器列表创建:

@classmethod
def RegisterSubManager(cls, manager):
   if not "SubManagers" in cls.__dict__:
       cls.SubManagers = []
   cls.SubManagers.append(manager)

关于Python3.5 : Class initialization using inheritance,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45871486/

相关文章:

python - Python 中的类字符列表映射如何工作?

定义函数闭包的 Pythonic 方式

ruby - 如何在 Ruby 中动态改变继承

python - 在 python 3.5 中,如何将字符串变量与另一个字符串的一部分进行比较?

python - 在单行中询问多个用户输入

java - 无法运行 Grinder Java 测试框架

没有属性样板的 Python 类

python 3 "Too many arguments"?

python - cx_freeze 一直在 python/libs 中查找,而不是在已编译的库中查找

Python - Selenium : Find/Click YT-Like Button