Python:对装饰器非常困惑

2 投票
5 回答
868 浏览
提问于 2025-04-15 17:49

我原以为我懂装饰器,但现在不太确定了。装饰器是只在函数创建的时候才起作用吗?

我想创建一系列函数,这些函数都有一个必需的参数,叫做'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.

5 个回答

3

这里有一个完整的例子,基于@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'})
5

装饰器只会在一个函数上被调用一次,也就是说,当我们定义这个函数的时候,它会被解析成这样:

@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

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

11

装饰器是在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

撰写回答