Tom Willis wrote:
Question on decorators in general. Can you parameterize those?

If I wanted to something and after the function call for example, I
would expect something like this would work.

def prepostdecorator(function,pre,post):
    def wrapper(*args,**kwargs):
        pre()
        result =  function(*args,**kwargs)
        post()
        return result
    return wrapper

def dopre():
    print "call pre"

def dopost():
    print "call post"

@prepostdecorator(pre,post)
def sayhello(Name):
    print "Hey %s, nice to meet you" % Name

#sayhello = prepostdecorator(sayhello,dopre,dopost)

if __name__=="__main__":
    sayhello("Dude")

but I get ...
TypeError: prepostdecorator() takes exactly 3 arguments (2 given)

You get this TypeError for the same reason that I get the following TypeError:


py> def prepostdecorator(function, pre, post):
...     pass
...
py> prepostdecorator(1, 2)
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
TypeError: prepostdecorator() takes exactly 3 arguments (2 given)

The expression after @ is a _normal Python expression_. So since you couldn't call prepostdecorator with 2 arguments in any other situation, you still can't.

If you want to call prepostdecorator with 2 arguments, you need to write it this way. A few options:

(1) Use nested functions:

py> def prepostdecorator(pre,post):
...     def decorator(function):
...         def wrapper(*args,**kwargs):
...             pre()
...             result =  function(*args,**kwargs)
...             post()
...             return result
...         return wrapper
...     return decorator
...
py> @prepostdecorator(dopre, dopost)
... def sayhello(name):
...     print "Hey %s, nice to meet you" % name
...
py> sayhello('Tom')
call pre
Hey Tom, nice to meet you
call post

(2) Use functional.partial (PEP 309[1])

py> def prepostdecorator(pre, post, function):
...     def wrapper(*args,**kwargs):
...         pre()
...         result =  function(*args,**kwargs)
...         post()
...         return result
...     return wrapper
...
py> @partial(prepostdecorator, dopre, dopost)
... def sayhello(name):
...     print "Hey %s, nice to meet you" % name
...
py> sayhello('Tom')
call pre
Hey Tom, nice to meet you
call post

(3) Use a class:

py> class prepostdecorator(object):
...     def __init__(self, pre, post):
...         self.pre, self.post = pre, post
...     def __call__(self, function):
...         def wrapper(*args,**kwargs):
...             self.pre()
...             result = self.function(*args,**kwargs)
...             self.post()
...             return result
...         return wrapper
py> @prepostdecorator(dopre, dopost)
... def sayhello(name):
...     print "Hey %s, nice to meet you" % name
...
py> sayhello('Tom')
call pre
Hey Tom, nice to meet you
call post

Note that in all of the above cases, the result of evaluating the expression after the @ is a callable object that takes _exactly one_ argument, the function to be decorated.

HTH,

STeVe

[1] http://www.python.org/peps/pep-0309.html
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to