Python:对装饰程序非常困惑

Python:对装饰程序非常困惑,python,decorator,Python,Decorator,我以为我了解装饰师,但现在不了解了。装饰器是否仅在创建函数时工作 我想创建一系列函数,这些函数都有一个名为“ticket_params”的必需参数,这是一个字典。然后用@param_checker(['req_param_1','req_param_2'])之类的东西装饰它们,然后如果字典中没有'req_param_1'和'req_param_2',则生成一个自定义异常子类。我是不是完全错了 在调用代码中可能是这样的: @param_checker(['req_param_1', 'req_pa

我以为我了解装饰师,但现在不了解了。装饰器是否仅在创建函数时工作

我想创建一系列函数,这些函数都有一个名为“ticket_params”的必需参数,这是一个字典。然后用
@param_checker(['req_param_1','req_param_2'])
之类的东西装饰它们,然后如果字典中没有'req_param_1'和'req_param_2',则生成一个自定义异常子类。我是不是完全错了

在调用代码中可能是这样的:

@param_checker(['req_param_1', 'req_param_2'])
def my_decorated_function(params):
    # do stuff

params = {'req_param_1': 'Some Value'}
my_decorated_function(params)

# exception would be raised here from decorator.

在函数上只调用一次装饰符,也就是说,当对
def
语句进行如下解析时:

@mydecorator
def myfunction(): ...
我猜你的意思是这样的:

class param_checker:
  def __init__(self, l):
    self.l = l

  def __call__(self, functionToBeDecorated):
    def wrapper(*args, **kwargs):
      if any(necessary not in kwargs["ticket_params"] for necessary in self.l):
        raise MyCustomException
      return functionToBeDecorated(*args, **kwargs)

    return wrapper

如果你不明白,请告诉我;)

def
语句之后立即应用修饰符;等价物是:

@param_checker(['req_param_1', 'req_param_2'])
def my_decorated_function(params):
    # do stuff
与以下内容完全相同:

def my_decorated_function(params):
    # do stuff
my_decorated_function = param_checker(['req_param_1', 'req_param_2'])(my_decorated_function)
因此,
param_checker
的任务是返回一个函数,该函数将要修饰的函数作为其参数,并返回另一个函数,该函数满足您的要求。到目前为止还好吗

编辑:这里有一个实现…:

import functools

def param_checker(reqs):
  reqs = set(reqs)
  def middling(f):
    @functools.wraps(f)
    def wrapper(params):
      missing = reqs.difference(params)
      if missing:
        raise TypeError('Missing parms: %s' % ', '.join(sorted(missing)))
      return f(params)
    return wrapper
  return middling

下面是一个基于@AndiDog示例的完整示例。记住,任何可调用函数都可以用作装饰器,它不必是类

class MyCustomException(Exception):
    pass

# The decorator - instances of this class are callable as it implements __call__
class param_checker:
    # In this example l is the parameter you pass to the decorator. 
    # For example, l could be ['req_param_1', 'req_param_2'].
    def __init__(self, l):
        self.l = l

    # This makes the instance callable
    def __call__(self, functionToBeDecorated):
        def wrapper(*args, **kwargs):
            # For the successful call below args = () and
            # kwargs = {'ticket_params': {'req_param_1': 'param_1', 'req_param_2': 'param_2'}}
            if "ticket_params" not in kwargs or any(necessary not in kwargs["ticket_params"] for necessary in self.l):
                # if the ticket params parameter has not been specified, or if
                # any of the required parameters are not present raise an exception
                raise MyCustomException
            return functionToBeDecorated(*args, **kwargs)
        return wrapper

@param_checker(['req_param_1', 'req_param_2'])
def myfunction(ticket_params=None): 
    # if the two required params are present this will print
    print "params ", ticket_params

if __name__ == "__main__":
    try:
        myfunction()
    except MyCustomException:
        print "all required params not supplied"
    try:
        myfunction(ticket_params={'req_param_1': 'param_1'})
    except MyCustomException:
        print "all required params not supplied"
    myfunction(ticket_params={'req_param_1': 'param_1', 'req_param_2': 'param_2'})

检查Alex的答案以了解python装饰器;顺便说一下:

1) 你对装饰师有什么不了解?难道您不把装饰器理解为一个一般概念,或者说Python装饰器吗?注意,“经典”装饰器模式、java注释和python装饰器是不同的

2) python decorators应该始终返回一个函数,例如,在代码中,param_checker([…])的返回值应该是一个函数,它接受一个函数作为param(要修饰的函数),并返回一个与my_decordent_函数具有相同签名的函数。看看下面的例子;decorator函数只执行一次(创建类时),而decorated func则在每次调用时执行。在这个特定的示例中,它随后调用原始func,但这不是一个要求

def decorator(orig_func):
    print orig_func

    def decorated(self, a):
        print "aahahah", orig_func(self, a)

    return decorated


class Example(object):
    @decorator
    def do_example(self, a):
        return 2 * a


m = Example()
m.do_example(1)

3) 在使用装饰器的方式中,您可能没有做到最好。当有一些概念与您实际编程的内容非常正交时,通常应该使用它们,并且可以在周围重用它们-这本质上是Python的AOP方式。你的param_checker可能没有那么正交-如果你的decorator只使用了一次,那么它可能根本就不是一个使用decorator的好地方。您的param_checker似乎就是这种情况——它假设修饰过的func接受一个arg,这是一个字典——您的代码中有很多func具有这样的签名和行为吗?如果答案为“否”,只需在函数的开头检查参数,如果缺少,则引发异常。

这是对提出相同问题的人的一个很好的解释:

# This does nothing.

class donothing(object):
    def __init__(self, func):
        """
        The 'func' argument is the function being decorated because in this
        case, we're not instantiating the decorator class. Instead we are just
        using the class object as a callable (a class is always callable as this
        is how an instance is returned) to use as a decorator, which means that
        it is being instantiated upon definition of the decorated function and
        the decorated function is being passed in as an argument to the class's
        __init__ method.
        """
        self.func = func

    def __call__(self, *args, **kwargs):
        """
        The __call__ function is called when the decorated function is called
        because the function has be eaten by the decorator class. Now it's up to
        the this method to return a call to the original function. The arguments
        are passed in as args, kwargs to be manipulated.
        """
        # Returns original function call with original arguments.
        return self.func(*args, **kwargs)

@donothing
def printer(text):
    print(text)

printer('hello world')

# The printer function is now an alias for the donothing instance created, so
# the preceding was the same as:
#
# instance = donothing(printer)
# instance('hello world')
#


# Next example:

class checkforkeysinparams(object):
    def __init__(self, required):
        self.required = set(required)

    def __call__(self, params):
        def wrapper(params):
            missing = self.required.difference(params)
            if missing:
                raise TypeError('Missing from "params" argument: %s' % ', '.join(sorted(missing)))
        return wrapper


# Apply decorator class, passing in the __init__'s 'required' argument.

@checkforkeysinparams(['name', 'pass', 'code'])
def complex_function(params):
    # Obviously these three are needed or a KeyError will be raised.
    print(params['name'])
    print(params['pass'])
    print(params['code'])


# Create params to pass in. Note, I've commented out one of the required params.

params = {
    'name': 'John Doe',
    'pass': 'OpenSesame',
    #'code': '1134',
}

# This call will output: TypeError: Missing from "params" argument: code

complex_function(params=params)

顺便说一句,在你的例子中,做一个简单的断言当然比使用装饰器更容易。这是否意味着每次调用函数时,函数都会被动态重写?我不明白返回“包装器”函数对象如何调用任何东西。这是因为我们不需要呼叫它,因为它正在被呼叫?以前,我会想象返回wrapper(),但现在我可能理解了。还有,如何执行断言。谢谢这个启发性的答案,因为我没有意识到这样做基本上可以覆盖函数上的uuu call uuu()。请注意,装饰器的
\uu call uu
方法在应用于
myfunction
时只调用一次(与
myfunction=param\u checker(myfunction)相同)
其他人已经提到)。因此,
\uuu调用
必须返回将替换
myfunction
的函数。然后,每次调用
myfunction
,都会调用
wrapper
函数。至于断言,在给定的示例中,只编写
assert all(在[req_param_1'、'req_param_2']中必需的params中必需的)作为orokusaki的
my_修饰的_函数的第一行更容易些,然后亚历克斯解释了,我意识到我突然又不明白了。“我现在必须花一个周末来考虑functools和bellybutton lint的组合。@Peter,另外两个答案解释了接受修饰符作为类的论点——将
\uuuuu init\uuuuuu
中的参数作为类,然后在
\uuuu调用中进行装饰的函数。这也没关系,如果实际情况(arg接受修饰符是返回高阶函数的函数——具体来说:返回接受函数并返回函数的函数;-)感觉太奇怪;-)。但是上面的例子非常简单(而且
functools.wrapps
只是在将来的内省中保留修饰函数的名称和docstring的一种巧妙的方法…!)。my_decordented_function=param_checker(['req_param_1','req_param_2'])(my u decordented_函数)。我不明白。将修饰后的函数放在语句后面的parens中会发生什么?它对函数调用的旁边会有什么反应?我知道这不是重点,但理解其中一些是如何分解的非常有趣。@orokusaki,这里的两对括号都表示函数调用。因此
f=deco(parms)(f)
意味着functio
deco
是用参数
parms
调用的,返回一个用参数
f
调用的函数,并返回一个函数(然后由
=
指定给name
f
,表示简单赋值)。这正是(使用
\n
指示换行符,因为注释不能有换行符)
@deco(parms)\ndef(…):