装修合同
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)
合同: 一个以函数作为参数的函数,并且 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)