Calling class method as part of initialization

时间:2016-04-19 12:58:49

标签: python

Current Status

I have an abstract base class that, which hosts data in the form of a numpy array, knows how to work this data, and which can explain matplotlib how to draw it. To accomodate different types of data, it has a number of subclasses, like this:

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

The Issue

now, the issue is how to get the class references into the subclasslist. At first I wanted to call super().register() in the class body, but im unable to get a reference to the class itself, which is what I want to store in the list. A small search has yielded two possible solutions, and I was wondering what the best one was.

Solution 1

Adding a call after each class definition, like this:

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()

This works, but seems like a very dirty solution to me - a very important part of the class structure is located outside of the body.

Solution 2

Another possibility could be class decorators. However, I've never used them before, and the examples I've found are generally used to override/add functionality to methods. ( herehere中显示文字。我对函数装饰器很熟悉,以下内容应该大致说明我的目标(以及一个愚蠢的版本在解释器中起作用):

def some_creative_decorator_name(cls):
    cls.register()
    return cls

或者至少是像解决方案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

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

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

1 个答案:

答案 0 :(得分:6)

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

>>> class A(object):pass
... 
>>> class B(A):pass
... 
>>> class C(A): pass
... 
>>> A.__subclasses__()
[<class '__main__.B'>, <class '__main__.C'>]
>>> 

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

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

>>> class D(B):pass
... 
>>> A.__subclasses__()
[<class '__main__.B'>, <class '__main__.C'>]

然而,找到所有子类很容易:

>>> 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'>]

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

>>> '__subclasses__' in dir(object)
False
>>> '__subclasses__' in dir(type)
True

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

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

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

我不会在这里详述。有关元类的更多信息,请查看What is a metaclass in Python?