博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
闭包函数与装饰器
阅读量:5090 次
发布时间:2019-06-13

本文共 6543 字,大约阅读时间需要 21 分钟。

闭包函数与装饰器

一、什么是闭包

闭包:闭是封闭(函数内部函数),包是包含(函数内部函数对外部作用域而非全局作用域的变量的引用)。闭包指的是:函数内部函数对外部作用域的引用。

x = 111  # 全局作用域的变量def outer():    y = 222  # 局部作用域的变量    def inner():        print(x,y)  # 引用了全局和局部作用域的变量    return inner

二、给函数体传值的的两种方式

1、传参

def save_file(filename,content):  # 通过参数给函数体代码传值    with open(filename,'a',encoding='utf-8') as fw:        fw.write(content)    return

2、闭包

def deco():    x = 1   # x的值写死了,无法修改     y = 10  # y的值写死了,无法修改     def my_max():        if x > y:  # 函数内部函数x对外部作用域x=1的引用,就是闭包            return x        return y    return my_maxres = deco()print(res())

# 闭包函数进阶def deco(x,y):  # x,y的值在调用时可以改变。    def my_max():        if x > y:            return x        return y    return my_maxres = deco(4,9)  # 优点一次传参,多次调用。print(res())print(res())

三、闭包函数的应用

闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,先使用自己外部包裹的作用域。

闭包函数,可以是一次传参,多次调用。

应用领域:延迟计算(原来我们是传参,现在我们是包起来)、爬虫领域。

# 使用函数传参实现import requestsurl = 'https://www.baidu.com'url2 = '....'def my_get(url):    response = requests.get(url)    print(response.text)my_get(url)  # 需要时,每次都要定义网址。如果需要爬取的网址比较多,会特别麻烦my_get(url) my_get(url)
# 使用闭包函数实现def deco(url):    def my_get():        response = requests.get(url)        print(response.text)    return my_getmy_jd = deco('http://www.jd.com')  # 一次定义,多次调用调用my_jd()  # 多次调用闭包函数my_jd()my_jd()my_baidu = deco('https://www.baidu.com')my_baidu()my_baidu()my_baidu()my_baidu()

装饰器

什么是装饰器?

器就是一个工具,而程序中的函数就是具备某一功能的工具,所以装饰器就是给被装饰对象添加新的功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加新的功能。

需要注意的是:

  • 装饰器本身其实是任意可调用的对象
  • 被装饰的对象也是任意可调用的对象

为什么要用装饰器?

如果我们上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护必须遵循开封封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能值的是开放的。

装饰器的实现必须遵循两大原则:

​ 1.不修改被装饰对象的源代码

​ 2.不修改被装饰对象的调用方式

怎么用装饰器?

# 提前了解知识# 导入时间模块import timeprint(time.time())  # 时间戳 当前时间距离1970-1-1 00:00:00相差的秒数# 1970-1-1 00:00:00是Unix诞生元年time.sleep(1)  # 睡眠3秒,程序暂停3秒print('FBI warning!')

要实现统计index函数执行的时间

# 源代码import timedef index():    time.sleep(3)    print('澳门最大赌场上线了 性感MM在线发牌')
# 通过修改源代码实现,实现统计index函数执行的时间import timedef index():    time.sleep(3)    print('澳门最大赌场上线了 性感MM在线发牌')start = time.time()index()end = time.time()print(f'index run time {end-start}')
# 通过改变源函数的调用方式实现import timedef index():    time.sleep(3)    print('澳门最大赌场上线了 性感MM在线发牌')    def get_time(func):  # func = index    start = time.time()    func()  # index()    end = time.time()    print(f'index run time {end-start}')get_time(index)  # 通过函数传参的方法实现了,但是改变了源函数的调用方式
# 通过闭包函数实现import timedef index():    time.sleep(3)    print('澳门最大赌场上线了 性感MM在线发牌')def outter(func):  # func = 原始的index    def get_time():  # 无参装饰器        start = time.time()        func()        end = time.time()        print(f'index run time {end-start}')    return get_timeindex = outter(index)  # get_timeindex()  # func()= index()

完善装饰器

上述的装饰器,最后调用index()的时候,其实是在调用get_time(),因此如果原始的index()有返回值的时候,get_time()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和get_time()方法的返回值。

import timedef index():    time.sleep(3)    print('澳门最大赌场上线了 性感MM在线发牌')    return 123def outter(func):    def get_time():        start = time.time()        res = func()        end = time.time()        print(f'index run time {end-start}')        return res    return get_timeindex = outter(index)  # get_timeres = index  # 
.get_time at 0x079D2DB0>print(res)# index()res2 = index()print(res2) # 123澳门最大赌场上线了 性感MM在线发牌index run time 3.000171661376953

如果我们想要该装饰器取装饰一个有参函数

def login(name):    time.sleep(1)    print(f'{name} is sb')    return 'login'

函数参数的问题:

无参函数和有参函数都可以直接使用,函数可以以接收任意数量的参数。

如果原始的login()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于login = outer(login),实际上outer(login)是等于get_time的,所以,要想login()传参,只需要在get_time()中传参即可。get_time(*args,**kwargs),实际上就是将原始的login(name)中的位置实参用元组接收,关键字实参用字典接收,并赋值给args,kwargs,fun(*args,**kwargs)将赋值的args,kwargs即(元组和字典)打散成位置形参传给login的形参。

def index():    time.sleep(3)    print('澳门最大赌场上线了 性感MM在线发牌')    return 'index'# res1 = index()def login(name):    time.sleep(1)    print(f'{name} is sb')    return 'login'# res = login('egon')def outter(func):    def get_time(*args,**kwargs):        start = time.time()        res = func(*args,**kwargs)        end = time.time()        print(f'func run time {end-start}')        return res    return get_timelogin = outter(login)  # login = get_timeres = login('egon')  # login('egon') = get_time('egon')print(res)  # res = 'login'egon is sbfunc run time 1.0000572204589844login

语法糖

import timedef outter(func):    def get_time(*args,**kwargs):        start = time.time()        res = func(*args,**kwargs)        end = time.time()        print(f'func run time {end-start}')        return res    return get_time@outter  # 等同于 index = outter(index)  # index = get_timedef index():    time.sleep(3)    print('澳门最大赌场上线了 性感MM在线发牌')    return 'index'# index = outter(index)  # index = get_time# print(index)index()  # get_time() -> 执行index()@outterdef login(name):    time.sleep(1)    print(f'{name} is sb')    return 'login'login = outter(login)  # login = get_timeprint(login)login('egon')   #  get_time('egon')——>执行真正的login()@outterdef home(*args,**kwargs):    time.sleep(1)    return 'home'home = outter(home)  # home = get_time()print(home)home()  # get_time()——>执行真正的home()

语法糖会将距紧挨着它的可调用对象的名字当做参数自动传入调用outter,自动执行outter函数

@outterdef index()    pass

无参装饰器模板

def outter(func):    def inner(*args,**kwargs):        print('执行被装饰函数之前,你可以做的操作')        res = func(*args,**kwargs)        print('执行被装饰函数之后,你可以做的操作')        return res    return inner
import timeuser_auth = {'is_login':None}def login_deco(func):    def wrapper(*args,**kwargs):        if user_auth['is_login']:  # 已经登录了            res = func(*args,**kwargs)            return res        else:            username = input('请输入你的用户名>>>>>>:').strip()  # 没有登录,执行登录            pwd = input('请输入你的密码>>>>>:').strip()            if username == 'nick'and pwd =='123':                print('恭喜登录成功')                user_auth['is_login'] = True                res = func(*args,**kwargs)                return res            else:                print('账号密码错误')    return wrapper@login_decodef index(name):    time.sleep(0.5)    print(f'{name} is dsb')    return 666@login_decodef home():    time.sleep(0.5)    print('home')    return 9999index('nick')home()

有参装饰器(最复杂就三层)

def sanceng(data):    def deco(func):        def wrapper(*args,**kwargs):            if data == 'file':                # 执行被装饰器函数之前你可以做的操作                res = func(*args,**kwargs)                # 执行被装饰函数之后你可以做的操作                return res        return wrapper    return deco

装饰器在装饰的时候,顺序从下往上。装饰器在执行的时候,顺序从上往下。

转载于:https://www.cnblogs.com/zuihoudebieli/p/11182042.html

你可能感兴趣的文章
C++智能指针
查看>>
2012年总结
查看>>
form表单select联动
查看>>
git 快捷键
查看>>
Airtime 2.2 发布,电台管理软件
查看>>
homework2-st
查看>>
centos 安装python3与Python2并存,并解决"smtplib" object has no attribute 'SMTP_SSL'的错误...
查看>>
启动应用程序时发生了一个错误 终端服务授权错误
查看>>
shell笔记
查看>>
Lucene系列六:Lucene搜索详解(Lucene搜索流程详解、搜索核心API详解、基本查询详解、QueryParser详解)...
查看>>
TCP网络拥塞控制
查看>>
RFID数据清洗与数据清洗的区别
查看>>
JavaSE 1.7 Api 文档下载
查看>>
Trait 关键字实现多继承(使用use 引用)
查看>>
CentOS 安装贴士
查看>>
位运算
查看>>
互联网协议(一)
查看>>
Linux下批量替换文件内容和文件名(转)
查看>>
Jackson错误:Can not deserialize instance of java.lang.String out of START_OBJECT token
查看>>
Maven的构建生命周期理解
查看>>