调用类方法作为初始化的一部分

Glo*_*eye 5 python

当前状态

我有一个抽象的基类,它以numpy数组的形式托管数据,知道如何处理这些数据,哪些可以解释matplotlib如何绘制它.为了容纳不同类型的数据,它有许多子类,如下所示:

class PlotData():
    """Base Class"""
    subclasslist = []

    @classmethod
    def register(cls):
        super().subclasslist.append(cls)

    def __new__(self, initdata, *args, **kwargs):
        for subclass in subclasslist:
            try:
                subclass.__test__(initdata)
            except AssertionError:
                continue
            else:
                break
        else:
            raise TypeError("Initdata does not fit any known subclass")
        return subclass(initdata, *args, **kwargs)

class Plot3D(PlotData):
    """Subclass for 3d-plotting data"""
    def __test__(initdata):
        assert Data_is_the_right_kind

class Plot_XY(PlotData):
    """Subclass for for plotting X-Y relations of data"""
    def __test__(initdata):
        assert Data_is_the_right_kind
Run Code Online (Sandbox Code Playgroud)

问题

现在,问题是如何将类引用引入子类列表.起初我想在类体中调用super().register(),但我无法获得对类本身的引用,这就是我想要存储在列表中的内容.一个小小的搜索已经产生了两种可能的解决方案,我想知道最好的解决方案是什么.

解决方案1

在每个类定义后添加一个调用,如下所示:

class Plot_XY(PlotData):
    """Subclass for for plotting X-Y relations of data"""
    def __test__(initdata):
        assert Data_is_the_right_kind
Plot_XY.register()
Run Code Online (Sandbox Code Playgroud)

这有效,但对我来说似乎是一个非常肮脏的解决方案 - 类结构的一个非常重要的部分位于身体外部.

解决方案2

另一种可能是类装饰器.但是,我之前从未使用它们,我发现的示例通常用于覆盖/添加方法的功能.(例如,这里这里).我熟悉函数装饰器,以下内容应该大致清楚我的目标是什么(以及在解释器中使用的一个愚蠢的版本):

def some_creative_decorator_name(cls):
    cls.register()
    return cls
Run Code Online (Sandbox Code Playgroud)

或者至少,像解决方案1一样起作用的东西,但看起来像:

@some_creative_decorator_name
class Plot_XY(PlotData):
    """Subclass for for plotting X-Y relations of data"""
    def __test__(initdata):
        assert Data_is_the_right_kind
Run Code Online (Sandbox Code Playgroud)

它似乎同样有效,但这会搞砸继承这样的东西吗?这是链接页面中提到的问题之一,我真的不敢指望它.(我不希望人们进一步继承它,但如果需要,我真的不想让它变得不可能.)

(当然也欢迎其他解决方案.)

Bak*_*riu 6

你正在做的事情是无用的,因为它已经提供:

>>> class A(object):pass
... 
>>> class B(A):pass
... 
>>> class C(A): pass
... 
>>> A.__subclasses__()
[<class '__main__.B'>, <class '__main__.C'>]
>>> 
Run Code Online (Sandbox Code Playgroud)

subclasslist当python已经为你提供一个时,没有必要保持自己的.

请注意,这不包括子类的子类:

>>> class D(B):pass
... 
>>> A.__subclasses__()
[<class '__main__.B'>, <class '__main__.C'>]
Run Code Online (Sandbox Code Playgroud)

但是,找到所有子类很容易:

>>> def all_subclasses(klass):
...     for sub in klass.__subclasses__():
...         yield sub
...         yield from all_subclasses(sub)
... 
>>> list(all_subclasses(A))
[<class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>]
Run Code Online (Sandbox Code Playgroud)

这就是说,如果要复制此功能,则更容易查看默认方法的工作原理.你会发现:

>>> '__subclasses__' in dir(object)
False
>>> '__subclasses__' in dir(type)
True
Run Code Online (Sandbox Code Playgroud)

所以在这里你可以看到,它是一个方法,type它是元类object.正确复制它的方法是编写自定义元类.

基本上,元类与装饰器方法类似:

  • 它更通用,因为您可以在创建类之前执行操作,控制它的创建方式并在之后执行某些操作.装饰器在完成时接收类对象,并且只能进行创建后的内容
  • 它们是继承的,因此您不必为每个类添加任何显式内容,只能添加到基类中.

我不会在这里详述.看看Python中的元类是什么? 有关元类的更多信息.