Python3.5:使用inheritan初始化类

2024-10-01 02:32:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近偶然发现了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

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


Tags: theto方法initdefargs组件class
3条回答

如果您希望您的Manager类型具有额外的行为,也许您希望它们拥有自己的、更精细的元类,而不仅仅是使用从Component继承的元类。尝试编写从MetaComponent继承的MetaManager元类。您甚至可以将类方法从Manager1移到元类中(在那里它们成为普通方法):

class MetaManager(MetaComponent):
    def __init__(cls, *args, **kwargs):
        super().__init__(*args, **kwargs)
        cls.SubManagers = [] # each class gets its own SubManagers list

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

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

class Manager(Component, metaclass=MetaManager): # inherit from this to get the metaclass
    pass

class Manager1(Manager):
    pass

@Manager1.RegisterSubManager
class Manager2(Manager):
    pass

@Manager2.RegisterSubManager
class Manager3(Manager):
    pass

好的,经过一些实验后,我设法提供了一个“修复”来允许类初始化,允许使用super()。在

首先,模块要“修复”初始化方法:

# ./PythonFix.py
import inspect
import types

def IsCellEmpty(cell):
    """Lets keep going, deeper !"""
    try:
        cell.cell_contents
        return False
    except ValueError:
        return True

def ClosureFix(cls, functionContainer):
    """This is where madness happens.
    I didn't want to come here. But hey, lets get mad.
    Had to do this to correct a closure problem occuring in
     Python < 3.6, joy.
    Huge thanks: https://stackoverflow.com/a/4885951/7983255
    """

    # Is the class decorated with @classmethod somehow
    isclassmethod = inspect.ismethod(functionContainer) and functionContainer.__self__ is cls
    if isclassmethod:
        function = functionContainer.__func__
    else:
        function = functionContainer

    # Get cells and prepare a cell holding ref to __class__
    ClosureCells = function.__closure__ or ()
    ClassCell_Fix = (lambda: cls).__closure__[0]

    # Shortcut
    c = function.__code__
    HasClassFreevar = '__class__' in c.co_freevars
    HasEmptyCells = any(IsCellEmpty(cell) for cell in ClosureCells)
    if HasClassFreevar and not HasEmptyCells: # No fix required.
        return classmethod(function)

    Freevars_Fixed = c.co_freevars
    Closure_Fixed = ClosureCells

    if not HasClassFreevar:
        Freevars_Fixed += ('__class__',)
        Closure_Fixed += (ClassCell_Fix,)

    elif HasEmptyCells: # This is silly, but for what I'm doing its ok.
        Closure_Fixed = tuple(ClassCell_Fix if IsCellEmpty(cell) else cell for cell in ClosureCells)

    # Now the real fun begins
    PyCode_fixedFreevars = types.CodeType(
        c.co_argcount, c.co_kwonlyargcount, c.co_nlocals,
        c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names,
        c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno,
        c.co_lnotab, Freevars_Fixed, c.co_cellvars
    )

    # Lets fetch the last closure to add our __class__ fix
    FixedFunction = types.FunctionType(
        PyCode_fixedFreevars, function.__globals__, function.__name__,
        function.__defaults__, Closure_Fixed
    )

    # Lets rewrap it so it is an actual classmethod (which it should be):
    return classmethod(FixedFunction)

现在,组件代码:

^{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__

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

如果您的类修饰符方法排除了元类,则不需要为此使用元类-更改register方法以执行上述“own”子管理器列表创建:

^{pr2}$

相关问题 更多 >