Python inheritance: pass all arguments from base to super class - python

I am not quite used to class inheritance in Python yet. All I want to do is simply pass all arguments from my base class to the super class when it is created:
class A:
def __init__(self, a, b):
self.a = a
self.b = b
def do(self):
c = self.a + self.b
return B(c=c)
class B(A):
def __init__(self, c):
self.c = c
my_A = A(a=1, b=2)
my_B = my_A.do()
print(my_B.c)
This works as expected. However, what I want is to also be able to call the arguments a and b from the x2 instance of the class my_B, so that I can directly write my_B.a for instance. I know this is done with super() like this:
class A:
def __init__(self, a, b):
self.a = a
self.b = b
def do(self):
c = self.a + self.b
return B(a=self.a, b=self.b, c=c)
class B(A):
def __init__(self, a, b, c):
super(B, self).__init__(a=a, b=b)
self.c = c
my_A = A(a=1, b=2)
my_B = my_A.do()
print(my_B.a)
print(my_B.b)
However, I don't want to explicitly write all arguments of A when I create the instance of B. Is there a way to automatically pass all arguments from class A to class B?

Based on your comment, you could do something like this:
class B(A):
def __init__(self, c, an_a):
super(B, self).__init__(an_a.a, an_a.b)
self.c = c
You may instead prefer to keep your current constructor and add a from_a static method:
class B(A):
def __init__(self, c, a, b): # note order
super(B, self).__init__(a=a, b=b)
self.c = c
#staticmethod
def from_a(c, an_a):
return B(c, an_a.a, an_a.b)
Finally, if you don't want to type out all of those parameters, you can add an args() method to A and then use the collection unpacking function syntax:
class A:
...
def args(self):
return (self.a, self.b)
class B(A):
def __init__(self, c, *args): # Note *args
super(B, self).__init__(*args)
self.c = c
#staticmethod
def from_a(c, an_a):
return B(c, *an_a.args())
Now B's constructor takes the parameter special to B, followed by any number of parameters which just get passed to A's constructor. This allows you to do the tuple unpacking when calling the constructor, instead of listing everything out manually.

Ok, thanks for your comments. I have come up with this solution:
class A:
def __init__(self, a, b):
self.a = a
self.b = b
def do(self):
c = self.a + self.b
return B(self, c)
class B:
def __init__(self, base, c):
self.base = base
self.c = c
my_A = A(a=1, b=2)
my_B = my_A.do()
print(my_B.base.a)
print(my_B.base.b)
print(my_B.c)
This removes the inheritance of class B and makes the code slightly less readable, but I guess it will do, right? 😊

yup there is a way use key word arguments so :
class A(object):
def __init__(self,**kwargs):
# Non pythonic and a bit of a hack
self.kwargs = kwargs
vars(self).update(kwargs)
def do(self):
c = self.a + self.b
return B(c=c, **kwargs)
class B(A):
def __init__(self, c, **kwargs):
self.c = c
super(B, self).__init__(**kwargs)
my_A = A(a=1, b=2)
my_B = my_A.do()
print(my_B.a)
print(my_B.b)
print(my_B.c)
This does what you are after nonethless the way in which it was written before was a bit more pythonic when run this should output:
1
2
3
The downside of doing this is that now A has not limit in terms of the number of attributes but you could ensure this with an assertion or something I guess.

Related

How to define a variable of a class as class object in Python

I need to define a variable of a class a class object. How can I do it?
if for example I have a class like this :
class A:
def __init__(self, a, b):
self.a = a
self.b = b
and I want to create another class B that have a variable as instance of class A like :
class B:
def __init__(self, c = A(), d):
self.c = c
self.d = d
How can I do it ? I need to do particular operation or simply declarate c as object of class A when I create the object of class B ?
class B:
def __init__(self, a, b, d):
self.c = A(a, b)
self.d = d
or
class B:
def __init__(self, c, d):
self.c = c
self.d = d
or
class B:
def __init__(self, d):
self.c = A(a, b) # a and b can be values
self.d = d
What you wrote mostly works:
def __init__(self, c = A(), d):
self.c = c
But there's a "gotcha" which you really want to avoid.
The A constructor will be evaluated just once,
at def time, rather than each time you construct
a new B object.
That's typically not what a novice coder wants.
That signature mentions a mutable default arg,
something it's usually best to avoid,
if only to save future maintainers from
doing some frustrating debugging.
https://dollardhingra.com/blog/python-mutable-default-arguments/
https://towardsdatascience.com/python-pitfall-mutable-default-arguments-9385e8265422
Instead, phrase it this way:
class B:
def __init__(self, c = None, d):
self.c = A(1, 2) if c is None else c
...
That way the A constructor will be evaluated afresh each time.
(Also, it would be good to supply both of A's mandatory arguments.)

Default arguments in a child class and an inheritance (Python 3.10)

I want a child class to inherit from a parent class all methods and attributes with one small change - setting a default value for one argument in the child class shared with the parent class. How can I do it? With following code, I get an AttributeError when trying to call add method on the child class' instance.
https://pastebin.com/WFxmbyZD
def ParentClass():
"""An exemplary parent class."""
def __init__(self, a, b):
self.a = a
self.b = b
def adder(self):
return a + b
def ChildClass(ParentClass):
"""An exemplary child class."""
def __init__(self, a, b=3):
super().__init__(a, b)
child_class_instance = ChildClass(5)
print(child_class_instance.adder())
You have a mistake declaring the class.
I will show you examples here. The child class can have a different signature for the constructor and the methods.
class ParentClass:
"""An exemplary parent class."""
def __init__(self, a, b):
self.a = a
self.b = b
def adder(self):
return self.a + self.b
def adder_bis(self, c):
return self.a + self.b + c
class ChildClass(ParentClass):
"""An exemplary child class."""
def __init__(self, a, b=3):
super().__init__(a, b)
def adder_bis(self):
return super().adder_bis(self.a)
child_class_instance = ChildClass(5)
print(child_class_instance.adder())
print(child_class_instance.adder_bis())
I commented what I thought was wrong:
class ParentClass(): #class instead of def
"""An exemplary parent class."""
def __init__(self, a, b):
self.a = a
self.b = b
def adder(self):
return self.a + self.b #use self here
class ChildClass(ParentClass): #class instead of def
"""An exemplary child class."""
def __init__(self, a, b=3):
super().__init__(a, b)
child_class_instance = ChildClass(5)
print(child_class_instance.adder())
output:
8

Python encapsulate data for a class

I have two python classes, A and B that inherits from A.
At runtime, I only have one instance of class A, but many instances of class B.
class A:
def __init__(self, a):
self.a = a
def _init2 (self, AA)
self.a = AA.a
class B(A):
def __init__(self, AA, b):
super()._init2(AA)
self.b = b
AA = A(0)
BB = B(AA, 1)
Is this the good way of writing it ? It seems ugly ...
It would probably be better to remove init2 and only use __init__. Having both is confusing and unnatural.
class A:
def __init__(self, obj):
# I believe that this is what you tried to achieve
if isinstance(obj, type(self)):
self.a = obj.a
else:
self.a = obj
class B(A):
def __init__(self, A, b):
super().__init__(A)
self.b = b
On a side note, there are too many things called A here. The A in def __init__(self, A, b): is most probably not referring to the A that you expect.

Python Inheritance: what is the difference?

Given a parent class 'A'
Class A(object):
def __init__(self,a,b):
self.a = a
self.b = b
What is the difference between making a subclass 'B' among the below options
Option 1
Class B(A):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
Option 2
Class B(A):
def __init__(self,a,b,c):
A.__init__(self, a, b)
self.c = c
In this case, none. But what if A.__init__ did loads of complex logic? You don't want to have to duplicate all that in B.
An enhancement on Option 2 is to use the super() function:
class B(A):
def __init_(self,a,b,c):
super(B, self).__init__(a, b)
self.c = c
Your first option initializes members of class A (a and b) as if it was in class B.
The second option uses constructor of A to initialize members of A before initializing members of B.
A better approach to design classes in Python would be
class A(object):
def __init__(self, a, b):
self._a = a
self._b = b
#property
def a(self):
return self._a
#property
def b(self):
return self._b
class B(A):
def __init__(self, a, b, c):
super(B, self).__init__(a, b)
self._c = c
#property
def c(self):
return self._c
The _ in member names mentions that the members should not be accessed directly. The decorator #property provides direct accessors for the members.
Note that members are read only. This class have no setters specified. For example, setter for c can be declared as follows
#c.setter
def c(self, c):
self._c = c

Inheritance inside Classes Python3

I need something like this
class Parent(object):
class Base(object):
def __init__(self, a, b):
self.a = a
self.b = b
class Derived(Base):
def __init__(self, a, b, c):
super(Derived,self).__init__(a, b)
self.c = c
def doit():
pass
parent = Parent()
derived = parent.Derived(x,y,z)
derived.doit()
When I try to run this, i get this following error: NameError: name 'Derived' is not defined
I tried with 'Base' in the place of 'Derived' in super() - didn't help
Class inheritance does not change the parent class. In this case your Parent class only contains the original Base class and not the derived class.
You can simply use monkey-patching to solve this problem,
class Parent(object):
pass
class Base(object):
def __init__(self, a, b):
self.a = a
self.b = b
class Derived(Base):
def __init__(self, a, b, c):
super(Derived,self).__init__(a, b)
self.c = c
def doit(self):
pass
Parent.Derived = Derived
parent = Parent()
x, y , z = 1, 1, 1
derived = parent.Derived(x,y,z)
derived.doit()
Prefixing 'Derived' with 'Parent.', made it. As I already have commented on the question. This is just for experimenting with the 'Derived' class. But I am still wondering how the, 'class Derived(Base):' is fine (without 'Parent.' prefix for 'Base' class)
class Parent(object):
class Base(object):
def __init__(self, a, b):
self.a = a
self.b = b
class Derived(Base):
def __init__(self, a, b, c):
super(Parent.Derived,self).__init__(a, b)
self.c = c
def doit():
pass
parent = Parent()
derived = parent.Derived(x,y,z)
derived.doit()

Categories

Resources