Calling different parent-class methods with one decorator - python

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.

Related

Python class decorator that overrides internal method

I'm trying to write a decorator that can be used to override a method during inheritance, but externally.
Something like:
class A:
def some_method(self):
print('does stuff')
def override(method):
# override decorator
....
def some_method_override():
print('does other stuff')
#override(method, some_method_override)
class B:
#overridden
a = A()
b = B()
a.some_method() # prints 'does stuff'
b.some_method() # prints 'does other stuff'
If anyone could share some ideas on this, that would be great!
Here is how I would go about solving the issue based on use-case;
FOR TESTS - I would look into the mock library if you are trying to dynamically override methods. Here is a resource - https://docs.python.org/3/library/unittest.mock.html
FOR SEPARATE CLASS - If it is class-wide change I would go with a child class that overrides the original method; this will make your code much more explicit
class A:
def some_method(self):
print('do_stuff_a')
class B(A):
def some_method(self):
print('do_stuff_b')
a = A()
a.some_method() # print 'do_stuff_a'
b = B()
a.some_method() # prints 'do_stuff_b'
FOR SPECIFIC SITUATION - If you only want this behavior to occur sometimes, I would use the state-observer code pattern - here is an example
class A:
def __init__(self)
self._use_method_a = True
def use_method_a(self, use_method_a):
self._use_method_a = use_method_a
def some_method(self):
if self._use_method_a:
return self._method_a()
else:
return self._method_b()
def _method_a(self):
print('do_stuff_a')
def _method_b(self):
print('do_other_stuff_b')
a = A()
a.some_method() # print 'do_stuff_a'
a.use_method_a(False)
a.some_method() # prints 'do_other_stuff_b'
As mentioned by others in the comments, I don't see any need for a decorator here. It's just a matter of class-attribute assignment.
Inheritance doesn't happen when a class is defined; methods from the parent aren't "pulled in" to the child class. Rather, it happens during attribute lookup. If B.some_method isn't defined, then the value of A.some_method is used. So to override a parent-class method, you simply provide a definition of the desired class attribute. Methods are just class attributes with values of type function.
class A:
def some_method(self):
print('does stuff')
def some_method_override(self):
print('does other stuff')
class B(A):
some_method = some_method_override
a = A()
b = B()
a.some_method() # prints 'does stuff'
b.some_method() # prints 'does other stuff'
If you did want a decorator, it would simply be something like
def override(old: str, new: Callable):
def _(cls):
setattr(cls, old, new)
return cls
return _
def some_method_override(self):
print('does other stuff')
class A:
def some_method(self):
print('does stuff')
#override('some_method', some_method_override)
class B(A):
pass

Can we pass the class object inside its own method?

I have a class A object method which uses another class B object's method, which the argument is class A object.
class A():
def calculate(self):
B = B.calculator(A)
class B():
def calculator(self, A):
...do something with A.attributes
It is possible to just pass attributes into the object, but I would see this possibility as the last priority. I am definitely a bit oversimplify my case, but I am wondering if there is a way to pass the entire class
Edit:
Sorry for the confusion. At the end I am trying to call class A object and A.calculate(), which will call class B obj and calculator function.
class A():
def __init__(self, value):
self.value = value
def calculate(self):
Bobj = B()
Bobj.calculator(A)
class B():
def calculator(self, A):
...do something with A.value
def main():
Aobj = A(value)
Aobj.calculate()
Your scenario does not currently indicate that you want to use any information from B when calculating A. There are a few ways of getting the functionality that you want.
Scenario: B stores no information and performs calculation. B should be a function
def B(value):
```do something with value```
return
class A():
def __init__(self, value):
self.value = value
def calculate(self):
return B(self.value)
def main():
Aobj = A(value)
Aobj.calculate()
Scenario: B stores some other information, but internal B information is not needed for the calculation. B should have a static method
class B():
#staticmethod
def calculate(value):
```do something with value```
return
class A():
def __init__(self, value):
self.value = value
def calculate(self):
return B.calculate(self.value)
def main():
Aobj = A(value)
Aobj.calculate()

How to call class method within namespace of the same class in python 3.x

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?

Call grandparent method in grand child class

Sorry, may be this is silly question, but its very confusing to me. Let's suppose we have the following classes:
class A():
def say(self):
print("A")
class B(A):
def say(self):
print("B")
class C(B):
def say(self,*args, **kwargs):
return super(C, self).say(*args, **kwargs)
I am accessing parent method in child, and it prints B, but I want to access method from class A as we are getting access from class B.
I know we can add super in class B, but I don't want to modify class B. so is there any option to get method from A directly in class C?
You can by calling A.say(self) like this:
class A():
def say(self):
print("A")
class B(A):
def say(self):
print("B")
class C(B):
def say(self):
A.say(self)
B.say(self)
print("C")
Then to test it out from a terminal:
>>> a = A()
>>> a.say()
A
>>> b = B()
>>> b.say()
B
>>> c = C()
>>> c.say()
A
B
C
Note: I dropped the args and kwargs because the A and B classes didn't use those arguments. If you wanted to make say take those all the way up though simply call A.say(self, *args, **kwargs) and if A.say returns something you can return it too

Access derived class attribute in base class function decorator

I want to do something like:
class A(Resource):
#dec(from_file=A.docpath)
def get(self):
pass
class B(A):
docpath = './docs/doc_for_get_b.json'
class C(A):
docpath = './docs/doc_for_get_c.json'
def dec(*args, **kwargs):
def inner(f):
docpath = kwargs.get('from_file')
f.__kwargs__ = open(path, 'r').read()
return f
return inner
The functions that will be called are B.get and C.get, never A.get.
How can I access the custom attribute docpath defined in class B or class C and pass it to the decorator of the get function in class A ?
Current solution: Put the decorator on each derived class ...
class A(Resource):
def _get(self):
pass
class B(A):
#dec(from_file='./docs/doc_for_get_b.json')
def get(self):
return self._get()
class C(A)
#dec(from_file='./docs/doc_for_get_c.json')
def get(self):
return self._get()
This works but it's pretty ugly compared to the one-line declaration of the classes in the previous code.
To access a class's attributes inside the decorator is easy:
def decorator(function):
def inner(self):
self_type = type(self)
# self_type is now the class of the instance of the method that this
# decorator is wrapping
print('The class attribute docpath is %r' % self_type.docpath)
# need to pass self through because at the point function is
# decorated it has not been bound to an instance, and so it is just a
# normal function which takes self as the first argument.
function(self)
return inner
class A:
docpath = "A's docpath"
#decorator
def a_method(self):
print('a_method')
class B(A):
docpath = "B's docpath"
a = A()
a.a_method()
b = B()
b.a_method()
In general I've found using multiple levels of decorators, i.e. decorator factory functions that create decorators such as you've used and such as:
def decorator_factory(**kwargs):
def decorator_function(function):
def wrapper(self):
print('Wrapping function %s with kwargs %s' % (function.__name__, kwargs))
function(self)
return wrapper
return decorator_function
class A:
#decorator_factory(a=2, b=3)
def do_something(self):
print('do_something')
a = A()
a.do_something()
a difficult thing to get right and not easy to comprehend when reading code, so I would err towards using class attributes and generic superclass methods in favour of lots of decorators.
So in your case, don't pass the file path in as an argument to your decorator factory, but set it as a class attribute on your derived classes, and then write a generic method in your superclass that reads the class attribute from the instance's class.

Categories