python decorator function with arguments

时间:2015-07-28 22:24:34

标签: python decorator

I've read and understood this article about function decorators: https://www.artima.com/weblogs/viewpost.jsp?thread=240845 Specifically I'm talking about the section "Decorator Functions with Decorator Arguments"

I'm running into a problem, though. I'm trying to write a decorator function with arguments to modify arguments into a class constructor. I have two ways to write this.

First some imports:

import scipy.stats as stats
import numpy as np

Way 1 (similar to the aforementioned article's example):

def arg_checker1(these_first_args):
    def check_args(func):
        def wrapped(*args):
            for arg in args[:these_first_args]:
                assert isinstance(arg, np.ndarray) and arg.ndim == 2
            return func(*args) 
        return wrapped
    return check_args

or way 2:

def arg_checker2(these_first_args, func):
    def wrapped(*args):
        for arg in args[:these_first_args]:
            assert isinstance(arg, np.ndarray) and arg.ndim == 2
        return func(*args) 
    return wrapped

I just want an error to be thrown when the first 'these_first_args' to the function aren't 2-d np arrays. But take a look what happens when I try to use it (not with a @ but using it directly as a function)

class PropDens1:
    def __init__(self, samp_fun):
        self.samp = arg_checker1(samp_fun, 2) #right here

class PropDens2:
    def __init__(self, samp_fun):
        self.samp = arg_checker2(2, samp_fun) #right here

q_samp = lambda xnm1, yn, prts : stats.norm.rvs(.9*xnm1,1.,prts)
q1 = PropDens1(q_samp) #TypeError: arg_checker1() takes exactly 1 argument (2 given)
q2 = PropDens2(q_samp) #no error

The second one seems to work with a few examples. Is there a better way to do this, though? If not, why does this work?

I think this is why I don't get it. Here's the example in that linked paper:

def decoratorFunctionWithArguments(arg1, arg2, arg3):
    def wrap(f):
        print "Inside wrap()"
        def wrapped_f(*args):
            print "Inside wrapped_f()"
            print "Decorator arguments:", arg1, arg2, arg3
            f(*args)
            print "After f(*args)"
        return wrapped_f
    return wrap

Why doesn't he actually have to pass the function-to-be-wrapped (f in this case) as an argument into the decoratorFunctionWithArguments()?

1 个答案:

答案 0 :(得分:3)

The exception is telling you exactly what is wrong. You call arg_checker1 with 2 args but you defined it with only one. You should write:

self.samp = arg_checker1(2)(samp_fun)

Which is somewhat equivalent to:

@arg_checker1(2)
def q_samp(...):
    ...

Anyway, you way 1 is the way to go, since it will work fine with the @ syntax.