Add decorator to a method from inherited class? - python

I want to inherit from class just to add decorators to its methods.
Is there a shortcut to do this without redefining each base class method?

You can use a class decorator to encapsulate the whole work
Example code:
def deco(func):
"""Function decorator"""
def inner(*args, **kwargs):
print("decorated version")
return func(*args, **kwargs)
return inner
def decoclass(decorator):
"""Class decorator: decorates public methods with decorator"""
def outer(cls):
class inner(cls):
pass
for name in dir(cls):
if not name.startswith("_"): # ignore hidden and private members
# print("decorating", name) # uncomment for tests
attr = getattr(inner, name)
setattr(inner, name, decorator(attr))
return inner
return outer
class Foo:
"""Sample class""
def foo(self):
return "foo in Foo"
You can then use it:
>>> #decoclass(deco)
class Foo2(Foo):
pass
>>> f = Foo2()
>>> f.foo()
decorated version
'foo in Foo'

Sure, you can do this dynamically. Suppose you have some class:
>>> class Foo:
... def bar(self): print('bar')
... def baz(self): print('baz')
...
And a decorator:
>>> def deco(f):
... def wrapper(self):
... print('decorated')
... return f(self)
... return wrapper
...
Then simply inherit:
>>> class Foo2(Foo):
... pass
...
Then loop over your original class, and apply the decorator to your new child-class:
>>> for name, attr in vars(Foo).items():
... if callable(attr):
... setattr(Foo2, name, deco(attr))
...
So...
>>> x = Foo2()
>>> x.bar()
decorated
bar
>>> x.baz()
decorated
baz
Now, using if callable(attr) might not be restrictive enough. You might want to ignore "dunder" methods, so instead:
for name, attr in vars(Foo):
if callable(attr) and not name.startswith('__'):
setattr(Foo2, name, attr)
might be more appropriate. Depends on your use-case.
And just for fun, here we can also use the type constructor:
In [17]: class Foo:
...: def bar(self): print('bar')
...: def baz(self): print('baz')
...:
In [18]: def deco(f):
...: def wrapper(self):
...: print('decorated')
...: return f(self)
...: return wrapper
...:
In [19]: Foo3 = type(
...: 'Foo3',
...: (Foo,),
...: {name:deco(attr) for name, attr in vars(Foo).items() if callable(attr)}
...: )
In [20]: y = Foo3()
In [21]: y.bar()
decorated
bar
In [22]: y.baz()
decorated
baz

Related

Automatic override of parent methods

I'm trying to add a wrapper to each method in a class by subclassing it, and reassigning them in the constructor of the new class, however i'm getting the same reference for all subclassed methods, how is this possible?
class A:
def foo(self):
print("foo")
def bar(self):
print("bar")
class B(A):
def __init__(self):
super().__init__()
methods = [
(method_name, getattr(self, method_name)) for method_name in dir(self) if not method_name.startswith('_')
]
for (method_name, f) in methods:
def wrapper(*args, **kwargs):
print('wrapped')
return f(*args, **kwargs)
setattr(self, method_name, wrapper)
b = B()
b.foo()
>>> wrapped
>>> foo
b.bar()
>>> wrapped
>>> foo
This is a spin on a common python gotcha, late binding closures.
What is happening is the last value of f is being bound to all your wrapped methods.
A common workaround is binding your changing variable to a keyword argument or using functools.partial.
For your example you can use it as a keyword argument.
class A:
def foo(self, baz='foo'):
print(baz)
def bar(self, baz='bar'):
print(baz)
class B(A):
def __init__(self):
super().__init__()
methods = [
(method_name, getattr(self, method_name)) for method_name in dir(self) if not method_name.startswith('_')
]
for (method_name, f) in methods:
# here you can use an implied private keyword argument
# to minimize the chance of conflicts
def wrapper(*args, _f=f, **kwargs):
print('wrapped')
return _f(*args, **kwargs)
setattr(self, method_name, wrapper)
b = B()
b.foo()
b.foo('baz')
b.foo(baz='baz')
b.bar()
I added a few more calls to your method to demonstrate that it still works with different forms of calls.

Python return instance of a different class from class instead of self

I am going to try my best to explain what I am trying to accomplish. I am trying to return an instance of a new class from a class instead of return self. Please refer to the comments in the example code.
class Test(object):
def __init__(self, a):
self.a = a
def methoda(self):
return len(self.a)
class SomeClass(object):
def __init__(self, lol):
self.lol = lol
self.test = Test(self.lol)
def __call__(self):
return self.test # this isnt going to work
c = SomeClass('bar')
print(c) # trying to access the Test class attributes and methods here
# so in the above example, i want to do
# print(c.a) # print bar
# print(c.length() # print 3
__repr__ and __str__ wouldnt work in this case because I am trying to get the Test class object back.
Other things i have tried in SomeClass is having something like self.test = Test(self.lol), but that doesnt seem to quite do what I want.
How can I do this?
You can override the __getattr__ method of SomeClass so that attributes not defined in a SomeClass object can be delegated to self.test:
class Test(object):
def __init__(self, a):
self.a = a
def methoda(self):
return len(self.a)
class SomeClass(object):
def __init__(self, lol):
self.lol = lol
self.test = Test(self.lol)
def __getattr__(self, item):
return getattr(self.test, item)
c = SomeClass('bar')
print(c.methoda())
This outputs:
3
No quite convinced of the use case but you can use some proxy methods in SomeClass (use property whenever needed) to delegate the calls to the respective Test methods:
In [343]: class Test(object):
...: def __init__(self, a):
...: self.a = a
...:
...: def methoda(self):
...: return len(self.a)
...:
...:
...: class SomeClass(object):
...: def __init__(self, lol):
...: self.lol = lol
...: self.test = Test(self.lol)
...:
...: #property
...: def a(self):
...: return self.test.a
...:
...: def length(self):
...: return self.test.methoda()
...:
...: def __call__(self):
...: return self.test
...:
In [344]: c = SomeClass('bar')
In [345]: c.a
Out[345]: 'bar'
In [346]: c.length()
Out[346]: 3
Also note that, you can call the SomeClass instance i.e. c as you have the __call_- method defined, and returns the Test instance. That way you can get the values using:
In [347]: print(c())
<__main__.Test object at 0x7fba862fc3c8>
In [348]: print(c().a)
bar
In [349]: print(c().methoda())
3
For this you need to understand when __init__ is called and when __call__ is called.
class foo(object):
def __init__(self, lol):
print('__init__ called')
def __call__(self):
print('__call__ called')
ob = foo('lol')
print(ob())
_ = foo('lol')()
this will output:
__init__ called # ob = foo('lol')
__call__ called # print(ob())
__init__ called # _ = foo('lol')()
__call__ called # _ = foo('lol')()
__init__ is called when class is instantiate. __call__ is called when you call that object of class as function.
To access Test class in your case you can do following.
c = SomeClass('bar')
test_c = c()
print(c.a)
print(c.methoda)
What you are looking for is the __new__ special method. It allows to build from scratch the object created from the class:
class SomeClass(object):
def __new__(cls, lol):
obj = Test(lol)
return obj
You can then use it:
>>> c = SomeClass('bar')
>>> c
<__main__.Test object at 0x03F87750>
>>> c.a
'bar'
>>> c.methoda()
3
But you should not unless you make Test a subclass of SomeClass. It is at least uncommon that SomeClass('bar') returns an object that is not a SomeClass, and you could be burned later because of that...

Add an automated function call to each method

Is it possible to create a "constructor".. or rather "Initializer" to each function, instead of having to manually write it at the top of each function in class?
So, each time a function in a class is called, the other assigned function (unknown to caller) is always called first (called pre_check in below example).
An example using super(), but I then have to manually copy it inside each function.
class Helper():
def pre_check(self):
print("Helper fcn")
class Parent(Helper):
def __init__(self):
print("Initializer")
def foo(self):
super().pre_check() # <---- new code
# ... existing code here ...
def bar(self):
super().pre_check() # <---- new code
# ... existing code here ...
def many_more_functions(self):
super().pre_check() # <---- new code
# ... existing code here ...
m = Parent()
m.foo()
m.bar()
Note how __init__ in Parent is not supposed to run pre_check.
You can use a decorator for the class that will in turn decorate all public methods defined in the class:
def addhelper(helpmethod):
def deco(cls):
def decomethod(method):
def inner(self, *args, **kwargs):
helpmethod(self)
return method(self, *args, **kwargs)
# copy signature, doc and names from the original method
inner.__signature__ = inspect.signature(method)
inner.__doc__ = method.__doc__
inner.__name__ = method.__name__
inner.__qualname__ = method.__qualname__
return inner
# search all methods declared in cls with a name not starting with _
for name, meth in inspect.getmembers(
cls,lambda x: inspect.isfunction(x)
and not x.__name__.startswith('_')
and x.__qualname__.startswith(cls.__name__)):
# replace each method with its decoration
setattr(cls, name, decomethod(meth))
return cls
return deco
class Helper():
def pre_check(self):
print("Helper fcn")
#addhelper(Helper.pre_check)
class Parent(Helper):
def __init__(self):
print("Initializer")
def foo(self):
# super().pre_check() # <----
print('in foo')
def bar(self):
# super().pre_check() # <----
print('in bar')
def many_more_functions(self):
# super().pre_check() # <----
print('in many_more_functions')
We can now use it:
>>> p = Parent()
Initializer
>>> p.foo()
Helper fcn
in foo
>>> p.bar()
Helper fcn
in bar
>>> p.many_more_functions()
Helper fcn
in many_more_functions
Use __init_subclass__ to change subclasses as they are created. You can wrap the methods of subclasses:
class Helper():
def __init_subclass__(cls):
for field, value in cls.__dict__.items():
# add additional checks as desired, e.g. exclude __special_methods__
if inspect.isfunction(value) and not getattr(value, 'checked', False):
setattr(cls, field, cls._check(value)) # wrap method
#classmethod
def _check(cls, fcn):
"""Create a wrapper to inspect the arguments passed to methods"""
#functools.wraps(fcn)
def checked_fcn(*args, **kwargs):
print(fcn, "got", args, kwargs)
return fcn(*args, **kwargs)
return checked_fcn
class Parent(Helper):
def __init__(self):
print("Initializer")
def foo(self):
print("Foo")
Note that this will wrap all methods, including special methods such as __init__:
>>> Parent().foo()
<function Parent.__init__ at 0x1029b2378> got (<__main__.Parent object at 0x102c09080>,) {}
Initializer
<function Parent.foo at 0x1029b2158> got (<__main__.Parent object at 0x102c09080>,) {}
Foo
You can extend the check in __init_subclass__ with arbitrary rules to filter out functions. For example, field[:2] == field[-2:] == "__" excludes special methods.
You can use metaclass and define a decorator for each method in the instance of that metaclass
Code :
def decorate(f):
def do_something(self, a):
if (f(self, a) > 18) :
return ("Eligible to vote")
else :
return ("Not eligible to vote")
return do_something
class Meta(type):
def __new__(cls, name, bases, namespace, **kwds):
namespace = {k: v if k.startswith('__') else decorate(v) for k, v in namespace.items()}
return type.__new__(cls, name, bases, namespace)
class MetaInstance(metaclass=Meta):
def foo1(self, val):
return val + 15
def foo2(self, val):
return val + 9
obj1 = MetaInstance()
print(obj1.foo1(5))
print(obj1.foo2(2))

Is there a use for the code cls().__init__() in a classmethod or elsewhere?

I have seen cls().__init__() used in a classmethod, but it seems that the code could have used a simple cls() instead. As in:
class SomeCrazyClass:
#classmethod
def newclass(cls):
return cls().__init__()
#classmethod
def newclass2(cls):
return cls()
Is this just a poor coding style choice or is there a practical use of cls().__init__() in some situation?
The difference between cls().__init__() and cls() is that former calls the __init__ on instance twice and hence will return None and the latter will return the actual instance.
But an imaginary scenario to of calling __init__ again can be used in lazy initialization of a class or may be some other use-cases as well.
For example in the below code the instance variables are loaded only on the first access of an attribute:
def init(cls, real_init):
def wrapped(self, *args, **kwargs):
cls.__init__ = real_init
return wrapped
class A(object):
def __new__(cls, *args, **kwargs):
cls.__init__ = init(cls, cls.__init__)
instance = object.__new__(cls)
return instance
def __getattr__(self, attr):
expected_attrs = ('a', 'b')
if attr in expected_attrs:
self.__init__(range(10000), range(1000))
return object.__getattribute__(self, attr)
def __init__(self, a, b):
print('inside __init__')
self.a = sum(a)
self.b = sum(b)
Demo:
>>> a = A()
>>> a.__dict__
{}
>>> a.a, a.b
inside __init__
(49995000, 499500)
>>> a.__dict__
{'a': 49995000, 'b': 499500}
>>> a = A()
>>> a.__init__(range(10**5), range(10**4))
inside __init__
>>> a.a, a.b
(4999950000, 49995000)
We can now also return a value from __init__ now which is usually not possible.

Can create a def in python class which can be called what ever the def name even if the def didn't exist

I'm looking to do something like this:
class MyClass(Object):
def ****(self):
print self.__name __
MyClass.test()
->test
MyClass.whatever()
->whatever
So you can call any method and it prints the name.
Implement a __getattr__() method on your class to intercept access attempts on unknown attributes, and return a function (which you could bind to the class):
class MyClass(object):
def __getattr__(self, name):
def echo():
return name
return echo
This returns unbound functions, with no reference to the instance.
You do need to create an instance first for this to work:
>>> class MyClass(object):
... def __getattr__(self, name):
... def echo():
... return name
... return echo
...
>>> instance = MyClass()
>>> instance.test()
'test'
>>> instance.whatever()
'whatever'
You can bind the function to the instance (so it gets self passed in) by manually invoking the descriptor protocol, calling __get__ on the function before returning:
class MyClass(object):
def __getattr__(self, name):
def echo(self):
return '{}.{}'.format(type(self).__name__, name)
return echo.__get__(self, type(self))
With access to self we can print a little more information:
>>> class MyClass(object):
... def __getattr__(self, name):
... def echo(self):
... return '{}.{}'.format(type(self).__name__, name)
... return echo.__get__(self, type(self))
...
>>> instance = MyClass()
>>> instance.test()
'MyClass.test'
>>> instance.whatever()
'MyClass.whatever'

Categories

Resources