装饰器工厂返回 lambda
Decorator factory returning lambda
我遇到了以下代码(这是对真实代码的简化),它结合了装饰器工厂和 lambda 用法:
from functools import wraps
def decorator_factory(arg1):
def decorator(arg2):
return lambda func: real_decorator(arg1, arg2, func)
return decorator
def real_decorator(prefix: str, perm: str, func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result
return wrapper
my_decorator_instance = decorator_factory("r")
class MyClass():
def __init__(self, *args, **kwargs):
pass
@my_decorator_instance('decorator args')
def method_1(self):
print("method_1")
m = MyClass()
m.method_1()
该代码运行良好,但我不太了解其机制。特别是作为装饰器返回时如何使用 lambda 值。请注意 decorator_factory 中没有 no func 参数。 func 是 lambda 的参数。
它只是返回一个匿名函数,而不是使用 def
语句。以下是完全等价的:
def decorator_factory(arg1):
def decorator(arg2):
def _(func):
return real_decorator(arg1, arg2, func)
return _
return decorator
decorator_factory("r")
returns 绑定到名称 my_decorator_instance
的函数; my_decorator_instance('decorator_args')
returns 应用于 method_1
的函数。
为了更好地了解装饰器的工作原理(它们只是应用于其他事物的函数),您也可以编写
class MyClass():
def __init__(self, *args, **kwargs):
pass
@decorator_factory("r")('decorator args')
def method_1(self):
print("method_1")
甚至
class MyClass():
def __init__(self, *args, **kwargs):
pass
method_1 = decorator_factory("r")('decorator args')(lambda self: print("method_1"))
为了更好地了解各种嵌套函数是如何生效的,您可以想象它们在每次函数调用时被“展开”(相应的函数参数被函数参数替换)。所以你的示例代码大致等同于以下内容:
def decorator_factory(arg1):
def decorator(arg2):
return lambda func: real_decorator(arg1, arg2, func)
return decorator
# The following function call effectively "unwraps" the `decorator_factory` function:
# `my_decorator_instance = decorator_factory("r")`
# is equivalent to (`arg1` being replaced by "r"):
def my_decorator_instance(arg2):
return lambda func: real_decorator("r", arg2, func)
# Now unwrapping `my_decorator_instance`:
# `@my_decorator_instance("decorator args")`
# is equivalent to (`arg2` being replaced by "decorator args"):
@(lambda func: real_decorator("r", "decorator args", func))
对于最后一步,即应用装饰器的地方,lambda
函数捕获 already-specified 参数("r"
和 "decorator args"
)并且它还需要注意将 func
参数放在函数调用 real_decorator
.
的正确位置(第三个)
通过将 functools.partial
与 real_decorator
结合使用可以实现类似的效果(即 partial
接替 decorator_factory
的工作):
from functools import partial
[...]
my_decorator_instance = partial(real_decorator, "r")
class MyClass:
@partial(my_decorator_instance, "decorator args")
def method_1(self):
pass
造成混淆的一个可能原因是 decorator_factory
函数不是 装饰器工厂 ,而是 (装饰器工厂) 工厂.
一个装饰器工厂是一个returns一个装饰器的函数(见). For example, functools.lru_cache
是一个装饰器工厂因为它 returns 一个装饰器:
import functools
decorator = functools.lru_cache(maxsize=69)
@decorator
def compute_something_expensive(foo, bar):
...
但是你的函数returns是一个装饰器工厂,而不是装饰器。您可以像这样重构它:
def decorator_factory_factory(arg1):
def decorator_factory(arg2):
def decorator(func):
return real_decorator(arg1, arg2, func)
return decorator
return decorator_factory
你不能像这样使用函数,因为它不是装饰器工厂:
# will not really work:
@decorator_factory_factory('foo')
def method_1(self):
print("method_1")
你必须这样使用它:
@decorator_factory_factory('foo')('bar')
def method_1(self):
print("method_1")
# alternatively:
decorator_factory = decorator_factory_factory('foo')
@decorator_factory('bar')
def method_1(self):
print("method_1")
让我们关注这部分代码:
my_decorator_instance = decorator_factory("r")
class MyClass():
def __init__(self, *args, **kwargs):
pass
@my_decorator_instance('decorator args')
def method_1(self):
print("method_1")
m = MyClass()
m.method_1()
第一行(函数调用)将在 arg1 上应用“r”,同样,装饰器正用于值为“decorator args”的方法,这意味着该值将应用于 arg2 ,然后该方法将应用于 func
参数
请记住,您可以在 python 中的函数调用中使用链接,因此上面的代码等同于:
decorator_factory("r")("decorator args")(method_1)
现在对于装饰器中的 lambda 部分,为了自己简化它,尝试将 lambda 转换为函数,它变成:
def function(func):
return real_decorator(arg1,arg2,func)
所以它没有什么神奇之处。
我遇到了以下代码(这是对真实代码的简化),它结合了装饰器工厂和 lambda 用法:
from functools import wraps
def decorator_factory(arg1):
def decorator(arg2):
return lambda func: real_decorator(arg1, arg2, func)
return decorator
def real_decorator(prefix: str, perm: str, func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result
return wrapper
my_decorator_instance = decorator_factory("r")
class MyClass():
def __init__(self, *args, **kwargs):
pass
@my_decorator_instance('decorator args')
def method_1(self):
print("method_1")
m = MyClass()
m.method_1()
该代码运行良好,但我不太了解其机制。特别是作为装饰器返回时如何使用 lambda 值。请注意 decorator_factory 中没有 no func 参数。 func 是 lambda 的参数。
它只是返回一个匿名函数,而不是使用 def
语句。以下是完全等价的:
def decorator_factory(arg1):
def decorator(arg2):
def _(func):
return real_decorator(arg1, arg2, func)
return _
return decorator
decorator_factory("r")
returns 绑定到名称 my_decorator_instance
的函数; my_decorator_instance('decorator_args')
returns 应用于 method_1
的函数。
为了更好地了解装饰器的工作原理(它们只是应用于其他事物的函数),您也可以编写
class MyClass():
def __init__(self, *args, **kwargs):
pass
@decorator_factory("r")('decorator args')
def method_1(self):
print("method_1")
甚至
class MyClass():
def __init__(self, *args, **kwargs):
pass
method_1 = decorator_factory("r")('decorator args')(lambda self: print("method_1"))
为了更好地了解各种嵌套函数是如何生效的,您可以想象它们在每次函数调用时被“展开”(相应的函数参数被函数参数替换)。所以你的示例代码大致等同于以下内容:
def decorator_factory(arg1):
def decorator(arg2):
return lambda func: real_decorator(arg1, arg2, func)
return decorator
# The following function call effectively "unwraps" the `decorator_factory` function:
# `my_decorator_instance = decorator_factory("r")`
# is equivalent to (`arg1` being replaced by "r"):
def my_decorator_instance(arg2):
return lambda func: real_decorator("r", arg2, func)
# Now unwrapping `my_decorator_instance`:
# `@my_decorator_instance("decorator args")`
# is equivalent to (`arg2` being replaced by "decorator args"):
@(lambda func: real_decorator("r", "decorator args", func))
对于最后一步,即应用装饰器的地方,lambda
函数捕获 already-specified 参数("r"
和 "decorator args"
)并且它还需要注意将 func
参数放在函数调用 real_decorator
.
通过将 functools.partial
与 real_decorator
结合使用可以实现类似的效果(即 partial
接替 decorator_factory
的工作):
from functools import partial
[...]
my_decorator_instance = partial(real_decorator, "r")
class MyClass:
@partial(my_decorator_instance, "decorator args")
def method_1(self):
pass
造成混淆的一个可能原因是 decorator_factory
函数不是 装饰器工厂 ,而是 (装饰器工厂) 工厂.
一个装饰器工厂是一个returns一个装饰器的函数(见functools.lru_cache
是一个装饰器工厂因为它 returns 一个装饰器:
import functools
decorator = functools.lru_cache(maxsize=69)
@decorator
def compute_something_expensive(foo, bar):
...
但是你的函数returns是一个装饰器工厂,而不是装饰器。您可以像这样重构它:
def decorator_factory_factory(arg1):
def decorator_factory(arg2):
def decorator(func):
return real_decorator(arg1, arg2, func)
return decorator
return decorator_factory
你不能像这样使用函数,因为它不是装饰器工厂:
# will not really work:
@decorator_factory_factory('foo')
def method_1(self):
print("method_1")
你必须这样使用它:
@decorator_factory_factory('foo')('bar')
def method_1(self):
print("method_1")
# alternatively:
decorator_factory = decorator_factory_factory('foo')
@decorator_factory('bar')
def method_1(self):
print("method_1")
让我们关注这部分代码:
my_decorator_instance = decorator_factory("r")
class MyClass():
def __init__(self, *args, **kwargs):
pass
@my_decorator_instance('decorator args')
def method_1(self):
print("method_1")
m = MyClass()
m.method_1()
第一行(函数调用)将在 arg1 上应用“r”,同样,装饰器正用于值为“decorator args”的方法,这意味着该值将应用于 arg2 ,然后该方法将应用于 func
参数
请记住,您可以在 python 中的函数调用中使用链接,因此上面的代码等同于:
decorator_factory("r")("decorator args")(method_1)
现在对于装饰器中的 lambda 部分,为了自己简化它,尝试将 lambda 转换为函数,它变成:
def function(func):
return real_decorator(arg1,arg2,func)
所以它没有什么神奇之处。