Python 装饰器是如何工作的?
How does Python decorator work under the hood?
我对 Python 中装饰器内部发生的事情有一些疑问。
- 考虑将总和保存在日志文件中的代码:
def logger(func):
def wrapped(*args, **kwargs):
result = func(*args, **kwargs)
with open('log.txt', 'w') as f:
f.write(str(result))
return result
return wrapped
@logger
def summator(numlist):
return sum(numlist)
print(f"Summator: {summator([1, 2, 3, 4, 5])}")
当我 运行 summator([1, 2, 3, 4, 5, 6])
时它是如何工作的?我假设 logger
发送 wrapped
内的 summator([1, 2, 3, 4, 5, 6])
函数实例,其中执行 summator
函数并且它的结果以某种方式被修改。但是 def logger(func)
对我来说很奇怪:这是否意味着它需要 func
并附加 func
个参数?我看到 logger
本身不接受 * args, ** kwargs
,只有 wrapped
接受...
- 考虑类似的代码:
def logger(filename):
def decorator(func):
def wrapped(*args, **kwargs):
result = func(*args, **kwargs)
with open(filename, 'w') as f:
f.write(str(result))
return result
return wrapped
return decorator
@logger('new_log.txt')
def summator(numlist):
return sum(numlist)
decorator
如何得到func
?它由 logger
包装,只接受文件名。
这是我对装饰器工作原理的简单解释。
你要装饰一个函数,在这里summator
并基本上在不触及它的主体的情况下向它添加一些功能。
表达式:
@logger
def summator(numlist):
return sum(numlist)
相当于:
def summator(numlist):
return sum(numlist)
summator = logger(summator)
@
只是一个语法糖。
发生了什么事?
查看 logger
函数。它有什么作用 ?它需要一个 function 和 returns 另一个 function(此处命名为 wrapped
)。不是那个函数的结果,只是函数本身。
然后您将调用 logger(summator)
的 返回函数 分配给名为 summator
的符号。从现在开始,无论何时调用 summator
,都是在调用 logger
返回的函数。您正在呼叫 wrapped
.
在wrapped
里面有一个func
。它是什么 ?这不是您在上一段中传递给 logger
的原始 summator
函数吗?是的。
问:wrapped
函数怎么访问呢?它不是它的局部变量!
A:因为 wrapped
是一个闭包并且可以访问封闭作用域的局部变量。 read more here
所以wrapped
调用func
,存储结果,将其写入文件然后returns它。
注意:我们通常用这个签名定义我们的wrapped
函数:def wrapped(*args, **kwargs):
因为我们想自由地传递所有参数(包括位置和关键字参数)到 func
函数。归根结底 func
和原来的 summator
是同一个对象。在你的情况下你可以这样写:
def logger(func):
def wrapped(lst): # <--------
result = func(lst) # <--------
with open('log.txt', 'w') as f:
f.write(str(result))
return result
return wrapped
你的第二个问题是指所谓的装饰器工厂。它是 returns 实际装饰器的函数。 问:我们什么时候使用它? A: 当我们想要从装饰器工厂到装饰器或其内部函数的附加参数时。 (在这里,您将 filename
作为参数传递给了装饰器工厂,但在 wrapped
函数中使用了它。)
故事是一样的,它只是我们装饰器之上的另一层。该语法糖的等价物是:
def summator(numlist):
return sum(numlist)
summator = logger('new_log.txt')(summator)
# In two steps, it would be:
# decorator = logger('new_log.txt')
# summator = decorator(summator)
注意:在第一个问题中,name logger
是实际的装饰器(因为它使用 summator
函数进行装饰)但是在第二个问题中它不是。它现在是一个装饰工厂。
wrapped
访问 filename
的方式与我之前提到的访问 func
的方式相同。
了解更多信息 here。
我对 Python 中装饰器内部发生的事情有一些疑问。
- 考虑将总和保存在日志文件中的代码:
def logger(func):
def wrapped(*args, **kwargs):
result = func(*args, **kwargs)
with open('log.txt', 'w') as f:
f.write(str(result))
return result
return wrapped
@logger
def summator(numlist):
return sum(numlist)
print(f"Summator: {summator([1, 2, 3, 4, 5])}")
当我 运行 summator([1, 2, 3, 4, 5, 6])
时它是如何工作的?我假设 logger
发送 wrapped
内的 summator([1, 2, 3, 4, 5, 6])
函数实例,其中执行 summator
函数并且它的结果以某种方式被修改。但是 def logger(func)
对我来说很奇怪:这是否意味着它需要 func
并附加 func
个参数?我看到 logger
本身不接受 * args, ** kwargs
,只有 wrapped
接受...
- 考虑类似的代码:
def logger(filename):
def decorator(func):
def wrapped(*args, **kwargs):
result = func(*args, **kwargs)
with open(filename, 'w') as f:
f.write(str(result))
return result
return wrapped
return decorator
@logger('new_log.txt')
def summator(numlist):
return sum(numlist)
decorator
如何得到func
?它由 logger
包装,只接受文件名。
这是我对装饰器工作原理的简单解释。
你要装饰一个函数,在这里summator
并基本上在不触及它的主体的情况下向它添加一些功能。
表达式:
@logger
def summator(numlist):
return sum(numlist)
相当于:
def summator(numlist):
return sum(numlist)
summator = logger(summator)
@
只是一个语法糖。
发生了什么事?
查看 logger
函数。它有什么作用 ?它需要一个 function 和 returns 另一个 function(此处命名为 wrapped
)。不是那个函数的结果,只是函数本身。
然后您将调用 logger(summator)
的 返回函数 分配给名为 summator
的符号。从现在开始,无论何时调用 summator
,都是在调用 logger
返回的函数。您正在呼叫 wrapped
.
在wrapped
里面有一个func
。它是什么 ?这不是您在上一段中传递给 logger
的原始 summator
函数吗?是的。
问:wrapped
函数怎么访问呢?它不是它的局部变量!
A:因为 wrapped
是一个闭包并且可以访问封闭作用域的局部变量。 read more here
所以wrapped
调用func
,存储结果,将其写入文件然后returns它。
注意:我们通常用这个签名定义我们的wrapped
函数:def wrapped(*args, **kwargs):
因为我们想自由地传递所有参数(包括位置和关键字参数)到 func
函数。归根结底 func
和原来的 summator
是同一个对象。在你的情况下你可以这样写:
def logger(func):
def wrapped(lst): # <--------
result = func(lst) # <--------
with open('log.txt', 'w') as f:
f.write(str(result))
return result
return wrapped
你的第二个问题是指所谓的装饰器工厂。它是 returns 实际装饰器的函数。 问:我们什么时候使用它? A: 当我们想要从装饰器工厂到装饰器或其内部函数的附加参数时。 (在这里,您将 filename
作为参数传递给了装饰器工厂,但在 wrapped
函数中使用了它。)
故事是一样的,它只是我们装饰器之上的另一层。该语法糖的等价物是:
def summator(numlist):
return sum(numlist)
summator = logger('new_log.txt')(summator)
# In two steps, it would be:
# decorator = logger('new_log.txt')
# summator = decorator(summator)
注意:在第一个问题中,name logger
是实际的装饰器(因为它使用 summator
函数进行装饰)但是在第二个问题中它不是。它现在是一个装饰工厂。
wrapped
访问 filename
的方式与我之前提到的访问 func
的方式相同。
了解更多信息 here。