I have a class hierarchy where some methods work with a list of properties defined at the class level.
Let's say that for class A I have A.X = [propA1, propA2] and for subclass C I need C.X = [propA1, propA2, propC]. Subclasses inherit the properties from the parents, so it would make sense to write class methods with super() calls, each one using the properties of their own class.
However, it is a bit cumbersome. I can deal with all properties in a single method in the base class. So it really feels more natural to define a class variable containing an array of new properties for each subclass and manually go down the cls.__mro__ to retrieve all properties.
What I've come up with (below) seems to work relatively transparently but is it idiomatic? Is there a more typical coding pattern for this? Is there a way to avoid decorating all subclasses?
class Base(object):
pass
class InheritClassVariable:
def __init__(self, var, base):
self.var = var
self.base = base
def __call__(self, cls):
name = self.var
uname = '_' + name
bases = [B for B in cls.__mro__ if issubclass(B, self.base)]
setattr(cls, uname, getattr(cls, name, []))
value = [item for B in bases for item in getattr(B, uname, [])]
setattr(cls, name, value)
return cls
#InheritClassVariable('X', Base)
class A(Base):
X = ['propA1', 'propA2']
#InheritClassVariable('X', Base)
class B(Base):
X = ['propB']
#InheritClassVariable('X', Base)
class C(A):
X = ['propC']
#InheritClassVariable('X', Base)
class D(C,B,A):
X = ['propD']
if __name__ == "__main__":
print(f"D.X = {D.X}")
A commentator mentioned metaclasses, something I didn't know of. I looked it up and found out there is an __init_subclass__ method that is meant to avoid some of the uses of metaclasses.
That being known, I could simplify my code to (edited):
def InheritConstantArray(varname, value=[]):
"""Return a class making the 'varname' array to be inherited in subclasses"""
basename = f"InheritConstantArray{varname}"
def init_subclass(cls):
# it seems __class__ won't work here. I still don't understand
# why. All I know is eval() is dirty so I do a lookup.
allbases = cls.mro()
base = [b for b in allbases if b.__name__ == basename][0]
# collaborate with other classes using __init_subclass__().
# if we want sevaral variables to be inherited.
super(base, cls).__init_subclass__()
# base._X[cls] will store original cls.X
uvarname = f'_{varname}' if varname[0] != '_' else f'{varname}_'
if not hasattr(base, uvarname):
setattr(base, uvarname, {base: value})
stored_values = getattr(base, uvarname)
stored_values[cls] = cls.__dict__.get(varname, [])
# Then we set cls.X value from base classes
bases = [b for b in allbases if issubclass(b, base)]
values = [i for b in bases for i in stored_values.get(b, [])]
print(cls, base)
setattr(cls, varname, values)
dct = {varname: value, '__init_subclass__': init_subclass}
base = type(basename, (object,), dct)
return base
class A(InheritConstantArray('X')):
X = ['A']
class B(A):
X = ['B']
class C(A):
X = ['C']
class D(B,C,InheritConstantArray('Y')):
X = ['D']
Y = ['d']
class E(D):
X = ['E']
Y = ['e']
class F(D):
X = ['F']
Y = ['f']
class G(E,F):
X = ['G']
Y = ['g']
if __name__ == "__main__":
print(f"A.X = {A.X}")
print(f"B.X = {B.X}")
print(f"C.X = {C.X}")
print(f"D.X = {D.X} {D.Y}")
print(f"E.X = {E.X} {E.Y}")
print(f"F.X = {F.X} {F.Y}")
print(f"G.X = {G.X} {G.Y}")
I'm still unsure if it's the standard way to do it, though. (Yes, there is a very strong rationale to have class variables and multiple inheritance in my real-world problem.)
Related
I want to get all class variable names of all sub-classes in my parent class (in python). While I managed to do that I'm not certain if there is a cleaner way to achieve that.
Pseudo code to explain the problem :
class Parent:
all_names = []
var_1 = 1
def __init__(self):
print(self.all_names)
class ChildA(Parent):
var_2 = 2
class ChildB(ChildA):
var_3 = 3
p = Parent()
ca = ChildA()
cb = ChildB()
>>> ["var_1"]
>>> ["var_1","var_2"]
>>> ["var_1","var_2","var_3"]
So what I am currently doing is that I use the __new__ function to set those values recursively :
class Parent(object):
test_1 = 1
signals = []
def __new__(cls, *args, **kwargs):
# create obj ref
_obj = super().__new__(cls, *args, **kwargs)
signals = []
# walk every base and add values
def __walk(base):
for key, value in vars(base).items():
if <condition>
signals.append(value.__name__)
for base in base.__bases__:
__walk(base)
__walk(cls)
signals.reverse() # to reorder the list
_obj.signals = signals
return _obj
For more context: I am trying to develop a Signal-System but to check whether an instance has a Signal I somehow need to add them to the Root-Parent-Class. Yes I could Make all Subclasses inherit from a helper class but I don't want to do that and instead have it all bundled in as few classes as possible.
Also if there are any bugs/risks with my implementation please let me know I just recently discovered __new__.
(Python 3.x)
You could walk up the method resolution order and check for any keys in each class's __dict__ that do not appear in object.__dict__ and are not callable or private keys.
class Parent:
var_p = 1
def __init__(self):
self.blah = 0
class_vars = []
for cls in self.__class__.mro()[:-1]:
class_vars.extend([
k for k, v in cls.__dict__.items()
if k not in object.__dict__ and not k.startswith('_')
and not callable(v)
])
print(class_vars)
def test(self):
return True
class ChildA(Parent):
var_a = 2
class ChildB(ChildA):
var_b = 3
p = Parent()
ca = ChildA()
cb = ChildB()
# prints:
['var_p']
['var_a', 'var_p']
['var_b', 'var_a', 'var_p']
If you have a class hierarchy and some class attribute is overridden in child class then the user of child class should not care what was the value of this attribute in parent classes.
But this is what I want to achieve:
class A:
_ITEM = "A"
#classmethod
def getClassPath(cls):
???
class B(A):
_ITEM = "B"
class C(B):
_ITEM = "C"
# expected behavior:
A.getClassPath() # "/A"
B.getClassPath() # "/A/B"
C.getClassPath() # "/A/B/C"
My original problem is a little more complicated, but it is reduced to this: get _ITEM attributes from all the classes in hierarchy and combine them somehow.
How can I do it? There is no multiple inheritance in my case.
you can use mro (method resolution order) to access attributes in the inheritance chain. The simple case:
class A:
_ITEM = "A"
#classmethod
def get_class_path(cls):
result = cls.mro()[:-1]
result = [res._ITEM for res in reversed(result)]
result = "/" + "/".join(result)
return result
If, additionally, you want to skip classes that don't have _ITEM, you can modify slightly with vars():
class A:
_ITEM = "attr_A"
#classmethod
def get_class_path(cls):
mro_list = reversed(cls.mro()[:-1])
result = []
for obj in mro_list:
if "_ITEM" in vars(obj):
result.append(obj._ITEM)
result = "/" + "/".join(result)
return result
class B(A):
# _ITEM = "attr_B"
pass
class C(B):
_ITEM = "attr_C"
output:
B.get_class_path() # returns '/attr_A'
C.get_class_path() # returns '/attr_A/attr_C'
I am trying to dynamically create classes in Python and am relatively new to classes and class inheritance. Basically I want my final object to have different types of history depending on different needs. I have a solution but I feel there must be a better way. I dreamed up something like this.
class A:
def __init__(self):
self.history={}
def do_something():
pass
class B:
def __init__(self):
self.history=[]
def do_something_else():
pass
class C(A,B):
def __init__(self, a=False, b=False):
if a:
A.__init__(self)
elif b:
B.__init__(self)
use1 = C(a=True)
use2 = C(b=True)
You probably don't really need that, and this is probably an XY problem, but those happen regularly when you are learning a language. You should be aware that you typically don't need to build huge class hierarchies with Python like you do with some other languages. Python employs "duck typing" -- if a class has the method you want to use, just call it!
Also, by the time __init__ is called, the instance already exists. You can't (easily) change it out for a different instance at that time (though, really, anything is possible).
if you really want to be able to instantiate a class and receive what are essentially instances of completely different objects depending on what you passed to the constructor, the simple, straightforward thing to do is use a function that returns instances of different classes.
However, for completeness, you should know that classes can define a __new__ method, which gets called before __init__. This method can return an instance of the class, or an instance of a completely different class, or whatever the heck it wants. So, for example, you can do this:
class A(object):
def __init__(self):
self.history={}
def do_something(self):
print("Class A doing something", self.history)
class B(object):
def __init__(self):
self.history=[]
def do_something_else(self):
print("Class B doing something", self.history)
class C(object):
def __new__(cls, a=False, b=False):
if a:
return A()
elif b:
return B()
use1 = C(a=True)
use2 = C(b=True)
use3 = C()
use1.do_something()
use2.do_something_else()
print (use3 is None)
This works with either Python 2 or 3. With 3 it returns:
Class A doing something {}
Class B doing something []
True
I'm assuming that for some reason you can't change A and B, and you need the functionality of both.
Maybe what you need are two different classes:
class CAB(A, B):
'''uses A's __init__'''
class CBA(B, A):
'''uses B's __init__'''
use1 = CAB()
use2 = CBA()
The goal is to dynamically create a class.
I don't really recommend dynamically creating a class. You can use a function to do this, and you can easily do things like pickle the instances because they're available in the global namespace of the module:
def make_C(a=False, b=False):
if a:
return CAB()
elif b:
return CBA()
But if you insist on "dynamically creating the class"
def make_C(a=False, b=False):
if a:
return type('C', (A, B), {})()
elif b:
return type('C', (B, A), {})()
And usage either way is:
use1 = make_C(a=True)
use2 = make_C(b=True)
I was thinking about the very same thing and came up with a helper method for returning a class inheriting from the type provided as an argument.
The helper function defines and returns the class, which is inheriting from the type provided as an argument.
The solution presented itself when I was working on a named value class. I wanted a value, that could have its own name, but that could behave as a regular variable. The idea could be implemented mostly for debugging processes, I think. Here is the code:
def getValueClass(thetype):
"""Helper function for getting the `Value` class
Getting the named value class, based on `thetype`.
"""
# if thetype not in (int, float, complex): # if needed
# raise TypeError("The type is not numeric.")
class Value(thetype):
__text_signature__ = "(value, name: str = "")"
__doc__ = f"A named value of type `{thetype.__name__}`"
def __init__(self, value, name: str = ""):
"""Value(value, name) -- a named value"""
self._name = name
def __new__(cls, value, name: str = ""):
instance = super().__new__(cls, value)
return instance
def __repr__(self):
return f"{super().__repr__()}"
def __str__(self):
return f"{self._name} = {super().__str__()}"
return Value
Some examples:
IValue = getValueClass(int)
FValue = getValueClass(float)
CValue = getValueClass(complex)
iv = IValue(3, "iv")
print(f"{iv!r}")
print(iv)
print()
fv = FValue(4.5, "fv")
print(f"{fv!r}")
print(fv)
print()
cv = CValue(7 + 11j, "cv")
print(f"{cv!r}")
print(cv)
print()
print(f"{iv + fv + cv = }")
The output:
3
iv = 3
4.5
fv = 4.5
(7+11j)
cv = (7+11j)
iv + fv + cv = (14.5+11j)
When working in IDLE, the variables seem to behave as built-in types, except when printing:
>>> vi = IValue(4, "vi")
>>> vi
4
>>> print(vi)
vi = 4
>>> vf = FValue(3.5, 'vf')
>>> vf
3.5
>>> vf + vi
7.5
>>>
Imagine a case like so:
class A:
pass
class B:
x = 5
class D(A):
pass
class C(A):
pass
What I want is to find all the classes in class B that are subclasses of A:
>>> for cls in dir(B):
if issubclass(cls, A):
print(cls)
<class '__main__.C'>
<class '__main__.D'>
And it works as intended, but the problem is: I need to get them in the order they are defiend in class B definition, so instead of printing C before D, I need to get D before C. Using dir() obviously doesn't work, since it returns alphabetically sorted list.
What are my other options, if any?
EDIT:
The reason I want this is to help "players" make their own heroes/champions (for a video game) as easily as possible. So instead of having to write:
class MyHero(Hero):
def __init__(self, name='My Hero', description='My description', ...):
super().__init__(name, description, ...)
self.spells = [MySpell1(), MySpell2(), MySpell3()]
class MySpell1(Spell):
def __init__(...):
...
They could just write:
class MyHero(Hero):
name = 'My Hero'
description = 'My description'
...
class MySpell1(Spell):
name = ...
class MySpell2(Spell):
...
Obviously the second one looks much better than the first, and even more to a person who doesn't know much of Python.
The metaclass documentation includes a nice example of how to get a class to remember what order its members were defined in:
class OrderedClass(type):
#classmethod
def __prepare__(metacls, name, bases, **kwds):
return collections.OrderedDict()
def __new__(cls, name, bases, namespace, **kwds):
result = type.__new__(cls, name, bases, dict(namespace))
result.members = tuple(namespace)
return result
class A(metaclass=OrderedClass):
def one(self): pass
def two(self): pass
def three(self): pass
def four(self): pass
>>> A.members
('__module__', 'one', 'two', 'three', 'four')
You can adapt this to your case like this:
class A:
pass
class B(metaclass=OrderedClass):
x = 5
class D(A):
pass
class C(A):
pass
print(filter(lambda x: isinstance(getattr(B, x), type), b.members)))
gives:
['D', 'C']
Note that this gives you the names of the classes; if you want the classes themselves, you can do this instead:
print(list(filter(lambda x: isinstance(x, type), (getattr(B, x) for x in B.members))))
May be something like that can be helpful:
import inspect
class Spell(object):
name = "Abstract spell"
class MyHero(object):
name = "BATMAN"
description = "the bat man"
class MySpell1(Spell):
name = "Fly"
class MySpell2(Spell):
name = "Sleep"
for k, v in MyHero.__dict__.iteritems():
if inspect.isclass(v) and issubclass(v, Spell):
print "%s cast the spell %s" % (MyHero.name, v.name)
UPDATE:
Another way to iterate by class attributes is:
for attr_name in dir(MyHero):
attr = getattr(MyHero, attr_name)
if inspect.isclass(attr) and issubclass(attr, Spell):
print "%s cast the spell %s" % (MyHero.name, attr.name)
P.S. Python class is also object
I've got a base class where I want to handle __add__() and want to support when __add__ing two subclass instances - that is have the methods of both subclasses in the resulting instance.
import copy
class Base(dict):
def __init__(self, **data):
self.update(data)
def __add__(self, other):
result = copy.deepcopy(self)
result.update(other)
# how do I now join the methods?
return result
class A(Base):
def a(self):
print "test a"
class B(Base):
def b(self):
print "test b"
if __name__ == '__main__':
a = A(a=1, b=2)
b = B(c=1)
c = a + b
c.b() # should work
c.a() # should work
Edit: To be more specific: I've got a class Hosts that holds a dict(host01=.., host02=..) (hence the subclassing of dict) - this offers some base methods such as run_ssh_commmand_on_all_hosts()
Now I've got a subclass HostsLoadbalancer that holds some special methods such as drain(), and I've got a class HostsNagios that holds some nagios-specific methods.
What I'm doing then, is something like:
nagios_hosts = nagios.gethosts()
lb_hosts = loadbalancer.gethosts()
hosts = nagios_hosts + lb_hosts
hosts.run_ssh_command_on_all_hosts('uname')
hosts.drain() # method of HostsLoadbalancer - drains just the loadbalancer-hosts
hosts.acknoledge_downtime() # method of NagiosHosts - does this just for the nagios hosts, is overlapping
What is the best solution for this problem?
I think I can somehow "copy all methods" - like this:
for x in dir(other):
setattr(self, x, getattr(other, x))
Am I on the right track? Or should I use Abstract Base Classes?
In general this is a bad idea. You're trying to inject methods into a type. That being said, you can certainly do this in python, but you'll have to realize that you want to create a new type each time you do this. Here's an example:
import copy
class Base(dict):
global_class_cache = {}
def __init__(self, **data):
self.local_data = data
def __add__(self, other):
new_instance = self._new_type((type(self), type(other)))()
new_instance.update(copy.deepcopy(self).__dict__)
new_instance.update(copy.deepcopy(other).__dict__)
return new_instance
def _new_type(self, parents):
parents = tuple(parents)
if parents not in Base.global_class_cache:
name = '_'.join(cls.__name__ for cls in parents)
Base.global_class_cache[parents] = type(name, parents, {})
return Base.global_class_cache[parents]
class A(Base):
def a(self):
print "test a"
class B(Base):
def b(self):
print "test b"
if __name__ == '__main__':
a = A(a=1, b=2)
b = B(c=1)
c = a + b
c.b() # should work
c.a() # should work
print c.__class__.__name__
UPDATE
I've updated the example to remove manually moving the methods -- we're using mixins here.
It is difficult to answer your question without more information. If Base is supposed to be a common interface to all classes, then you could use simple inheritance to implement the common behavior while preserving the methods of the subclasses. For instance, imagine that you need a Base class where all the objects have a say_hola() method, but subclasses can have arbitrary additional methods in addition to say_hola():
class Base(object):
def say_hola(self):
print "hola"
class C1(Base):
def add(self, a, b):
return a+b
class C2(Base):
def say_bonjour(self):
return 'bon jour'
This way all instances of C1 and C2 have say_hola() in addition to their specific methods.
A more general pattern is to create a Mixin. From Wikipedia:
In object-oriented programming
languages, a mixin is a class that
provides a certain functionality to be
inherited by a subclass, while not
meant for instantiation (the
generation of objects of that class).
Inheriting from a mixin is not a form
of specialization but is rather a
means of collecting functionality. A
class may inherit most or all of its
functionality from one or more mixins
through multiple inheritance.