【问题标题】:Arguments to a decorator without using classes不使用类的装饰器参数
【发布时间】:2018-01-13 06:58:25
【问题描述】:
def tracer(func, enabled=True):
    def wrap(*args, **kwargs):
        if enabled:
            print('Calling {}'.format(func))
        return func(*args, **kwargs)
    return wrap


@tracer(enabled=False)
def rotate_list(l):
    return l[1:] + [l[0]]

我有点困惑为什么这不起作用,特别是这部分:@tracer(enabled=False)

我理解这里发生的是: 每当tracer 执行调用时,函数对象rotate_list 就会作为参数传递。

我认为这不起作用的原因是因为跟踪器(以及与此相关的任何包装器)只接受可调用对象,enabled=False 不是可调用对象,因此它不起作用。

但是错误消息并不能很好地说明这一点,所以我想知道为什么错误消息是: TypeError: tracer() missing 1 required positional argument: 'func'

我猜括号内的参数首先被评估,这样没有可调用的对象被传递给跟踪器?

我想这可以通过使用类装饰器来解决,比如

class Trace:
    def __init__(self, enabled=False):
        print('Inside __init__')
        self.enabled = enabled

那么tracer = Trace(enabled=True) 会起作用,但我想看看如何在不使用类的情况下解决这个问题。

============

编辑(解决方案): 不要介意这个,只需输入它以确保我理解解决方案。 因为在装饰器中放置一个参数使它像一个正常的函数一样工作。解决方案是让装饰器返回另一个可调用对象(这是实际的装饰器)。

喜欢: @dec def foo: pass 将变为 foo = dec(foo)

@dec(ARG) def foo: pass 将变为 foo = dec(ARG)(foo)

解决方案是让dec 返回另一个可调用对象,它是实际的装饰器。例如,该函数将是 wrap

foo = dec(ARG)(foo) 将变为foo = wrap(foo),其中ARG 已传递给dec。 多谢你们!我喜欢函数式编程。

【问题讨论】:

    标签: python class python-decorators


    【解决方案1】:

    要将函数以外的其他参数传递给装饰器函数,您可以嵌套多个 def:

    def my_decorator(flagDoThat)
        def internal(func):
            def wrapper(*argv, **kwv):
                retval = func(*argv, **kwv)
                if flagDoThat:
                    print("Hello", retval)
                return retval
    
            wrapper.__name__ = func.__name__ #update name
            return wrapper
        return internal
    
    @my_decorator(True)
    def my_func(): return "world"
    
    #equals to
    
    tmp = my_decorator(True)
    
    @tmp
    def my_func(): return "world"
    

    编辑

    这个装饰器有助于构建其他装饰器。虽然它包含多个嵌套函数,但它允许您定义只有两个层和参数的装饰器,就像您所做的那样:

    def decorator(keepFunctionName=True):
        def internal(func):
            def newFunc(*argv, **kwv):
                def decoWrapper(theFuncUsedInFunc):
                    fRet = func(theFuncUsedInFunc, *argv, **kwv)
                    if keepFunctionName:
                        fRet.__name__ = theFuncUsedInFunc.__name__
                    return fRet
                return decoWrapper
            return newFunc
        return internal
    

    并且可以这样使用:

    @decorator()
    def my_decorator(func, flagDoThat):
        def wrapper(*argv, **kwv):
            retval = func(*argv, **kwv)
            if flagDoThat:
                print("Hello", retval)
            return retval
    
        return wrapper
    

    这个装饰器的作用与上面的装饰器完全一样。

    编辑二

    您可以通过将装饰器附加到 init 函数来对类执行相同的操作。

    但这里有另一种方法,您可以通过将参数存储在类中来制作带参数的装饰器:

    class my_decorator:
        __slots__ = ["flagDoThat"] # optional line
        def __init__(self, flagDoThat):
            self.flagDoThat = flagDoThat
    
        def __call__(self, func):
            def wrapper(*argv, **kwv):
                retval = func(*argv, **kwv)
                if self.flagDoThat:
                    print("Hello", retval)
                return retval
    
            return wrapper
    

    【讨论】:

      【解决方案2】:

      只能用函数来完成。 tracer 应该是这样的:

      def tracer(enabled=False):
          def wrapper(func):
              def wrap(*args, **kwargs):
                  if enabled:
                      print('Calling {}'.format(func))
                  return func(*args, **kwargs)
              return wrap
          return wrapper
      

      这里所做的是您创建了返回装饰器的函数(跟踪器)。该装饰器接受一个函数。

      如果你将它翻译成与 python 对任何装饰器相同的格式,你就会明白为什么需要这样做。

      每次python看到

      @dec
      def foo(): pass
      

      将其翻译为:

      def foo(): pass
      foo = dec(foo)
      

      所以,当你有需要参数的装饰器时,它会被翻译成这样:

      foo = dec(ARGS)(foo)
      

      所以,你需要做的是确保装饰器返回接受函数作为其参数的东西。

      PS:使用functools.wraps作为装饰器来保存函数名、文档字符串等很好。

      【讨论】:

      • 谢谢,我现在明白了。
      猜你喜欢
      • 2022-08-15
      • 2011-08-02
      • 2011-11-21
      • 1970-01-01
      • 2014-03-24
      • 2020-04-02
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多