装修合同

Contract of a decorator

合同: 一个以函数作为参数的函数,并且 return 是一个函数[即传递函数的修改(或相同)版本]。传递的函数,这里是,例如square

@floatify
def square(n):
   return n*n

装饰器是否假设 return 只是传递函数的装饰版本,而不是其他?

它应该只是 return 一个函数,但没有什么能阻止您 returning 任何您想要的东西。

>>> def d(x):
...   return "hello"
...
>>> @d
... def f():
...   return "world"
...
>>> f
'hello'
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>>

装饰函数应该 return 一个函数,因为无论它 return 什么都将绑定到原始函数的名称。因此,如果 returned 对象不是函数(或其他可调用的对象,如 class 构造函数或可调用的 class 实例),则相当令人困惑。

一般来说,returned 函数应该有一个与原始函数兼容的函数签名,但我想如果 returned 函数也接受额外的参数也没关系。此外,returned 函数的 return 类型应该与原始函数的类型兼容。

装饰函数有点像原始函数的子class,因此遵守 Liskov substitution principle.

是有意义的

修饰函数可能有副作用:例如,它可以修改一些全局的。这可能很有用,例如用于记录目的; OTOH,功能通常应避免产生副作用。

FWIW,一些标准函数装饰器 return 非函数调用,最常见的可能是 @classmethod.


装饰器并没有什么特别神奇的地方。正如 Jared Goguen 在评论中提到的那样,

@decorator
def some_function(args):
    #etc

等同于

def some_function(args):
    #etc

some_function = decorator(some_function)

第二种形式稍长一些,但功能更强大,因为如果需要,您可以选择将 returned 函数绑定到不同的名称。有些事情可以使用较长的语法轻松完成,如果使用 @ 语法不是完全不可能的话,可能会很困难。

装饰器可以用作

  • 注册装饰器
  • 函数式装饰器
  • 参数化装饰器
  • Class基于装饰器

您查询中的代码是 Functional decorator,它将 decorated function(square) 替换为 wrapper . wrapper 执行以下操作:

  • 调用 f(n)
  • float 应用于 result 并 returns 它。
def floatify(f):
   def wrapper(n):
      result = f(n)
      return float(result)
   return wrapper

功能装饰器的契约:

替换函数通常遵守装饰函数的约定:

  • 接受相同的 number/kinds 参数
  • Return兼容类型的结果

替换函数应该保留来自装饰函数

的元数据
  • 对于调试和其他元编程目的很重要,请使用 @functools.wraps(f)