Call class method as part of initialization

Current state

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

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 XY relations of data""" def __test__(initdata): assert Data_is_the_right_kind 

Problem

now the problem is how to get class references in a subclass. At first I wanted to call super (). Register () in the class body, but im could not get a link to the class itself, which I want to save in the list. A small search yielded two possible solutions, and I was wondering what the best one was.

Solution 1

Adding a call after each class definition, for example:

 class Plot_XY(PlotData): """Subclass for for plotting XY relations of data""" def __test__(initdata): assert Data_is_the_right_kind Plot_XY.register() 

It works, but it seems a very dirty decision for me - a very important part of the class structure is outside the body.

Decision 2

Another possibility might be class decorators. However, I have never used them before, and the examples I found are usually used to override / add functions to methods. ( here and here , for example). However, I am familiar with function decorators, and the following should be pretty clear on what I'm aiming for (and the dead end version works in the interpreter):

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

or at least something that works as solution 1 but looks like this:

 @some_creative_decorator_name class Plot_XY(PlotData): """Subclass for for plotting XY relations of data""" def __test__(initdata): assert Data_is_the_right_kind 

Everything seems to work just as well, but would it be like inheritance? This was one of the problems noted on the linked pages, and I dare not count on it. (I do not expect people to subclass it further, but I really do not want this to be impossible if it were necessary.)

(Of course, other solutions are also welcome.)

+5
source share
1 answer

What you do is useless, as it is already provided:

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

There is no need to keep your own subclasslist when python already provides it for you.

Note that this does not include subclasses of subclasses:

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

However, it is easy enough to find all subclasses:

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

This suggests that if you want to replicate this functionality, it’s easier to see how the default method works. And you will find that:

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

So you can see that this is a type method, which is a metaclass of object . The way to replicate correctly is to write your custom metaclass.

Basically, a metaclass is similar to a decorator approach:

  • This is more general, because you can do something before creating the class, control how it is created and do something later. The decorator receives the class object when it is completed, and can only do material after creation.
  • They are inherited, so you do not need to add anything explicit for each class, but only in the base class.

I will not go into details here. Check out What is a Metaclass in Python? for more information on metaclasses.

+6
source

Source: https://habr.com/ru/post/1247430/


All Articles