So let's say I have this code structure:
class Parent:
def __init__(self, a, b, c):
self.a = a,
self.b = b
self.c = c
#classmethod
def from_string(cls, some_string):
if some_string == 'duh':
return cls(a=5, b=6, c='banana')
else:
return cls(a=0, b=0, c='chicken')
def method_1(self):
#do something
def method_2(self):
#do something else
class FirstChild(Parent):
def __init__(self, a, b, c):
super().__init__(a, b, c)
def child_specific_method(self):
#do something
class SecondChild(Parent):
def __init__(self, a, b):
super().__init__(a, b)
def some_other_method(self):
#do stuff
My thinking was that I want both subclasses to have access to methods of the Parent class, but also extend its functionality. At the same time I want the Parent class to instantiate with different parameters based on the class method.
Now I'm confused as to how I would create instances of child classes? Or, more precisely, how would I create child instances when there can be different versions of the parent class?
Related
I'm coding in Python. I have a Base class which contains several methods. There are some child classes; they may have their own methods. I want the Base class constructor to create the object not of the class itself but the object of one of the child classes depending on the argument.
For example, guess our child classes are Point, Line, and Plane, all are inherited from Base, and the difference between them is set by the dim attribute of the Base class.
class Base():
def __init__(self, dim, a, b):
self.dim = dim
self.a = a
self.b = b
class Point(Base):
def __init__(self, a, b):
super().__init__(1, a, b)
class Line(Base):
def __init__(self, a, b):
super().__init__(2, a, b)
class Plane(Base):
def __init__(self, a, b):
super().__init__(3, a, b)
If I explicitly create a Point, the object type will be Point:
pointA = Point(0, 0)
type(pointA) # __main__.Point
But if I do the same through the Base constructor, the object will be of class Base:
pointB = Base(1, 0, 0)
type(pointB) # __main__.Base
So I'd like to change this behavior and make the Base constructor return a Point, Line or Plane object if the dim attribute is equal to 1, 2 or 3 respectively. How can I do this?
EDIT:
Based on this thread (Improper use of __new__ to generate class instances?) I overrid the Base.__new__() and got the following code:
class Base():
def __new__(cls, a, b, dim):
if dim == 1:
return object.__new__(Point)
elif dim == 2:
return object.__new__(Line)
elif dim == 3:
return object.__new__(Plane)
class Point(Base):
def __init__(self, a, b, dim):
self.a = a
self.b = b
class Line(Base):
def __init__(self, a, b, dim):
self.a = a
self.b = b
class Plane(Base):
def __init__(self, a, b, dim):
self.a = a
self.b = b
The code above works, but it requires an explicit setting of the dim argument even when I create a new Point instance. A non-identical set of arguments in Base.__new__() and Point.__init__() raises an error. How can I keep this behavior but remove dim from the Point constructor?
You don't typically want to do this by instantiating a base case. While I suppose you could override __new__, I wouldn't advise it for this. Notably, though, __init__ has a None return type. So, regardless, you can't do this in __init__ - the object is already created at that point.
Instead, what you probably want is a static so-called 'Factory' method on your base class:
from typing import Type
class Base():
#staticmethod
def create(dim, a, b) -> Type[Base]:
# Decide which subclass you want to create, instantiate and return it.
...
new_obj = Base.create(x, y, z)
Python solves the diamond problem well if there are no fields in the classes by linearizing the method resolution order. However, if the classes have fields then how do you call the super constructors? Consider:
class A:
def __init__(self, a):
self.a = a # Should only be initialized once.
class B(A):
def __init__(self, a, b):
super().__init__(a)
self.b = b
class C(A):
def __init__(self, a, c, b=None):
super().__init__(a)
self.c = c
class D(C, B):
def __init__(self, a, b, c):
super().??? # What do you put in here.
For my use case I do actually have a solution, because b can't be None in the application and therefore the following largely works:
class A:
def __init__(self, a):
self.a = a # Should only be initialized once.
class B(A):
def __init__(self, a, b):
assert b is not None # Special case of `b` can't be `None`.
super().__init__(a)
self.b = b
class C(A):
def __init__(self, a, c, b=None): # Special init with default sentinel `b`.
if b is None:
super().__init__(a) # Normally `C`'s super is `A`.
else:
super().__init__(a, b) # From `D` though, `C`'s super is `B`.
self.c = c
class D(C, B): # Note order, `C`'s init is super init.
def __init__(self, a, b, c):
super().__init__(a, c, b)
def main():
A('a')
B('b', 1)
C('c', 2)
D('d', 3, 4)
C('c2', 5, 6) # TypeError: __init__() takes 2 positional arguments but 3 were given
This largely works for the special case of b can't be None, however it still has a problem if C's __init__ is called directly (see last line of above). Also you have to modify C for the multiple inheritance and you have to inherit in the order C, B.
==== Edit ===
Another possibility is to manually initialize each field (this is somewhat similar to how Scala handles fields under the covers).
class A0:
def __init__(self, a): # Special separate init of `a`.
self._init_a(a)
def _init_a(self, a):
self.a = a
class B0(A0):
def __init__(self, a, b): # Special separate init of `b`.
self._init_a(a)
self._init_b(b)
def _init_b(self, b):
self.b = b
class C0(A0):
def __init__(self, a, c): # Special separate init of `c`.
self._init_a(a)
self._init_c(c)
def _init_c(self, c):
self.c = c
class D0(C0, B0):
def __init__(self, a, b, c): # Uses special separate inits of `a`, `b`, and `c`.
self._init_a(a)
self._init_b(b)
self._init_c(c)
The disadvantage of this approach is that it is very non-standard, to the extent that PyCharm gives a warning about not calling super init.
==== End edit ===
Is there a better way?
Thanks in advance for any help, Howard.
Let's say I have parent class P
class P(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
If a create another class C which is a child class of P, is it possible to inherit SOME parameters of P but not all (Let's say I just want parameters a,c from class P to be passed on to C). I know it's a weird question, and I don't know if there's an application to this, but I just can't seem to find an answer. Thank you in advance!
The only good way to do it as far as I know is to do something like the following:
class P:
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
class C(P):
def __init__(self, a, b, c):
super(C, self).__init__(a, b, c)
self._b = b
Essentially, you call the superclass constructor to define all of the values, but then override the value (in this case, for self._b) later in the child class constructor.
Alternatively, if you don't want self._b to even be a thing in the class C, you can also do del self._b after the super().__init__() call to remove it altogether.
In general, though, it seems like bad practice to have a child class not have some of the fields that the parent class has, since other parts of the code may rely on that field being there.
Let's say I have a class called Adder:
class adder(object):
def __init__(self, a, b):
self.a=a
self.b=b
self.result = None
def perform_addition(self):
self.result = self.a + self.b
return self.result
If I instantiate this class:
myAdder = adder(1,2)
Then the value of myAdder.result depends on calling perform_addition() first, otherwise it'll always remain None. It other words, there's a dependency on perform_addition() for the value of self.result. And if we extrapolate, a more complex class can have a chain of dependencies: ie, you have to call functions A, B, and C before D, because they in turn populate the necessary variables that the next function needs.
Is this bad class design? What is the remedy for it?
I think the above is a example of: https://en.wikipedia.org/wiki/Sequential_coupling
I think it all depends on what you want to do and how you want to go about it. the code you have is not necessarily bad, if you want a static dependency on 'perform_addition()' for the value of 'self.result' . But if you want a dynamic dependency, then the code below will be a good and simple approach. this way when an object is created by instantiating the class with values 'a' and 'b', 'self.result' will be automatically computed. you could also use more advanced tools like properties, decorators, descriptors etc. like i said, it all depends on what you want.
Class adder(object):
def __init__(self, a, b):
self.a=a
self.b=b
self.result = self.perform_addition()
def perform_addition(self):
self.result = self.a + self.b
return self.result
This would be a good case to make result a property instead, so that the addition is only performed when the result attribute is accessed:
class adder(object):
def __init__(self, a, b):
self.a = a
self.b = b
#property
def result(self):
return self.a + self.b
myAdder = adder(1,2)
print(myAdder.result)
This outputs: 3
In case the result attribute is expected to be accessed multiple times and that the calculation involved is expensive, you can save the result in an instance variable to avoid re-calculations:
class adder(object):
def __init__(self, a, b):
self.a = a
self.b = b
self._result = None
#property
def result(self):
if self._result is None:
self._result = self.a + self.b
return self._result
I have a class structure like
class A:
def method1(self):
return 1
class B:
def method2(self):
return 2
class C(A,B):
def method3(self):
return self.method1()+self.method2()
The Classes A and B provide functionality which class C gathers and enriches with some wrappers. Now it happens that I need a similar class C2 which is derived from a different implementation A2 instead of A: class C2(A2, B) with the same method3.
How would you realize such a design (C can derive from different implementation classes)?
If it has to do with advanced Python programming (meta-classes?) an example would be appreciated :)
class CMixin:
def method3(self):
return self.method1()+self.method2()
class C1(A,B, CMixin):
pass
class C2(A2,B, CMixin):
pass
Though the design you describe sounds like you want aggregation instead:
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def method3(self):
return self.a.method1() + self.b.method2()
c1 = C(a1, b)
c2 = C(a2, b)