python - accessing superclass attributes - python

Hi I want to achieve the following in python, however I cant figure out what to replace the line super.a = b with:
class Super:
def __init__(self):
self.a = 1
class Sub(Super):
def method(self, b):
super.a = b

An Sub is a Super, i.e. all instances of Sub can be treated exactly like instances of Super. In your case, that means you simply set self.a = b.

Related

Am I able to call a submethod of a class's attribute from that class using the class as an attribute?

I am very sorry for the confusing title, I did not know how else to phrase the question.
Let's say I have a class, A. It is described as shown:
class A:
def __init__(self, argument):
self.value = argument
def submethod(self, argumentThatWillBeAClass):
print(dir(argumentThatWillBeAClass))
And then I initialize it as shown below:
classAInstance = A('42.0')
Now, I have a class, B. Let's add a submethod that calls A's submethod with B as an argument.
class B:
def __init__(self, argumentThatIsAClassAInstance):
self.classAInstance = argumentThatIsAClassAInstance
def submethod(self):
self.classAInstance.submethod(self)
Let's initialize it with classInstance:
classBInstance = B(classAInstance)
My desired result is that all the attributes of B are printed when B.submethod is called. Is this possible, and if not, how would I achieve something like this?
Now, I have a class, B. Let's add a submethod that calls A's submethod
with B as an argument.
But that isn't what your code does. On the following line:
self.classAInstance.submethod(self)
You are calling the method (I don't know what you mean by "sub" method, these are all just normal methods) with *an instance of B, not B.
Two different ways you could do this:
self.classAInstance.submethod(type(self))
Or:
self.classAInstance.submethod(B)
The semantics aren't exactly the same, since the first dynamically retreives the instance, if some other class inherits from B, it will call dir on that class. The second always prints dir(B), regardless of inheritance.
So:
class A:
def method(self, klass: type) -> None:
print(dir(klass))
class B:
def __init__(self, a: A) -> None:
self.a = a
def method(self) -> None:
self.a.method(type(self))
b = B(A())
As one potential solution, you can use inheritance. This allows class B to inherit everything from class A
class A:
def __init__(self, argument):
self.value = argument
def submethod(self, argumentThatWillBeAClass):
print(dir(argumentThatWillBeAClass))
class B(A):
def __init__(self, value):
super().__init__(value)
def submethod(self, argumentThatWillBeAClass): # You can override the method and do extra code too.
super().submethod(argumentThatWillBeAClass) # Calls A's submethod function

Instantiate an child object directly from a parent object

I have two objects one inherits from the other and the only difference between them is a few attribute fields:
class Parent:
def __init__(self,a, b):
self.a = a
self.b = b
def methodA(self):
# do something
pass
class Child(Parent):
def __init__(self,c,**kwargs):
self.c = c
super().__init__(**kwargs)
I have an instance of the parent object and I want to find a fast way in python to create an instance of the child object which only has one additional field by using the already existing parent object.
Is there a python way or module that lets you do that easily. IN my real code the parent class has hundreds of fields and it is a bit inefficient to just reassign its value.
The canonical solution is to add a class method to Child that works as a constructor. It takes a Parent instance and returns the Child instance with the proper attributes.
For example:
class Parent:
def __init__(self,a, b):
self.a = a
self.b = b
class Child(Parent):
def __init__(self,c,**kwargs):
self.c = c
super().__init__(**kwargs)
#classmethod
def from_parent(cls, parent, c):
return cls(a=parent.a, b=parent.b, c=c)
p = Parent(a=1, b=2)
c = Child.from_parent(parent=p, c=3)
print(c.a, c.b, c.c) # output: 1 2 3
I would argue that your Parent class having hundreds of attributes is irrelevant to the answer. Yes, it's tedious having to explicitly write every attribute of the Parent instance in the from_parent method, but that's simply a limitation of having a class with that many attributes anyway. Possibly, a better design choice would be to encapsulate groups of Parent attributes into proper classes, so that only those instances need to be delivered to the Child class upon initialization.
Ok the other suggestions for making a method that takes in parent attributes and creates a child object is ok but adds unnecessary code I think. I made this solution, which I ended up using. It doesnt accept the parent object directly in as an argument but it is more concise I think:
class Parent:
def __init__(self, a, b):
self.a = a
self.b = b
class Child(Parent):
def __init__(self,c, **kwargs):
self.c = c
super().__init__(**kwargs)
# So if I start with this parent object
parent_args = {"a":23,"b":"iuhsdg"}
parent =Parent(**parent_args)
# I then make child with all the parent attributes plus some more
child_args = {"c":567}
child_args.update(vars(parent))
child = Child(**child_args)

How to override only one variable inside parent class's method?

Let's suppose that Parent class is:
class Parent:
def __init__(self, a):
self.a = a
def do_something(self):
a = self.a
b = self.a
print('The a is ', a)
print('The b is ', b)
I want to create a Child class that will redefine only b
Something like:
class Child(Parent):
def do_something(self, custom_param):
#some_exotic_decorator_or_something_else(b=custom_param)
super().do_something()
And get output like:
obj = Child('the_a')
obj.do_something('the_b')
The a is the_a
The b is the_b
Of course original method has many lines and complicated logic to override whole method. Also that is one of common python libraries so I want to minimise intrusion inside method.
Here is any common way in python to do that?
To be more clear, the method what I want to override is _write_end_record
I want to redefine only centDirOffset. All other logic is working good. I may copy and paste whole method inside my code and change only one line, but it don't looking like a smart idea
A clean and easy way would be to make your parent class more general by adding a parameter to your method and assign a default value to it.
class Parent:
def __init__(self, a):
self.a = a
def do_something(self, b=None):
if b is None:
b = self.a
a = self.a
print('The a is ', a)
print('The b is ', b)
By doing this, you do not even need a child class.

Conditional Inheritance based on arguments in Python

Being new to OOP, I wanted to know if there is any way of inheriting one of multiple classes based on how the child class is called in Python. The reason I am trying to do this is because I have multiple methods with the same name but in three parent classes which have different functionality. The corresponding class will have to be inherited based on certain conditions at the time of object creation.
For example, I tried to make Class C inherit A or B based on whether any arguments were passed at the time of instantiating, but in vain. Can anyone suggest a better way to do this?
class A:
def __init__(self,a):
self.num = a
def print_output(self):
print('Class A is the parent class, the number is 7',self.num)
class B:
def __init__(self):
self.digits=[]
def print_output(self):
print('Class B is the parent class, no number given')
class C(A if kwargs else B):
def __init__(self,**kwargs):
if kwargs:
super().__init__(kwargs['a'])
else:
super().__init__()
temp1 = C(a=7)
temp2 = C()
temp1.print_output()
temp2.print_output()
The required output would be 'Class A is the parent class, the number is 7' followed by 'Class B is the parent class, no number given'.
Thanks!
Whether you're just starting out with OOP or have been doing it for a while, I would suggest you get a good book on design patterns. A classic is Design Patterns by Gamma. Helm. Johnson and Vlissides.
Instead of using inheritance, you can use composition with delegation. For example:
class A:
def do_something(self):
# some implementation
class B:
def do_something(self):
# some implementation
class C:
def __init__(self, use_A):
# assign an instance of A or B depending on whether argument use_A is True
self.instance = A() if use_A else B()
def do_something(self):
# delegate to A or B instance:
self.instance.do_something()
Update
In response to a comment made by Lev Barenboim, the following demonstrates how you can make composition with delegation appear to be more like regular inheritance so that if class C has has assigned an instance of class A, for example, to self.instance, then attributes of A such as x can be accessed internally as self.x as well as self.instance.x (assuming class C does not define attribute x itself) and likewise if you create an instance of C named c, you can refer to that attribute as c.x as if class C had inherited from class A.
The basis for doing this lies with builtin methods __getattr__ and __getattribute__. __getattr__ can be defined on a class and will be called whenever an attribute is referenced but not defined. __getattribute__ can be called on an object to retrieve an attribute by name.
Note that in the following example, class C no longer even has to define method do_something if all it does is delegate to self.instance:
class A:
def __init__(self, x):
self.x = x
def do_something(self):
print('I am A')
class B:
def __init__(self, x):
self.x = x
def do_something(self):
print('I am B')
class C:
def __init__(self, use_A, x):
# assign an instance of A or B depending on whether argument use_A is True
self.instance = A(x) if use_A else B(x)
# called when an attribute is not found:
def __getattr__(self, name):
# assume it is implemented by self.instance
return self.instance.__getattribute__(name)
# something unique to class C:
def foo(self):
print ('foo called: x =', self.x)
c = C(True, 7)
print(c.x)
c.foo()
c.do_something()
# This will throw an Exception:
print(c.y)
Prints:
7
foo called: x = 7
I am A
Traceback (most recent call last):
File "C:\Ron\test\test.py", line 34, in <module>
print(c.y)
File "C:\Ron\test\test.py", line 23, in __getattr__
return self.instance.__getattribute__(name)
AttributeError: 'A' object has no attribute 'y'
I don't think you can pass values to the condition of the class from inside itself.
Rather, you can define a factory method like this :
class A:
def sayClass(self):
print("Class A")
class B:
def sayClass(self):
print("Class B")
def make_C_from_A_or_B(make_A):
class C(A if make_A else B):
def sayClass(self):
super().sayClass()
print("Class C")
return C()
make_C_from_A_or_B(True).sayClass()
which output :
Class A
Class C
Note: You can find information about the factory pattern with an example I found good enough on this article (about a parser factory)

Inheriting a virtual class method - how to call it from base class?

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.

Categories

Resources