Dynamic subclass in Python

I have a number of atomic classes (Components / Mixins, not quite sure what to call them) in the library that I am developing that are designed to subclass applications. This atomicity was created in such a way that applications can only use the functions that they need and combine components through multiple inheritance.

However, sometimes this atomicity cannot be ensured, because some component may depend on another. For example, imagine that I have a component that gives a graphical representation of an object, and another component that uses this graphical representation to perform some kind of collision check. The former is purely atomic, but the latter requires that the current object already subclasses this component of the graphical representation so that its methods are accessible to it. This is a problem because we need to somehow tell the users of this library that in order to use a particular component, they must also subclass this other. We could make this subcomponent of the collision component a visual component, but if the user also subclasses this visual component, it will not work because the class is not at the same level (as opposed to the simple diamond ratio, which is desirable) and give critical meta errors class that is difficult for a programmer to understand.

So I would like to know if there is any cool way, perhaps redefining a metaclass or using class decorators to mark these non-atomic components, and when they are subclassed, an additional dependency will be introduced into the current object, if it is not already there. Example:

class AtomicComponent(object): pass @depends(AtomicComponent) # <- something like this? class UnAtomicComponent(object): pass class UserClass(UnAtomicComponent): #automatically includes AtomicComponent pass class UserClass2(AtomicComponent, UnAtomicComponent): #also works without problem pass 

Can someone tell me how I can do this? or if possible ...

edit: Since it is debatable that a meta-class solution is the best choice, I will leave it unacceptable for 2 days.

Other solutions may be to improve error messages, for example, running something like UserClass2 will result in an error stating that UnAtomicComponent is already extending this component. This, however, creates a problem that it is not possible to use two UnAtomicComponents, given that they will subclass the object at different levels.

+4
source share
1 answer

"Metaclasses"

That's what they are for! When creating a class, the class parameters go through a metaclass, where you can check the databases and then change, for example.

This is done without errors - although it does not preserve the order of the required classes marked by the decorator "depends":

 class AutoSubclass(type): def __new__(metacls, name, bases, dct): new_bases = set() for base in bases: if hasattr(base, "_depends"): for dependence in base._depends: if not dependence in bases: new_bases.add(dependence) bases = bases + tuple(new_bases) return type.__new__(metacls, name, bases, dct) __metaclass__ = AutoSubclass def depends(*args): def decorator(cls): cls._depends = args return cls return decorator class AtomicComponent: pass @depends(AtomicComponent) # <- something like this? class UnAtomicComponent: pass class UserClass(UnAtomicComponent): #automatically includes AtomicComponent pass class UserClass2(AtomicComponent, UnAtomicComponent): #also works without problem pass 

(I removed the inheritance from the "object" since I declared the __metaclass__ global variable. All classes will still be a new style class and have this metaclass. Inheriting from an object or another class overrides the __metaclass__ global variable and the class level __metclass__ must be declared)

- change -

Without metaclasses, the way is that your classes inherit their dependencies correctly. Tei will no longer be so "atomic", but since they cannot work as atomic, it may not matter.

In the following example, classes C and D will be your user classes:

 >>> class A(object): pass ... >>> class B(A, object): pass ... >>> >>> class C(B): pass ... >>> class D(B,A): pass ... >>> 
+3
source

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


All Articles