【问题标题】:What is the equivalent of decorators with arguments without the syntactical-sugar?带参数但没有语法糖的装饰器的等价物是什么?
【发布时间】:2020-07-24 07:58:57
【问题描述】:

我正在学习装饰器,并遇到了一个装饰器进行参数的示例。不过,这对我来说有点令人困惑,因为我了解到(注意:这个问题的示例 大部分来自this article):

def my_decorator(func):
  def inner(*args, **kwargs):
    print('Before function runs')
    func(*args, **kwargs)
    print('After function ran')
  return inner

@my_decorator
def foo(thing_to_print):
  print(thing_to_print)

foo('Hello')
# Returns:
# Before function runs
# Hello
# After function ran

相当于

foo = my_wrapper(foo)

所以,对我来说,如何接受参数是没有意义的,为了更好地解释,这是一个接受参数的装饰器示例:

def repeat(num_times):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat
    return decorator_repeat

@repeat(num_times=4)
def greet(name):
    print(f"Hello {name}")

greet('Bob')
# Returns:
# Hello Bob
# Hello Bob
# Hello Bob
# Hello Bob

所以当我看到这个时,我在想:

greet = repeat(greet, num_times=4)

我知道这是不对的,因为 num_times 是唯一应该通过的参数。那么没有“@-symbol-syntax”的@repeat(num_times=4)的正确等价物是什么?谢谢!

【问题讨论】:

  • 有关装饰器是什么的详细说明,请参阅this answer

标签: python python-decorators


【解决方案1】:

从您的示例代码中,repeat 返回 decorator_repeat 定义。
所以你可以调用decorator_repeat函数并传入greet函数如下:

def greet(name):
    print(f"Hello {name}")

greet = repeat(num_times=4)(greet)

greet('Bob')
# Hello Bob
# Hello Bob
# Hello Bob
# Hello Bob

【讨论】:

    【解决方案2】:

    在这种情况下,它将是:

    greet = repeat(num_times=4)(greet)
    

    这将解释repeat 内的两层嵌套(您需要“两次”调用该函数,您可以说)。 repeat(num_times=4) 返回一个装饰器,然后该装饰器环绕 greet

    【讨论】:

      【解决方案3】:

      那篇文章与我对装饰器的所有了解相同!这个棒极了。关于非@ 符号语法的样子:

      你可以想象实际的装饰器函数是decorator_repeat(func)repeat(num_times=4)中的函数。

      @repeat(num_times=4) 返回一个装饰器,它本质上是@decorator_repeat,除了@decorator_repeat 现在可以访问变量num_times

      在文章页面的下方,它显示了如何使这些参数成为可选参数,这可能有助于您进一步阐明它。

      【讨论】:

      • 哇,谢谢!这是有道理的,括号意味着它将实际调用带有参数num_times = 4 的函数repeat,它返回实际的装饰器。 @ 符号在技术上仍然存在,这意味着返回的装饰器本质上变成了装饰器,但正如你所说,仍然有说它应该运行四次的论点!这澄清了很多,谢谢!
      【解决方案4】:

      如果你使用会更好

      from typing import Callable
      
      def func(*args, **kwargs):
          pass
      
      def repeat(times: int, func: Callable, *args, **kwargs):
          for __ in range(times):
              value = func(*args, **kwargs)
          return value
      
      # usage: repear(4, func, ...)
      

      那么你可以把它变成另一种形式,让repeat返回一个函数:

      def repeat(times: int, func: Callable) -> Callable:
          # the function that you really have to have:
          def inner(*args, **kwargs):
              for __ in times:
                  value = func(*args, **kwargs)
              return valur
          # return the function that you really want
          return inner
      
      # usage: repeat(4, func)(...)
      

      很好,但现在你认为装饰器会更好:

      def repeat(func: Callable) -> Callable:
          # the function that you really have to have:
          def inner(*args, **kwargs):
              for __ in times:
                  value = func(*args, **kwargs)
              return valur
          # return the function that you really want
          return inner
      

      现在你可以像这样使用它了:

      times = 4
      @repeat
      def func(*args, **kwargs):
          pass
      

      但它确实很难看,如何让它成为一个强大的功能?好主意。

      def outer_repeat(times: int):
          return repeat
      

      现在你可以这样工作了:

      @outer_repeat(times = 4)
      def func(*args, **kwargs):
          pass
      

      所以最终的解决方案是这样的:

      def out_repeat(times: int):
          def repeat(func: Callable):
              # the function that you want to have
              @functools.wraps(func)
              def inner(*args, **kwargs):
                  for __ in range(times):
                      value = func(*args, **kwargs)
                  return value
              # return the function that you wanna
              return inner
          return repeat
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-12
        • 2010-11-28
        • 2023-03-17
        • 2020-08-06
        • 1970-01-01
        • 2016-03-16
        • 2012-08-31
        相关资源
        最近更新 更多