I have the following issue with class inheritance. Given class A, B, C as follows
class A(object):
...
class B(A):
...
class C(A):
...
I want a class D that can either inherit from B or from C, depending on the use case.
Right now, I have solved this issue by having a dynamic class definition:
def D(base_class, parameter1, parameter2):
class D(base_class):
...
return D(parameter1, parameter2)
Is this the proper way to do it, or is there a better way of solving this issue?
Rather than have D both create a class and return an instance, have it just return the class, which you can then use to create multiple instances as necessary.
def make_D(base_class):
class D(base_class):
...
return D
DB = make_D(B)
DC = make_D(C)
d1 = DB(...)
d2 = DC(...)
d3 = DC(...)
At this point, you should consider whether you actually need a factory function to define your subclasses, rather than simply define DB and DC directly.
Related
Say I built some classes containing some instance methods:
class A:
def func_of_A(self):
print("foo")
class B:
def func_of_B(self):
print("bar")
How can I construct an object/variable c that is an instance of both A and B, so that I can call both c.func_of_A() and c.func_of_B()?
I could of course build a new class inheriting from A and B and make c a member of that:
class C(A,B):
pass
c = C()
But that is not what I am looking for. I would rather not create a new class every time I am planning to use a combination of already built ones.
I could also create a function to dynamically define a new class and return an instance of it:
def merge(*inherit_from):
class D(*inherit_from):
pass
return D()
c = merge(A,B)
but this is beyond cursed, because now merge(A,B), merge(A) and merge(B) all return the same type <class '__main__.merge.<locals>.D'>.
There should be an intended way to do this, shouldn't?
Is there a solution that scales well with the number of classes involved? If I already have class A1, class A2, ..., class A100 and I want to construct some c to be an instance of class A2, class A23, class A72, class A99 but not the others how would I do that? Creating a new class for every combination is pretty much impossible, given the ~2^100 combinations.
You can use type() for that as #deceze mentioned
>>> class A:
... def a():
... pass
...
>>> class B:
... def b():
... pass
...
>>> def merge(name: str, *parents):
... return type(name, parents, dict())
...
>>> C = merge("C", A, B)
>>> C.a()
>>> C.b()
>>>
Suppose I have the following example:
import uuid
class A:
def __init__(self):
self.id = uuid.uuid4().hex
class B(A):
def __init__(self):
super().__init__()
self.print_id()
def print_id(self):
print(self.id)
class C(A):
def __init__(self):
super().__init__()
self.print_id()
def print_id(self):
print(self.id)
We have class B and C that are inheriting A. Once B or C are instantiated, they will print the unique ID created in the super class. The issue I am running into here is that each time B or C is instantiated, there is a brand new id created for class A. I would like to print/use the SAME ID for both class B and C. In other words, I only want to have one instance of class A to be used for B and C. Without involving any sort of logic surrounding singletons, how can I achieve this? How can class C know that class B has already initialized class A, therefore an ID already exists? I am attempting to create a base class that is initialized only once, and that ID is inherited by all the sub classes. Any help here would be appreciated.
You can use a class attribute
e.g.
class A:
static_id = uuid.uuid4().hex
def __init__(self):
pass
The rest of the code will be the same.
I changed the name of the id member, because it shadowed the name of a built-in function.
(This solution smells to me a little, but I do not understand your problem enough to suggest anything better).
Let B inherit from A. Suppose that some of B's behavior depends on the class attribute cls_x and we want to set up this dependency during construction of B objects. Since it is not a simple operation, we want to wrap it in a class method, which the constructor will call. Example:
class B(A):
cls_x = 'B'
#classmethod
def cm(cls):
return cls.cls_x
def __init__(self):
self.attr = B.cm()
Problem: cm as well as __init__ will always be doing the same things and their behavior must stay the same in each derived class. Thus, we would like to put them both in the base class and not define it in any of the derived classes. The only difference will be the caller of cm - either A or B (or any of B1, B2, each inheriting from A), whatever is being constructed. So what we'd like to have is something like this:
class A:
cls_x = 'A'
#classmethod
def cm(cls):
return cls.cls_x
def __init__(self):
self.attr = ClassOfWhateverIsInstantiated.cm() #how to do this?
class B(A):
cls_x = 'B'
I feel like it's either something very simple I'm missing about Python's inheritance mechanics or the whole issue should be handled entirely differently.
This is different than this question as I do not want to override the class method, but move its implementation to the base class entirely.
Look at it this way: Your question is essentially "How do I get the class of an instance?". The answer to that question is to use the type function:
ClassOfWhateverIsInstantiated = type(self)
But you don't even need to do that, because classmethods can be called directly through an instance:
def __init__(self):
self.attr = self.cm() # just use `self`
This works because classmethods automatically look up the class of the instance for you. From the docs:
[A classmethod] can be called either on the class (such as C.f()) or on an instance
(such as C().f()). The instance is ignored except for its class.
For ClassOfWhateverIsInstantiated you can just use self:
class A:
cls_x = 'A'
#classmethod
def cm(cls):
return cls.cls_x
def __init__(self):
self.attr = self.cm() # 'self' refers to B, if called from B
class B(A):
cls_x = 'B'
a = A()
print(a.cls_x) # = 'A'
print(A.cls_x) # = 'A'
b = B()
print(b.cls_x) # = 'B'
print(B.cls_x) # = 'B'
To understand this, just remember that class B is inheriting the methods of class A. So when __init__() is called during B's instantiation, it's called in the context of class B, to which self refers.
For example, I want class A and class B to share a common method do_something(), but inside the method, it refers to some_attr which varies based on class A or class B.
class A:
...
some_attr = 1
...
#classmethod
def do_something(cls):
....
cls.some_attr...
...
class B:
...
some_attr = 2
...
#classmethod
def do_something(cls):
....
cls.some_attr...
...
I also want to be able to easily extend the class by just changing the some_attr, without touching the do_something() method.
class C(B):
...
some_attr = 3
...
I don't want C to inherit anything from A, that's why I can't let B inherit A and then let C inherit B.
Is there a better solution than defining an abstract class to store do_something() method and set some_attr to None?
You can have an abstract or base class that holds only things you want in common, e.g.:
class Base(object):
some_attr = None
#classmethod
def do_something(cls):
cls.some_attr...
And inherit from that.
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.