I have a class A:
class A(object):
def pprint(x):
print(x)
Then I have a class B:
class B(object):
def pprint(x):
x += 1
# find a way to call A.pprint(x)
Then I have a child class:
class Child(B, A):
pass
Which should be used:
child = Child()
child.pprint(1)
>>> 2
I can make changes to B but not to A. I cannot refer to A directly in B. B will never be instantiated directly, always via children class.
After the explanation - what you need is not super() you need something like sibling_super() to find the next class in the multiple inheritance chain. You can poll Python's MRO for that, for example:
class A(object):
def pprint(self, x): # just to make it valid, assuming it is valid in the real code
print(x)
class B(object):
#staticmethod
def sibling_super(cls, instance):
mro = instance.__class__.mro()
return mro[mro.index(cls) + 1]
def pprint(self, x):
x += 1
self.sibling_super(B, self).pprint(self, x)
class Child(B, A):
pass
child = Child()
child.pprint(1) # 2
You have a couple of options for accessing the A method from the B class without having B inherit from A.
First, you could create a staticmethod and call it from B.
class A(object):
#staticmethod
def pprint(x):
print(x)
class B(object):
def pprint(self, x):
print(x + 1)
A.pprint(x)
Or you could inherit A in B like this:
class A(object):
def pprint(self, x):
print(x)
class B(A):
def pprint(self, x):
print(x + 1)
super(B, self).pprint(x)
Then for your Child class only inherit from B:
class Child(B):
pass
>>> c = Child()
>>> c.pprint(1)
2
1
OK, newest solution.
import inspect
class C(B, A):
def pprint(self, x):
a_class = inspect.getmro(Child)[-2]
a_class.pprint(self, x)
Since object will be the last result in inspect.getmro(Child) we skip that one to get the one before the last one, which is A. We then call that class's pprint method. You could also, to be more sure, if you know the __name__ of the class you want to call, iterate over the results from inspect.getmro(Child) and find the one that you want.
Related
When working with python instances, it is possible to access bound methods of the same class using self. This resolves to a method corresponding to the same class in hierarchy.
class A:
def f(self):
return 1
def __init__(self):
self.v = self.f()
class B(A):
def f(self):
return 2
b = B()
# b.v is set to 2
But, when working with class methods, there is no apparent way of accessing methods of the same class as above.
In my use case, f above needs to be a class method and I need to set class variable v according to f corresponding to the same class. Somewhat like:
class A:
#classmethod
def f(cls):
return 1
v = resolution_of_calling_class.f()
class B(A):
#classmethod
def f(cls):
return 2
# B.v should be 2
edit: v is actually an attribute defined by parent class, which should find a method overridden by child class
You just need to override __new__ method, since it is invoked before the __init__ and its purpose is to create an instance, that will be initialized by __init__.
class A:
def __new__(cls, *args, **kwargs):
cls.v = cls.f()
return super().__new__(cls, *args, **kwargs)
#classmethod
def f(cls):
return 1
class B(A):
#classmethod
def f(cls):
return 2
a = A()
print(a.v)
b = B()
print(b.v)
1
2
I am not 100% sure I understand what you are trying to do.
I used your code above and
class A:
#classmethod
def f(cls):
return 1
class B:
#classmethod
def f(cls):
return 2
print(B.f())
gives me 2 just as I expected it would. Should B be a child class of A as in the first example?
So basically my problem seems like this.
class A():
def func(self):
return 3
class B():
def func(self):
return 4
class AA(A):
def func(self):
return super(AA, self).func
class BB(B):
def func(self):
return super(BB, self).func
The func function is doing some work and one of the things it does is getting some attribute(or running method or whatever) from it's parent class.
Since func originally does the same logic at both cases (except that only parent class changes) I'd like to do this with decorators.
Is it possible? if so how to do it? Do I have somehow to pass parent-class as a argument?
I'll be very grateful for answers it's been bothering me for a while now.
There is no need to use super to access data attributes of a parent class.
Neither does a class need a parent in order for access to data attributes to work.
You can use a mixin to do the job:
# A and B stay the same - they still have a c attribute
class A():
c = 3
class B():
c = 4 # I've changed B to make it clear below
#Instead have a mixin which defines func()
class Mixin:
def func(self):
# func has its behaviour here
return self.c
class AA(Mixin, A):
pass
class BB(Mixin, B):
pass
a = AA()
b = BB()
print(a.func())
print(b.func())
Output:
3
4
You could do it with a single class decorator by defining a generic method inside of it that does what you want, and then adding it to the class being decorated. Here's what I mean:
def my_decorator(cls):
def call_super_func(self):
return super(type(self), self).func()
setattr(cls, 'call_super_func', call_super_func)
return cls
class A():
def func(self):
print('in A.func')
return 3
class B():
def func(self):
print('in B.func')
return 4
#my_decorator
class AA(A):
def func(self):
print('in AA.func')
return self.call_super_func()
#my_decorator
class BB(B):
def func(self):
print('in BB.func')
return self.call_super_func()
aa = AA()
aa.func()
bb = BB()
bb.func()
Output:
in AA.func
in A.func
in BB.func
in B.func
Of course you could eliminate the need to do this by just defining baseclass for A and B that has a call_super_func() method in it that they would then both inherit.
Here is the format of my code:
class A(object):
def __init__(self, x, other):
self.other = other
self.x = x
class B(A):
def __init__(self):
# place code here
def something_else(self):
return self.x["foo"]
x is an object which I would like to call, with a subscript later on (in something_else.
I would like only x to be inherited from the parent class.
It is important that other is not inherited, so super().__init__ is not suitable.
I have attempted a workaround by creating a function within class A:
def x(self):
return self.x
so I could call super().x() in class B, but this doesn't work either.
I have attempted calling directly super.x["foo"], and this doesn't work.
How can I achieve what I want in my case?
Thanks!
Variables don't always have to be registered in the __init__ function, if you want x from class A, have a method in A:
def set_x(self, x):
self.x = x
# other stuff
you'll still be able to call set_x from class B as all functions are inherited, from there you can instantiate property x without calling __init__ from A.
Im just learning about classes in python and I would
like to know how results from a method in one class can
be used in a another class..here is the scenario.
Class A:
def x(self):
#do stuff and call Class B
Class B:
def y(self):
#do stuff get results and pass results to Class A
results
How can results be passed back to Class A ?
Thanks
maybe you want this
class A:
def x(self):
#do stuff and call Class B
n = 1
n += B().y() #Instance B and call y method
print n
class B:
def y(self):
#do stuff get results and pass results to Class A
x = 10
return x
a = A() #create a object with type A
a.x() #call x method of A class
Class A:
def x(self):
#do stuff and call Class B
my_B = B()
theResult = my_B.y() # you just called B's function y()
Class B:
def y(self):
#do stuff get results and pass results to Class A
return results
Assuming you create class B within the method A.x:
Class A:
def x(self):
#do stuff and call Class B
results = B().y()
Class B:
def y(self):
#do stuff get results and pass results to Class A
return results
Assuming method A.x gets the object of class B:
Class A:
def x(self, b):
#do stuff and call Class B
results = b.y()
Class B:
def y(self):
#do stuff get results and pass results to Class A
return results
Assume that we have an object k of type class A. We defined a second class B(A). What is the best practice to "convert" object k to class B and preserve all data in k?
This does the "class conversion" but it is subject to collateral damage. Creating another object and replacing its __dict__ as BrainCore posted would be safer - but this code does what you asked, with no new object being created.
class A(object):
pass
class B(A):
def __add__(self, other):
return self.value + other
a = A()
a.value = 5
a.__class__ = B
print a + 10
a = A() # parent class
b = B() # subclass
b.value = 3 # random setting of values
a.__dict__ = b.__dict__ # give object a b's values
# now proceed to use object a
Would this satisfy your use case? Note: Only the instance variables of b will be accessible from object a, not class B's class variables. Also, modifying variables in a will modify the variable in b, unless you do a deepcopy:
import copy
a.__dict__ = copy.deepcopy(b.__dict__)
class A:
def __init__(self, a, b):
self.a = a
self.b = b
class B(A):
def __init__(self, parent_instance, c):
# initiate the parent class with all the arguments coming from
# parent class __dict__
super().__init__(*tuple(parent_instance.__dict__.values()))
self.c = c
a_instance = A(1, 2)
b_instance = B(a_instance, 7)
print(b_instance.a + b_instance.b + b_instance.c)
>> 10
Or you could have a sperate function for this:
def class_converter(convert_to, parent_instance):
return convert_to(*tuple(parent_instance.__dict__.values()))
class B(A):
def __init__(self, *args):
super().__init__(*args)
self.c = 5
But using the 2nd method, I wasn't able to figure out how to pass additional values