Your design looks like you are making mixins. Here is a class factory toy example that can produces the mixins dynamically.
figures.py
data = ((0, 1),(0, 3),(0, 4),(2, 1),(2, 3),(2, 7),(6, 3))
class FT:
@staticmethod
def ge():
return data
class FT1:
@staticmethod
def ge():
return [(x*x,y*y) for x,y in data]
def compose(ftype):
'''Returns a class composed of F and ftype.'''
return type(f'F_{ftype.__name__}',(F,ftype),{})
some_module.py:
import importlib
class F:
def __init__(self):
self.x = 'foo'
def a(self):
s = '|'.join(f'{thing}' for thing in self.ge())
return s
def b(self):
return 'baz'
def compose(ftype):
cls = getattr(importlib.import_module('figures'),ftype)
return type(f'F_{ftype}',(F,cls),{})
z = compose('FT')()
y = compose('FT1')()
Both objects have the same b methods.
>>> z.b(), y.b()
('baz', 'baz')
Both have the same a methods using data from specific ge methods
>>> print(z.a())
(0, 1)|(0, 3)|(0, 4)|(2, 1)|(2, 3)|(2, 7)|(6, 3)
>>> y.a()
'(0, 1)|(0, 9)|(0, 16)|(4, 1)|(4, 9)|(4, 49)|(36, 9)'
Each object has specific ge methods
>>> z.ge()
((0, 1), (0, 3), (0, 4), (2, 1), (2, 3), (2, 7), (6, 3))
>>> y.ge()
[(0, 1), (0, 9), (0, 16), (4, 1), (4, 9), (4, 49), (36, 9)]
>>>
z and y are instances of different classes.
>>> z.__class__, y.__class__
(<class '__main__.F_FT'>, <class '__main__.F_FT1'>)
>>>
Multiple instances of a composed class.
>>> One = compose(FT)
>>> q,r,s = One(),One(),One()
>>> q,r,s
(<__main__.F_FT object at 0x0000000003163860>,
<__main__.F_FT object at 0x000000000D334198>,
<__main__.F_FT object at 0x000000000D334828>)
>>>
If everything is in the same module then compose becomes
def compose(ftype):
return type(f'F_{ftype.__name__}',(F,ftype),{})
z = compose(FT)
y = compose(FT1)
What is the difference between a mixin and inheritance?
Similar solution using an abstract base class. - G cannot be instantiated unless
ge is overridden.
import importlib
import abc
class G(abc.ABC):
def __init__(self):
self.x = 'foo'
def a(self):
s = '|'.join(f'{thing}' for thing in self.ge())
return s
def b(self):
return 'baz'
@staticmethod
@abc.abstractmethod
def ge():
pass
# either of these work
def new(ftype):
cls = getattr(importlib.import_module('figures'),ftype)
return type(cls.__name__,(cls,G),{})
#def new(ftype):
# cls = getattr(importlib.import_module('figures'),ftype)
# return type(cls.__name__,(G,),{'ge':staticmethod(cls.ge)})
#usage
# A = new('FT')
Similar except the specific static methods are just plain functions and using the ABC from above
def FT_ge():
return ((0, 1),(0, 3),(0, 4),(2, 1),(2, 3),(2, 7),(6, 3))
def other_new(f):
return type(f.__name__.split('_')[0],(G,),{'ge':staticmethod(f)})
# usage
# B = other_new(FT_ge)
Figurehave methods common to allfigure_type's and eachfigure_typehas specific edges and vertices methods? Do the objects need to beFigureinstances for some reason?Figurecontains methods applied to eachfigure_type.figure_typecontains only methods which returnssets with edges, vertices or surfaces to draw them inOpenGLinFigureclass. Why have I chosen it? Well, it seems to have good readability for me and maybe in future I can add some specific methods to each figure, but honestly speaking I'm beginner, so probably I could do better design. At the moment I try to follow given links in answers, but those concepts seems to me a little bit advanced.execand useimportlibinstead. Using Factory pattern might be easier than metaclasses as stated by hspandher (but metclasses are more sophisticated and fun to play with).figure.cubeandfigure.whateverdo anything useful, and complicates precisely what you are trying to do.