How to add pre/post methods to class. Python - python

Lets assume I've a class A which has a bunch of methods, but I want it to run certain lines before and after each method is called.
For example: I want my class Dog here to run before() and after() every time bark() or run() are been called.
class Dog():
def __init__(self, sound, speed):
self.sound = sound
self.speed = speed
def before(self):
check_some_things(self)
def after(self):
do_some_things(self)
def bark(self):
sound(self.sound)
def run(self):
move(self.speed)

You could encapsulate this in a decorator; the following decorator will call before and after if these are available on self:
import inspect
from functools import wraps
def before_and_after(f):
#wraps(f)
def wrapper(self, *args, **kw):
if hasattr(self, 'before') and inspect.ismethod(self.before):
self.before()
result = f(self, *args, **kw)
if hasattr(self, 'after') and inspect.ismethod(self.after):
self.after()
return result
return wrapper
then simply apply to the methods that should be wrapped:
class Dog():
def __init__(self, sound, speed):
self.sound = sound
self.speed = speed
def before(self):
check_some_things(self)
def after(self):
do_some_things(self)
#before_and_after
def bark(self):
sound(self.sound)
#before_and_after
def run(self):
move(self.speed)
The decorator assumes it is used on methods, e.g. the produced wrapper expects self as a first argument.
If this needs to apply to all methods that are not before or after, perhaps a metaclass is in order:
class BeforeAfterMeta(type):
def __new__(mcs, classname, bases, body):
for name, value in body.items():
if not inspect.isfunction(value):
continue
if name in ('before', 'after') or name[:2] + name[-2:] == '_' * 4:
# before or after hook, or a special method name like __init__.
continue
body[name] = before_and_after(value)
return super(BeforeAfterMeta, mcs).__new__(mcs, classname, bases, body)
which you then can apply to your class:
class Dog(metaclass=BeforeAfterMeta):
def __init__(self, sound, speed):
self.sound = sound
self.speed = speed
def before(self):
check_some_things(self)
def after(self):
do_some_things(self)
def bark(self):
sound(self.sound)
def run(self):
move(self.speed)

You could also use a decorator function to inspect your class Dog if the pre and post methods exists and override the run method:
def PrePostMethod(inputClass):
mainRun = inputClass.run
beforeFunc = inputClass.before if "before" in inputClass.__dict__ else None
afterFunc = inputClass.after if "after" in inputClass.__dict__ else None
def new_run(self, *args, **kwargs):
# you could inspect the given arguments if you need
# to parse arguments into before and the after methods
if beforeFunc:
self.before()
mainRun(self)
if afterFunc:
self.after()
inputClass.run = new_run
return inputClass
#PrePostMethod
class Dog(object):
def __init__(self, sound, speed):
self.sound = sound
self.speed = speed
def before(self):
print "Do stuff before"
def after(self):
print "Do stuff after"
def run(self):
print "Do main process"
Dog(1,2).run()
To parse arguments and keywords arguments from run into before and after, use the class inspect and loop through the args and kwargs to parse the right ones.
from inspect import getargspec
def argHandler(method, *args, **kwargs):
method = getargspec(method)
mArgs = method.args
mKwargs = method.keywords
rArgs = args[:len(mArgs)-1]
rKwargs = { k:v for k,v in kwargs.iteritems() if k in mKwargs }
leftArgs = len(mArgs)-len(rArgs)
if len(rKwargs):
rKwargs = [ rKwargs[k] for k in mArgs[:leftArgs-1]]
rArgs += rKwargs
return rArgs
def PrePostMethod(inputClass):
mainRun = inputClass.run
beforeFunc = inputClass.before if "before" in inputClass.__dict__ else None
afterFunc = inputClass.after if "after" in inputClass.__dict__ else None
def new_run(self, *args, **kwargs):
if beforeFunc:
nargs = argHandler(self.before, *args, **kwargs)
if nargs: self.before( *nargs)
else: self.before()
nargs = argHandler(mainRun, *args, **kwargs)
if nargs: mainRun(self, *nargs)
else: mainRun(self)
if afterFunc:
nargs = argHandler(self.after, *args, **kwargs)
if nargs: self.after( *nargs)
else: self.after()
inputClass.run = new_run
return inputClass

You can use many different ways to do this. But I think the best way is, to define a class with the Pre- and Post-Methods and redefine it's object hidden methods: __enter__ and __exit__. To use them, just call the class with the compound statement with.
class pre_post(object):
def __enter__(self):
print "Enter check method.."
def __exit__(self, type, value, tb):
print "Exit check method.."
class dog(object):
def run(self, checkups=True):
if checkups:
with pre_post() as pp:
print "My stuff.."
else:
print "My stuff.."
dog().run(True)
This will give you the following result:
Enter check method..
My stuff..
Exit check method..
I hope that will help you.

Related

Accessing class property as decorator argument

I'm trying to apply a conditional decorator as described in another stackoverflow post, but I'd like the condition to be set from inside the class its being used. Instead I get a Reference error pointing that self is not defined.
class foo:
def __init__(self):
self.debug = True
#conditional_decorator(decorator, self.debug)
def function(self):
pass
I tried defining a global variable and updating it from inside the __init__() method but it kept its original value when called as an argument of the decorator.
debug = None
class foo:
def __init__(self):
self.debug = True
global debug
debug = self.debug
#conditional_decorator(decorator, debug)
def function(self):
pass
The only way it worked was declaring a global variable and setting it outside of the class.
How can I apply the value of the class property to the decorator?
An update to the answer given by #Maurice Meyer which allows a member of the class to be nominated:
from functools import wraps
def conditional_decorator(decoration, member):
def decorator(method):
predecorated = decoration(method)
#wraps(method)
def wrapper(*args, **kwargs):
self = args[0]
condition = getattr(self, member)
if not condition:
return method(*args, **kwargs)
return predecorated(*args, **kwargs)
return wrapper
return decorator
#And used like this for example:
class foo:
def __init__(self, debug):
self.debug = debug
#conditional_decorator(decorator, "debug")
def function(self):
pass
f1 = foo(True)
f1.function()
This is how you make a decorator handle classes and arguments:
from functools import wraps
def conditional_decorator(param):
def real_decorator(fn):
#wraps(fn)
def wrapper(*args, **kw):
cls = args[0]
print(cls.debug)
print(param)
return wrapper
return real_decorator
class foo:
def __init__(self):
self.debug = True
#conditional_decorator('param1')
def function(self):
pass
f = foo()
f.function()
Output:
True
param1
The decorator should not be conditional. Rather, when the decorated function is called, it should look at self.debug to determine whether to use the original function or the wrapped part.
def conditional_decorator(dec):
def decorator(func):
def _(self, *args, **kwargs):
f = func
if self.debug:
f = dec(f)
return f(self, *args, **kwargs)
return _
return decorator
def decorator(f):
def _(*args, **kwargs):
print("Decorated")
return f(*args, **kwargs)
return _
class foo:
def __init__(self, debug):
self.debug = debug
#conditional_decorator(decorator)
def function(self):
print("foo stuff")
foo(True).function()
print("===")
foo(False).function()
outputs
Decorated
foo stuff
===
foo stuff

Setting a get/set property in a python memoization decorator class

I have created a decorator memoization class that I am actively using for cache my calls. There are already many excellent suggestions on how to implement python memoization.
The class that I have created currently uses get and set method calls to set the cacheTimeOut. They are called getCacheTimeOut() and setCacheTimeOut(). While this is an adequate solution. I was hoping to use the #property and #cacheTimeOut.setter decorators to enable the functions to be called directly as for example cacheTimeOut=120
The problem is in the details. I do not know how to make these properties accessible in the __get__ method. The __get__ method assigns the different function calls defined within the class to functions.partial.
Here is my script example designed for Python 2.7
import time
from functools import partial
import cPickle
class memoize(object):
def __init__(self, func):
self.func = func
self._cache = {}
self._timestamps = {}
self._cacheTimeOut = 120
self.objtype = None
def __new__(cls, *args, **kwargs):
return object.__new__(cls,*args, **kwargs)
def __get__(self, obj, objtype=None):
"""Used for object methods where decorator has been placed before methods."""
self.objtype = objtype
fn = partial(self, obj)
fn.resetCache = self.resetCache
fn.getTimeStamps = self.getTimeStamps
fn.getCache = self.getCache
fn._timestamps = self._timestamps
fn.setCacheTimeOut = self.setCacheTimeOut
fn.getCacheTimeOut = self.getCacheTimeOut
return fn
def __argsToKey(self, *args, **kwargs):
args = list(args)
for x, arg in enumerate(args): # remove instance from
if self.objtype:
if isinstance(arg, self.objtype):
args.remove(arg)
str = cPickle.dumps(args, 1)+cPickle.dumps(kwargs, 1)
return str
def __call__(self, *args, **kwargs):
"""Main calling function of decorator."""
key = self.__argsToKey(*args, **kwargs)
now = time.time() # get current time to query for key
if self._timestamps.get(key, now) > now:
return self._cache[key]
else:
value = self.func(*args, **kwargs)
self._cache[key] = value
self._timestamps[key] = now + self._cacheTimeOut
return value
def __repr__(self):
'''Return the function's docstring.'''
return self.func.__doc__
def resetCache(self):
"""Resets the cache. Currently called manually upon request."""
self._cache = {}
self._timestamps = {}
def getCacheTimeOut(self):
"""Get the cache time out used to track stale data."""
return self._cacheTimeOut
def setCacheTimeOut(self, timeOut):
"""Set the cache timeout to some other value besides 120. Requires an integer value. If you set timeOut to zero you are ignoring the cache"""
self._cacheTimeOut = timeOut
def getCache(self):
"""Returns the cache dictionary."""
return self._cache
def getTimeStamps(self):
"""Returns the encapsulated timestamp dictionary."""
return self._timestamps
#property
def cacheTimeOut(self):
"""Get cacheTimeOut."""
return self._cacheTimeOut
#cacheTimeOut.setter
def cacheTimeOut(self, timeOut):
"""Set cacheTimeOut."""
self._cacheTimeOut = timeOut
memoize
def increment(x):
increment.count+=1
print("increment.count:%d, x:%d"%(increment.count, x))
x+=1
return x
increment.count = 0 # Define the count to track whether calls to increment vs cache
class basic(object):
def __init__(self):
self.count = 0
#memoize
def increment(self, x):
self.count+=1
print("increment.count:%d, x:%d"%(increment.count, x))
x+=1
return x
def main():
print increment(3)
print increment(3)
# What I am actually doing
print increment.getCacheTimeOut() # print out default of 120
increment.setCacheTimeOut(20) # set to 20
print increment.getCacheTimeOut() # verify that is has been set to 120
# What I would like to do and currently does not work
print increment.cacheTimeOut
# Assign to property
increment.cacheTimeOut = 20
myObject = basic()
print myObject.increment(3)
print myObject.count
print myObject.increment(3)
print myObject.count
print myObject.increment(4)
print myObject.count
####### Unittest code.
import sys
import time
import unittest
from memoize import memoize
class testSampleUsages(unittest.TestCase):
# """This series of unit tests is to show the user how to apply memoize calls."""
def testSimpleUsageMemoize(self):
#memoize
def increment(var=0):
var += 1
return var
increment(3)
increment(3)
def testMethodBasedUsage(self):
"""Add the #memoize before method call."""
class myClass(object):
#memoize
def increment(self,var=0):
var += 1
return var
#memoize
def decrement(self, var=0):
var -=1
return var
myObj = myClass()
myObj.increment(3)
myObj.increment(3)
myObj.decrement(6)
myObj.decrement(6)
def testMultipleInstances(self):
#memoize
class myClass(object):
def __init__(self):
self.incrementCountCalls = 0
self.decrementCountCalls = 0
self.powCountCall = 0
# #memoize
def increment(self,var=0):
var += 1
self.incrementCountCalls+=1
return var
# #memoize
def decrement(self, var=0):
self.decrementCountCalls+=1
var -=1
return var
def pow(self, var=0):
self.powCountCall+=1
return var*var
obj1 = myClass() # Memoizing class above does not seem to work.
obj2 = myClass()
obj3 = myClass()
obj1.increment(3)
obj1.increment(3)
#obj2.increment(3)
#obj2.increment(3)
#obj3.increment(3)
#obj3.increment(3)
obj1.pow(4)
obj2.pow(4)
obj3.pow(4)
There's no way to attach a property to a single instance. Being descriptors, propertys must be part of a class definition in order to function. That means you can't easily add them to the partial object you create in __get__.
Now, you could create a class of your own to reimplement the behavior of partial with your added property. However, I suspect the limitation is actually to your benefit. If memo is applied to a method, its state is shared by all instances of the class (and perhaps even instances of subclasses). If you allow the caching details to be adjusted through instances, you might confuse users with cases like:
obj1 = basic()
print obj1.increment.getCacheTimeout() # prints the initial value, e.g. 120
obj2 = basic()
obj2.increment.setCacheTimeOut(20) # change the timeout value via another instance
print obj1.increment.getCacheTimeout() # the value via the first instance now prints 20
I suggest that you make the memoization-related interfaces of decorated methods accessible only through the class, not through instances. To make that work, you need to update your __get__ method to work if obj is None. It can simply return self:
def __get__(self, obj, objtype=None):
if obj is None:
return self
self.objtype = objtype
return partial(self, obj) # no need to attach our methods to the partial anymore
With this change, using a property on the memo via the class works:
basic.increment.cacheTimeOut = 20 # set property of the "unbound" method basic.increment
There is actually a way to accomplish this - by rebinding the decorator as instance-object with a call-method
class Helper(object):
def __init__(self, d, obj):
self.d = d
self.obj = obj
self.timeout = 0
def __call__(self, *args, **kwargs):
print self, self.timeout
return self.d.func(self.obj, *args, **kwargs)
class decorator(object):
def __init__(self, func):
self.func = func
self.name = func.__name__
def __get__(self, obj, clazz):
if object is not None:
obj.__dict__[self.name] = Helper(self, obj)
return obj.__dict__[self.name]
class Foo(object):
#decorator
def bar(self, args):
return args * 2
f = Foo()
g = Foo()
f.bar.timeout = 10
g.bar.timeout = 20
print f.bar(10)
print g.bar(20)
HTH

Class method decorator with self arguments?

How do I pass a class field to a decorator on a class method as an argument? What I want to do is something like:
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization("some_attr", self.url)
def get(self):
do_work()
It complains that self does not exist for passing self.url to the decorator. Is there a way around this?
Yes. Instead of passing in the instance attribute at class definition time, check it at runtime:
def check_authorization(f):
def wrapper(*args):
print args[0].url
return f(*args)
return wrapper
class Client(object):
def __init__(self, url):
self.url = url
#check_authorization
def get(self):
print 'get'
>>> Client('http://www.google.com').get()
http://www.google.com
get
The decorator intercepts the method arguments; the first argument is the instance, so it reads the attribute off of that. You can pass in the attribute name as a string to the decorator and use getattr if you don't want to hardcode the attribute name:
def check_authorization(attribute):
def _check_authorization(f):
def wrapper(self, *args):
print getattr(self, attribute)
return f(self, *args)
return wrapper
return _check_authorization
A more concise example might be as follows:
#/usr/bin/env python3
from functools import wraps
def wrapper(method):
#wraps(method)
def _impl(self, *method_args, **method_kwargs):
method_output = method(self, *method_args, **method_kwargs)
return method_output + "!"
return _impl
class Foo:
#wrapper
def bar(self, word):
return word
f = Foo()
result = f.bar("kitty")
print(result)
Which will print:
kitty!
from re import search
from functools import wraps
def is_match(_lambda, pattern):
def wrapper(f):
#wraps(f)
def wrapped(self, *f_args, **f_kwargs):
if callable(_lambda) and search(pattern, (_lambda(self) or '')):
f(self, *f_args, **f_kwargs)
return wrapped
return wrapper
class MyTest(object):
def __init__(self):
self.name = 'foo'
self.surname = 'bar'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'foo')
def my_rule(self):
print 'my_rule : ok'
#is_match(lambda x: x.name, 'foo')
#is_match(lambda x: x.surname, 'bar')
def my_rule2(self):
print 'my_rule2 : ok'
test = MyTest()
test.my_rule()
test.my_rule2()
ouput:
my_rule2 : ok
Another option would be to abandon the syntactic sugar and decorate in the __init__ of the class.
def countdown(number):
def countdown_decorator(func):
def func_wrapper():
for index in reversed(range(1, number+1)):
print(index)
func()
return func_wrapper
return countdown_decorator
class MySuperClass():
def __init__(self, number):
self.number = number
self.do_thing = countdown(number)(self.do_thing)
def do_thing(self):
print('im doing stuff!')
myclass = MySuperClass(3)
myclass.do_thing()
which would print
3
2
1
im doing stuff!
I know this issue is quite old, but the below workaround hasn't been proposed before. The problem here is that you can't access self in a class block, but you can in a class method.
Let's create a dummy decorator to repeat a function some times.
import functools
def repeat(num_rep):
def decorator_repeat(func):
#functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_rep):
value = func(*args, **kwargs)
return
return wrapper_repeat
return decorator_repeat
class A:
def __init__(self, times, name):
self.times = times
self.name = name
def get_name(self):
#repeat(num_rep=self.times)
def _get_name():
print(f'Hi {self.name}')
_get_name()
I know this is an old question, but this solution has not been mentioned yet, hopefully it may help someone even today, after 8 years.
So, what about wrapping a wrapper? Let's assume one cannot change the decorator neither decorate those methods in init (they may be #property decorated or whatever). There is always a possibility to create custom, class-specific decorator that will capture self and subsequently call the original decorator, passing runtime attribute to it.
Here is a working example (f-strings require python 3.6):
import functools
# imagine this is at some different place and cannot be changed
def check_authorization(some_attr, url):
def decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"checking authorization for '{url}'...")
return func(*args, **kwargs)
return wrapper
return decorator
# another dummy function to make the example work
def do_work():
print("work is done...")
###################
# wrapped wrapper #
###################
def custom_check_authorization(some_attr):
def decorator(func):
# assuming this will be used only on this particular class
#functools.wraps(func)
def wrapper(self, *args, **kwargs):
# get url
url = self.url
# decorate function with original decorator, pass url
return check_authorization(some_attr, url)(func)(self, *args, **kwargs)
return wrapper
return decorator
#############################
# original example, updated #
#############################
class Client(object):
def __init__(self, url):
self.url = url
#custom_check_authorization("some_attr")
def get(self):
do_work()
# create object
client = Client(r"https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments")
# call decorated function
client.get()
output:
checking authorisation for 'https://stackoverflow.com/questions/11731136/class-method-decorator-with-self-arguments'...
work is done...
You can't. There's no self in the class body, because no instance exists. You'd need to pass it, say, a str containing the attribute name to lookup on the instance, which the returned function can then do, or use a different method entirely.
It will be very useful to have a general-purpose utility, that can turn any decorator for functions, into decorator for methods. I thought about it for an hour, and actually come up with one:
from typing import Callable
Decorator = Callable[[Callable], Callable]
def decorate_method(dec_for_function: Decorator) -> Decorator:
def dec_for_method(unbounded_method) -> Callable:
# here, `unbounded_method` will be a unbounded function, whose
# invokation must have its first arg as a valid `self`. When it
# return, it also must return an unbounded method.
def decorated_unbounded_method(self, *args, **kwargs):
#dec_for_function
def bounded_method(*args, **kwargs):
return unbounded_method(self, *args, **kwargs)
return bounded_method(*args, **kwargs)
return decorated_unbounded_method
return dec_for_method
The usage is:
# for any decorator (with or without arguments)
#some_decorator_with_arguments(1, 2, 3)
def xyz(...): ...
# use it on a method:
class ABC:
#decorate_method(some_decorator_with_arguments(1, 2, 3))
def xyz(self, ...): ...
Test:
def dec_for_add(fn):
"""This decorator expects a function: (x,y) -> int.
If you use it on a method (self, x, y) -> int, it will fail at runtime.
"""
print(f"decorating: {fn}")
def add_fn(x,y):
print(f"Adding {x} + {y} by using {fn}")
return fn(x,y)
return add_fn
#dec_for_add
def add(x,y):
return x+y
add(1,2) # OK!
class A:
#dec_for_add
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# TypeError: add_fn() takes 2 positional arguments but 3 were given
# A().f(1,2)
class A:
#decorate_method(dec_for_add)
def f(self, x, y):
# ensure `self` is still a valid instance
assert isinstance(self, A)
return x+y
# Now works!!
A().f(1,2)

How can I add a delay to every method in a Python sub-class when I don't want to replicate every method in the parent class

My apologies if this question has already been answered somewhere, but if it has I have not been able to locate the answer.
I would like to create a sub-class of a parent class in such a way that there will be a delay (e.g. time.sleep()) before each call to the corresponding parent class method. I would like to do this in such a way that I do not need to replicate each parent class method in the child class. In fact, I would like to have a generic method that would work with virtually any parent class -- so that I do not even need to know all the parent class methods.
The delay would be specified when instantiating the sub-class.
For example:
class Parent():
....
def method1(self):
....
def method2(self):
....
class Child(Parent):
def __init__(self, delay)
self.delay = delay
....
child = Child(1)
A call to child.method1() would result in a 1 second delay before Parent.method1() is called.
I think the previously given answers have not really addressed your specific need to delay ALL methods from the parent class, and not necessarily have to go and decorate them. You said you do NOT want to have to replicate the parent class method in the child class just so that you can delay them. This answer uses the same delay wrapper from S.Lott, but also uses a metaclass (http://www.voidspace.org.uk/python/articles/metaclasses.shtml)
#!/usr/bin/env python
from types import FunctionType
import time
def MetaClassFactory(function):
class MetaClass(type):
def __new__(meta, classname, bases, classDict):
newClassDict = {}
for attributeName, attribute in classDict.items():
if type(attribute) == FunctionType:
attribute = function(attribute)
newClassDict[attributeName] = attribute
return type.__new__(meta, classname, bases, newClassDict)
return MetaClass
def delayed(func):
def wrapped(*args, **kwargs):
time.sleep(2)
func(*args, **kwargs)
return wrapped
Delayed = MetaClassFactory(delayed)
class MyClass(object):
__metaclass__ = Delayed
def a(self):
print 'foo'
def b(self):
print 'bar'
The MetaClassFactory wraps every function in the delayed decorator. If you wanted to make sure certain built-ins like the init function were not delayed, you could just check for that name in the MetaClassFactory and ignore it.
Really, what you have here is a design that involves a Strategy object.
Your best approach is to fix the parent class to include a call to a "delay object". A default delay object does nothing.
This violates the "so that I do not even need to know all the parent class methods" hoped-for feature set.
Method lookup doesn't have a handy __getmethod__ that corresponds to __getattribute__; this gap makes it difficult to tap into Python's internals for method invocation.
class Parent( object ):
delay= ZeroDelay()
def method1(self):
self.delay()
....
def method2(self):
self.delay()
...
class ZeroDelay( object ):
def __call__( self ):
pass
class ShortDelay( ZeroDelay ):
def __init__( self, duration=1.0 )
self.duration= duration
def __call__( self ):
time.sleep( self.duration )
class Child( Parent ):
delay= ShortDelay( 1 )
EDIT: Of course, you can decorate each method, also.
def delayed( delayer ):
def wrap( a_method ):
def do_delay( *args, **kw ):
delayer()
return a_method( *args, **kw )
return do_delay
return wrap
class Parent( object ):
delay= ZeroDelay()
#delayed( self.delay )
def method1(self):
self.delay()
....
#delayed( self.delay )
def method2(self):
self.delay()
...
S.Lott solution is a good one. If you need more granularity (i.e. to delay only certain methods, not all of them), you could go with a decorator:
from time import sleep
def delayed(func):
'''This is the decorator'''
def wrapped(*args, **kwargs):
sleep(2)
func(*args, **kwargs)
return wrapped
class Example(object):
#delayed
def method(self, str):
print str
e = Example()
print "Brace! I'm delaying!"
e.method("I'm done!")
The idea is that you add #delayed in before the definition of those methods you want to delete.
EDIT: Even more granularity: setting an arbitrary delay:
from time import sleep
def set_delay(seconds):
def delayed(func):
'''This is the decorator'''
def wrapped(*args, **kwargs):
sleep(seconds)
func(*args, **kwargs)
return wrapped
return delayed
class Example(object):
#set_delay(1)
def method(self, str):
print str
#set_delay(2)
def method_2(self, str):
print str
e = Example()
print "Brace! I'm delaying!"
e.method("I'm done!")
e.method_2("I'm also done!")
You can achieve what you want by using the method __getattribute__
class Child(Parent):
def __init__(self, delay):
self.delay = delay
def __getattribute__(self, name):
attr = object.__getattribute__(self, name)
if hasattr(attr, '__call__'):
def proxFct(*args, **kwargs):
time.sleep(object.__getattribute__(self, "delay"))
return attr(*args, **kwargs)
return proxFct
else:
return attr
Update: Updated according delnan's comment
Update 2: Updated according delnan's second comment

Python decorators in classes

Can one write something like:
class Test(object):
def _decorator(self, foo):
foo()
#self._decorator
def bar(self):
pass
This fails: self in #self is unknown
I also tried:
#Test._decorator(self)
which also fails: Test unknown
I would like to temporarily change some instance variables
in the decorator and then run the decorated method, before
changing them back.
Would something like this do what you need?
class Test(object):
def _decorator(foo):
def magic( self ) :
print "start magic"
foo( self )
print "end magic"
return magic
#_decorator
def bar( self ) :
print "normal call"
test = Test()
test.bar()
This avoids the call to self to access the decorator and leaves it hidden in the class namespace as a regular method.
>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>>
edited to answer question in comments:
How to use the hidden decorator in another class
class Test(object):
def _decorator(foo):
def magic( self ) :
print "start magic"
foo( self )
print "end magic"
return magic
#_decorator
def bar( self ) :
print "normal call"
_decorator = staticmethod( _decorator )
class TestB( Test ):
#Test._decorator
def bar( self ):
print "override bar in"
super( TestB, self ).bar()
print "override bar out"
print "Normal:"
test = Test()
test.bar()
print
print "Inherited:"
b = TestB()
b.bar()
print
Output:
Normal:
start magic
normal call
end magic
Inherited:
start magic
override bar in
start magic
normal call
end magic
override bar out
end magic
What you're wanting to do isn't possible. Take, for instance, whether or not the code below looks valid:
class Test(object):
def _decorator(self, foo):
foo()
def bar(self):
pass
bar = self._decorator(bar)
It, of course, isn't valid since self isn't defined at that point. The same goes for Test as it won't be defined until the class itself is defined (which its in the process of). I'm showing you this code snippet because this is what your decorator snippet transforms into.
So, as you can see, accessing the instance in a decorator like that isn't really possible since decorators are applied during the definition of whatever function/method they are attached to and not during instantiation.
If you need class-level access, try this:
class Test(object):
#classmethod
def _decorator(cls, foo):
foo()
def bar(self):
pass
Test.bar = Test._decorator(Test.bar)
import functools
class Example:
def wrapper(func):
#functools.wraps(func)
def wrap(self, *args, **kwargs):
print("inside wrap")
return func(self, *args, **kwargs)
return wrap
#wrapper
def method(self):
print("METHOD")
wrapper = staticmethod(wrapper)
e = Example()
e.method()
This is one way to access(and have used) self from inside a decorator defined inside the same class:
class Thing(object):
def __init__(self, name):
self.name = name
def debug_name(function):
def debug_wrapper(*args):
self = args[0]
print 'self.name = ' + self.name
print 'running function {}()'.format(function.__name__)
function(*args)
print 'self.name = ' + self.name
return debug_wrapper
#debug_name
def set_name(self, new_name):
self.name = new_name
Output (tested on Python 2.7.10):
>>> a = Thing('A')
>>> a.name
'A'
>>> a.set_name('B')
self.name = A
running function set_name()
self.name = B
>>> a.name
'B'
The example above is silly, but it works.
Here's an expansion on Michael Speer's answer to take it a few steps further:
An instance method decorator which takes arguments and acts on a function with arguments and a return value.
class Test(object):
"Prints if x == y. Throws an error otherwise."
def __init__(self, x):
self.x = x
def _outer_decorator(y):
def _decorator(foo):
def magic(self, *args, **kwargs) :
print("start magic")
if self.x == y:
return foo(self, *args, **kwargs)
else:
raise ValueError("x ({}) != y ({})".format(self.x, y))
print("end magic")
return magic
return _decorator
#_outer_decorator(y=3)
def bar(self, *args, **kwargs) :
print("normal call")
print("args: {}".format(args))
print("kwargs: {}".format(kwargs))
return 27
And then
In [2]:
test = Test(3)
test.bar(
13,
'Test',
q=9,
lollipop=[1,2,3]
)
​
start magic
normal call
args: (13, 'Test')
kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
27
In [3]:
test = Test(4)
test.bar(
13,
'Test',
q=9,
lollipop=[1,2,3]
)
​
start magic
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-3-576146b3d37e> in <module>()
4 'Test',
5 q=9,
----> 6 lollipop=[1,2,3]
7 )
<ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
11 return foo(self, *args, **kwargs)
12 else:
---> 13 raise ValueError("x ({}) != y ({})".format(self.x, y))
14 print("end magic")
15 return magic
ValueError: x (4) != y (3)
I found this question while researching a very similar problem. My solution is to split the problem into two parts. First, you need to capture the data that you want to associate with the class methods. In this case, handler_for will associate a Unix command with handler for that command's output.
class OutputAnalysis(object):
"analyze the output of diagnostic commands"
def handler_for(name):
"decorator to associate a function with a command"
def wrapper(func):
func.handler_for = name
return func
return wrapper
# associate mount_p with 'mount_-p.txt'
#handler_for('mount -p')
def mount_p(self, slurped):
pass
Now that we've associated some data with each class method, we need to gather that data and store it in a class attribute.
OutputAnalysis.cmd_handler = {}
for value in OutputAnalysis.__dict__.itervalues():
try:
OutputAnalysis.cmd_handler[value.handler_for] = value
except AttributeError:
pass
I use this type of decorator in some debugging situations, it allows overriding class properties by decorating, without having to find the calling function.
class myclass(object):
def __init__(self):
self.property = "HELLO"
#adecorator(property="GOODBYE")
def method(self):
print self.property
Here is the decorator code
class adecorator (object):
def __init__ (self, *args, **kwargs):
# store arguments passed to the decorator
self.args = args
self.kwargs = kwargs
def __call__(self, func):
def newf(*args, **kwargs):
#the 'self' for a method function is passed as args[0]
slf = args[0]
# replace and store the attributes
saved = {}
for k,v in self.kwargs.items():
if hasattr(slf, k):
saved[k] = getattr(slf,k)
setattr(slf, k, v)
# call the method
ret = func(*args, **kwargs)
#put things back
for k,v in saved.items():
setattr(slf, k, v)
return ret
newf.__doc__ = func.__doc__
return newf
Note: because I've used a class decorator you'll need to use #adecorator() with the brackets on to decorate functions, even if you don't pass any arguments to the decorator class constructor.
The simple way to do it.
All you need is to put the decorator method outside the class.
You can still use it inside.
def my_decorator(func):
#this is the key line. There's the aditional self parameter
def wrap(self, *args, **kwargs):
# you can use self here as if you were inside the class
return func(self, *args, **kwargs)
return wrap
class Test(object):
#my_decorator
def bar(self):
pass
Declare in inner class.
This solution is pretty solid and recommended.
class Test(object):
class Decorators(object):
#staticmethod
def decorator(foo):
def magic(self, *args, **kwargs) :
print("start magic")
foo(self, *args, **kwargs)
print("end magic")
return magic
#Decorators.decorator
def bar( self ) :
print("normal call")
test = Test()
test.bar()
The result:
>>> test = Test()
>>> test.bar()
start magic
normal call
end magic
>>>
Decorators seem better suited to modify the functionality of an entire object (including function objects) versus the functionality of an object method which in general will depend on instance attributes. For example:
def mod_bar(cls):
# returns modified class
def decorate(fcn):
# returns decorated function
def new_fcn(self):
print self.start_str
print fcn(self)
print self.end_str
return new_fcn
cls.bar = decorate(cls.bar)
return cls
#mod_bar
class Test(object):
def __init__(self):
self.start_str = "starting dec"
self.end_str = "ending dec"
def bar(self):
return "bar"
The output is:
>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec
I have a Implementation of Decorators that Might Help
import functools
import datetime
class Decorator(object):
def __init__(self):
pass
def execution_time(func):
#functools.wraps(func)
def wrap(self, *args, **kwargs):
""" Wrapper Function """
start = datetime.datetime.now()
Tem = func(self, *args, **kwargs)
end = datetime.datetime.now()
print("Exection Time:{}".format(end-start))
return Tem
return wrap
class Test(Decorator):
def __init__(self):
self._MethodName = Test.funca.__name__
#Decorator.execution_time
def funca(self):
print("Running Function : {}".format(self._MethodName))
return True
if __name__ == "__main__":
obj = Test()
data = obj.funca()
print(data)
You can decorate the decorator:
import decorator
class Test(object):
#decorator.decorator
def _decorator(foo, self):
foo(self)
#_decorator
def bar(self):
pass

Categories

Resources