闭包和装饰器的关系

因为最近想总结一下闭包和装饰器,有点细节总是理不顺,于是找了一下B站上播放量大的,其中一个下面评论很多都说讲的很好,但是我听了一下,关于闭包的地方讲解的就有明显错误。与fluent python和effective python上有矛盾,其实python cookbook上也没说一定是函数作为参数,只是说可以。但是B站有些视频讲解时,竟然说闭包一定是传入的参数是函数,其实这个就差远了。所以大家看一些东西时,最好还是看经典教材,毕竟网上一些讲解的视频,没有经过审核,再者讲解者自身水平参差不齐。

经过总结,我发现装修真的是一件好事,灵活方便。装饰符是通过闭包实现的,可以说是通过闭包实现的,也就是两者之间的关系。

[En]

After summing up, I found that the decorator is really a good thing, flexible and convenient. Decorators are implemented through closures, so to speak, the relationship between the two.

fluent python 2nd中关于闭包的说法。

A closure is a function-let’s call it f – with an extend scope that encompasses variables referenced in the body of f that are not global variables nor local variables of f. Such variables must come from the local scope of an outer function which encompasses f. It does not matter whether the function is anonymous or not; what matters is that it can access nonglobal variables that are defined outside of its body.

闭包是一个函数 f +一个/些变量,这些变量在闭包内引用,但是不是global变量也不是f的局部变量。这些变量必须来自包含函数f的外部函数的局部区域。函数是不是匿名函数无所谓,关键是f可以访问那些定义在 f 外部的非全局变量。书中给了一个图例,很清晰,到底什么是闭包。

闭包和装饰器的关系

从这个图的定义来看,闭包是函数并且这个函数可以访问非global的自由变量。当然一般闭包首先涉及到嵌套函数(函数内有函数),也涉及到高阶函数(传入的参数是函数或者返回值是函数)。但是并不像有些人讲的那样,闭包一定是传入函数。

以下是闭包的典型用法,其中闭包外部的函数没有参数。

[En]

The following is a typical use of closures, where functions outside the closure have no arguments.

python;gutter:true; def make_averager(): count = 0 total = 0 def averager(new_value): nonlocal count,total count += 1 total += new_value return total/count return averager # 返回的是函数,带括号返回的函数运行结果</p> <p>avg = make_averager() # an object of function make_averager print(avg.<strong>name</strong>) # the name is averager avg(10) avg(11) res = avg(13) res = avg(14) print(res)</p> <pre><code> 闭包外部的函数传入的参数也可以是函数。这种形式是许多装饰者的基本形式。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>The argument passed in by a function outside the closure can also be a function. This form is the basic form of many decorators.</font>*</details> ;gutter:true;
def f(n):
return n**2

def make_averager(func):
count = 0
total = 0
def averager(new_value):
nonlocal count,total
count += 1
res = func(new_value)
print(res)
total += res
return total/count
return averager # 返回的是函数,带括号返回的函数运行结果

avg = make_averager(f) # an object of function make_averager
print(avg.__name__) # the name is averager
avg(1)
avg(2)
res = avg(3)
res = avg(4)
print(res)

这个装饰品什么时候用?比如你要做计时统计,要记录日志,这些都是一些常见的函数,可以单独提取和使用,然后打包到其他函数上,这样其他函数的主要函数就既清晰又清晰。并可以实现部分记录和统计的功能。

[En]

When will the decorator be used? For example, you have to do timing statistics, to record logs, these are some common functions, which can be extracted and used separately, and then packaged on other functions, so that the main functions of other functions are both clear and clear. and can achieve some records and statistics of the function.

在谈到闭包时,事实上,从概念上讲,闭包不一定是传入和返回函数,但通常可能会更频繁地使用它们。但如上图所示,您不需要传入函数。因此,不能说闭包中传递的参数是函数,甚至装饰符也不一定是函数。

[En]

When talking about closures, in fact, conceptually, closures are not necessarily incoming and return functions, but they may be used more often in general. But as in the figure above, you don’t need to pass in a function. Therefore, it can not be said that the parameters passed in the closure are functions, even decorators are not necessarily.

那么闭包和装饰器之间是什么关系呢?可以说,修饰器是以闭包的形式实现的。通过下面的例子,我们可以理解闭包和装饰符之间的关系。装饰品真的很好用。从这个层面上,我们可以看出为什么很多博主都说装饰者给功能增加了一个功能,但从功能上来说确实是这样。

[En]

So what is the relationship between closures and decorators? It can be said that decorators are implemented in the form of closures. Through the following examples, we can understand the relationship between closures and decorators. Decorators are really easy to use. From this level, we can see why many bloggers say that the decorator adds a function to the function, but it is indeed so in terms of function.

上面的功能可以是装饰符的形式。

[En]

The above function can be in the form of a decorator.

python;gutter:true; ''' 这样用不好,但没关系。那么,装饰器和闭包之间是什么关系呢?<details><summary><em><font color='gray'>[En]</font></em></summary><em><font color='gray'>It's not good to use it this way, but it's okay. So what is the relationship between decorators and closures?</font></em></details> 有没有一目了然呢? '''</p> <p>def make_averager(func): count = 0 total = 0 def averager(new_value): nonlocal count,total count += 1 res = func(new_value) total += res return total/count return averager # 返回的是函数,带括号返回的函数运行结果</p> <p>@make_averager def f(n): return n**2</p> <p>''' the decorator can be detailed below: def f(n): return n**2 avg = make_averager(f) # an object of function make_averager</p> <p>''' print(f.<strong>name</strong>) f(10) f(20)</p> <pre><code> 因此,让我们来看看装饰者常用的情况,即计时统计和日志记录。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>So let's take a look at the situations commonly used by decorators, that is, timing statistics and logging.</font>*</details> ;gutter:true;
”’
closure
the free vairable is func
here the inner function clocked() can call func and
”’

import time

def clock(func):
def clocked(*args):
t0 = time.perf_counter()
result = func(*args)
elapsed = time.perf_counter() – t0
name = func.__name__
arg_lst = []
if args:
arg_lst.append(‘, ‘.join(repr(arg) for arg in args))
arg_str = ‘, ‘.join(arg_lst)
print(arg_str)
print(f'[{elapsed:0.8f}s] {name}({arg_str}) -> {result!r}’)
return result
return clocked

”’
decorator
”’
@clock
def factorial(n):
return 1 if n < 2 else n*factorial(n-1)
”’
def factorial(n):
return 1 if n < 2 else n*factorial(n-1)
factorial = clock(factorial)
”’

if __name__ == ‘__main__’:
print(‘*’ * 20, ‘Calling factorial(6)’)
print(‘6! =’, factorial(6))

你想实现一个功能,然后想要计时,然后用一个类似上面的装饰器,不想计时,只需要把装饰器注释掉,比如下面的例子,把装饰器注释掉,就是没有计时,没有其他的影响。

[En]

You want to achieve a function, and then want to timing, then use a decorator similar to the above, do not want to time, just need to comment out the decorator, such as the following example, comment out the decorator, that is, there is no timing, nothing else affects.

python;gutter:true; import time</p> <p>def clock(func): def clocked(<em>args): t0 = time.perf_counter() result = func(</em>args) elapsed = time.perf_counter() - t0 name = func.<strong>name</strong> arg_lst = [] if args: arg_lst.append(', '.join(repr(arg) for arg in args)) arg_str = ', '.join(arg_lst) print(arg_str) print(f'[{elapsed:0.8f}s] {name}({arg_str}) -> {result!r}') return result return clocked</p> <p>''' decorator '''</p> <h1>@clock</h1> <p>def factorial(n): return 1 if n < 2 else n<em>factorial(n-1) ''' def factorial(n): return 1 if n < 2 else n</em>factorial(n-1) factorial = clock(factorial) '''</p> <p>if <strong>name</strong> == '<strong>main</strong>': # print('*' * 20, 'Calling factorial(6)') print('6! =', factorial(6)) # 6! = 720</p> <pre><code> 日志记录示例与此类似。如果您想记录日志,请添加一个修饰器,如果您不想使用它,则将其注释掉。<details><summary>*<font color='gray'>[En]</font>*</summary>*<font color='gray'>The logging example is similar. If you want to record a log, add a decorator, and comment it out if you don't want to use it.</font>*</details> ;gutter:true;
from functools import wraps
import logging

def logged(level, name=None, message=None):
"""
Add logging to a function. level is the logging
level, name is the logger name, and message is the
log message. If name and message aren’t specified,
they default to the function’s module and name.

"""
def decorate(func):
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__

@wraps(func)
def wrapper(*args, **kwargs):
log.log(level, logmsg)
return func(*args, **kwargs)
return wrapper
return decorate

Example use
@logged(logging.DEBUG)
def add(x, y):
return x + y

@logged(logging.CRITICAL)
def plus(x, y):
return x * y

@logged(logging.CRITICAL, ‘example’)
def spam():
print(‘Spam log test!’)

spam()

add(3,4)

plus(3,4)

参考:fluent python 2nd, effective python, python cookbook

Original: https://www.cnblogs.com/jianyingzhou/p/15949968.html
Author: caoeryingzi
Title: 闭包和装饰器的关系

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/509908/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球