IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> python闭包与装饰器 -> 正文阅读

[Python知识库]python闭包与装饰器

python闭包与装饰器

一、什么是闭包?

1、函数的作用域

首先我们先明白python的一个变量的作用域,python对于每一个变量都有其的命名空间namespace,一个函数只能调用global全局变量或者是这个函数自己的local局部变量。清楚了这一点后,那什么是闭包呢。

2、什么是闭包?

简单地说,闭包就是由其他函数动态生成并返回的函数,其关键性质是,被返回的函数可以访问其创建者(这里的创建者一般指的是最外层的函数)的局部命名空间中的变量。
综合来说,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是闭包仍然能够保留那些绑定。闭包和普通函数的区别在于:即使创建者已经执行完毕,闭包仍能继续访问其创建者的局部命名空间。

3、闭包的作用

闭包可以避免使用全局值并提供某种形式的数据隐藏。它还可以提供面向对象的解决问题的解决方案。当在类中几乎没有方法(大多数情况下是一种方法)时,闭包可以提供一个替代的和更优雅的解决方案。 但是当属性和方法的数量变大时,更好地实现一个类。

4、举两个闭包的例子

# 闭包:返回函数的函数
# 闭包的作用是被访问的函数可以访问其创建者的局部命名空间内的变量
def make_closure(a):
    def closure(b):
        print('I know the variable a of outer function is %d'%a)
        print('The answer of plus is', a + b)
    return closure

closure = make_closure(10)
closure(3)
I know the variable a of outer function is 10
The answer of plus is 13

这个例子中,我们的闭包可以接受外部函数的一个变量,并且打印它,内部函数暂时不执行任何功能。内部函数在单独执行时,仍然保留了外部函数的自由变量a的绑定,这就是一个简单的闭包。

# 求任意数的任意次方
def make_multiple_of(n):
    def multiple(x):
        print('The times is {}'.format(n))
        return x ** n
    return multiple

time3 = make_multiple_of(3)
time5 = make_multiple_of(5)

print(time3(2))
print(time5(2))
The times is 3
8
The times is 5
32

在这个例子中,multiple函数接受make_multiple_of函数指定的幂,在multiple函数单独执行的时候,仍然保留了make_multiple_of的变量绑定,完成了求次方的功能。

5、闭包的__closure__属性

闭包比普通的函数多了一个 closure 属性,该属性记录着自由变量的地址。

print(time3.__closure__)
(<cell at 0x0000028A02079D00: int object at 0x00007FFC9D241F40>,)

二、装饰器

1、普通的装饰器

实际上,实现特殊方法__call__()的任何对象都被称为可调用。 因此,在最基本的意义上,装饰器是可调用的,并且可以返回可调用。基本上,装饰器接收一个函数,添加一些函数并返回。

写在前面,在python中一切皆对象,函数也不例外,所以我们可以将函数赋值另外的变量,使这两个变量指向同一个函数的引用。

先定义一个简单的装饰器

# 一个装饰器
def make_wrapper(func):
    def wrapper():
        print('func has been decorated')
        func()
    return wrapper

def ordinary():
    print('This is an ordinary function')

为了使用我们定义好的装饰器,我们可以这样调用:

ordinary = make_wrapper(ordinary)

这样我们其实是利用了闭包,打包保留了一些自由变量的绑定,将ordinary函数再赋给本身,但是这时的ordinary函数与一开始的函数多了一些变量的绑定,还是可以像调用之前的ordianry函数一样调用它。

ordinary()
func has been decorated
This is an ordinary function

2、装饰器语法糖

我们可以使用@wrapper_name这样的语法糖来代替ordinary = make_wrapper(ordinary)这样的复杂赋值过程,可以使代码更加简洁。

def make_wrapper(func):
    def wrapper():
        print('func has been decorated')
        func()
    return wrapper

@make_wrapper
def ordinary():
    print('This is an ordinary function')

ordinary()
func has been decorated
This is an ordinary function

3、标准的计时装饰器模板

这里写一个可以计算函数运行时间的装饰器,提供一种写装饰器的思路。

import time
from functools import wraps

def time_wrapper(func):
    @wraps(func)  # 这个wraps装饰器是python内置的,主要用于复制函数名称、注释文档等功能
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print("It takes {:2f} second".format(time.time() - start))
        return result
    return wrapper
    
@time_wrapper
def factorial(n):
# 计算阶乘
    result = 1
    for i in range(1, n + 1):
        result = result * i
    return result

在这里插入图片描述

4、总结

装饰器的典型行为可以概括为,通过闭包,接受并且绑定一些自由变量,把被装饰的函数替换成新的函数,二者接受相同的参数,而且(通常)返回被装饰的函数本该返回的值,同时还进行一些额外的操作。

functools.wraps装饰器可以把被装饰的函数的相关属性从func复制到装饰器中。

三、装饰器工厂函数(参数化装饰器)

Python把被装饰的函数作为第一个参数传给装饰器函数,那怎么让装饰器接受其他的函数呢?答案是:创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。

由于装饰器是带参数的,所以我们再利用语法糖的时候也需要带上参数

一般来说,装饰器需要两层函数嵌套,由于装饰器工厂函数需要接受参数,所以装饰器工厂函数需要三层函数嵌套。

# 一个函数注册装饰器,方便查看注册的函数
registry = set()
def register(active=True):
    def decorator(func):
        print('runing registry(active=%s) -> wrapper(%s)' %(active, func))
        if active:
            registry.add(func)
        else:
            registry.discard(func)

        # 这里才是一个装饰器
        def wrapper(*args, **kwargs):
            print('do something')
            result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@register(active=False)
def func1():
    print('runing func1')

@register(active=True)
def func2():
    print('runing func2')

@register(active=True)
def func3():
    print('runing func3')
runing registry(active=False) -> wrapper(<function func1 at 0x0000028A020AC280>)
runing registry(active=True) -> wrapper(<function func2 at 0x0000028A020AC820>)
runing registry(active=True) -> wrapper(<function func3 at 0x0000028A020ACDC0>)

在这里插入图片描述

查看有哪些函数是注册的,func1是没有注册的,所以结果中没有func1
在这里插入图片描述

四、Reference

http://c.biancheng.net/view/5335.html
https://www.yiibai.com/python/closure.html
https://www.yiibai.com/python/decorator.html
《FluentPYthon》

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:26:58  更:2022-02-26 11:28:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 23:49:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码