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

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

先来点补充。

x=1def foo():    print(x)x=2foo()结果:2
x=1def foo():    global x    x=10000000000000    print(x)foo()print(x)结果:1000000000000010000000000000
x=1def f1():    x=2    def f2():        x=3        def f3():            # global x            nonlocal x            x=1000000000        f3()        print('f2内部的打印',x)    f2()    print('f1内部的打印', x)f1()print(x)结果:f2内部的打印 1000000000f1内部的打印 21
def foo():    x = 1    def wrapper():        nonlocal x        x = 1000        print(x)    return wrapperf = foo()print(f.__closure__)结果:(
,)

闭包函数

函数内部定义的函数,称为内部函数,该内部函数包含对外部作用域,而不是对全局作用域名字的引用,那么该内部函数称为闭包函数

name='alex'def func():    name='egon'    def bar():        print(name)    return barb=func()print(b)结果:
.bar at 0x0000000001E90730>
name='alex'def func():    name='egon'    x=1000000000000000000000    def bar():        print(name)        print(x)        a=1        b=2    return bar#f=func()print(f.__closure__[0].cell_contents)print(f.__closure__[1].cell_contents)结果:egon1000000000000000000000
name='alex'def func():    name='egon'    x=1000000000000000000000    def bar():        print(name)        print(x)        a=1        b=2    print(bar.__closure__)func()结果:(
,
)

闭包函数的特点:

    • 自带作用域
    • 延迟计算
name='alex'def func():    def bar():        print(name)    return barf=func()print(f.__closure__)f()结果:Nonealex
money=1000def tell_ino(name):    print('%s have money %s' %(name,money))tell_ino('egon')def f1():    money=10    tell_ino('egon')f1()结果:egon have money 1000egon have money 1000
money=1000def f1():    money=10    def tell_ino(name):        print('%s have money %s' %(name,money))    tell_ino('egon')f1()结果:egon have money 10

#包一层

def wrapper():    money=1000    def tell_info():        print('egon have money %s' % money)    return tell_info tell_info=wrapper()print(tell_info)def foo():    money=100    tell_info()foo()结果:
.tell_info at 0x00000000023F0730>egon have money 1000

#包两层

def aaa():    name = 'egon'    def wrapper():        money = 1000        def tell_info():            print('egon have money %s' % money)            print('my name is %s' % name)        return tell_info    return wrapperwrapper = aaa()tell_info = wrapper()print(tell_info.__closure__[0].cell_contents)print(tell_info.__closure__[1].cell_contents)结果:1000egon

我们可以看到,wrapper中引用了外围作用域变量name,但name信息存在于wrapper的定义之外。我们称namewrapper环境变量

同样的,tell_info中引用了外围作用域变量moneymoney信息存在于tell_info的定义之外。我们称moneytell_info环境变量;一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。

在Python中,所谓的闭包是一个含有环境变量的函数对象。环境变量被保存在函数对象的__closure__属性中。
'''

def tell_info(name):    print('%s have money %s' % (name,money))def foo():    money = 100    tell_info('egon')foo()结果:NameError: name 'money' is not defined

报错NameError: name 'money' is not defined

原因:
    函数的作用域关系在函数定义阶段就已经固定,与调用位置无关
    无论函数在何处调用,都需要回到定义阶段去找对应的作用域关系
    此例:虽然tell_info('egon')是在foo内调用并且引用money,但仍需要回到定义
    tell_info的阶段去找作用域关系,而定义时tell_info引用的money就是全局的money
    如果全局不存在则抛出异常NameError
'''
#定义闭包函数的基本形式:
 def 外部函数名():
     内部函数需要的变量
     def 内部函数():
         引用外部变量
     return 内部函数

def deco():    x = 1    def wrapper():        print(x)    return wrapperwrapper = deco()print(wrapper)结果:
.wrapper at 0x00000000027B0730>

 

def deco1():    y = 2    def deco():        x = 1        def wrapper():            print(x)            print(y)        return wrapper    return decodeco = deco1()wrapper = deco()wrapper()结果:12
from urllib.request import urlopenprint(urlopen('http://www.xiaohua100.cn/').read())print(urlopen('https://www.python.org').read())def get(url):    return urlopen(url).read()print(get('http://www.xiaohua100.cn/'))结果:b'\xef\xbb\xbf...b'\n
\n
from urllib.request import urlopendef index(url):    # url='http://www.xiaohua100.cn/'    def get():        return urlopen(url).read()    return getxiaohua=index('http://www.xiaohua100.cn/')python=index('https://www.python.org')baidu=index('http://www.baidu.com')print(python())结果:b'\n
\n

装饰器

'''

一:开放封闭原则,对扩展是开放的,对修改是封闭的
二:装饰器,装饰器本质可以是任意可调用对象,被装饰的对象也可以是任意
    可调用对象,
    装饰器的功能是:
        在不修改被装饰对象源代码以及调用方式的前提下为期添加新功能
        原则:
            1.不修改源代码
            2.不修改调用方法
        目标:添加新功能
'''

import timeimport random#装饰器def timmer(func):    # func=index    def wrapper():        start_time = time.time()        func() #index()        stop_time = time.time()        print('run time is %s' % (stop_time-start_time))    return wrapper#被装饰函数def index():    time.sleep(random.randrange(1, 5))    print('welcome to index page')def home():    time.sleep(random.randrange(1, 3))    print('welcome to HOME page')index = timmer(index) #index=wrapperhome = timmer(home)index() #wrapper()home()结果:welcome to index pagerun time is 1.0000572204589844welcome to HOME pagerun time is 1.0000572204589844

#装饰器的语法:在被装饰对象的正上方的单独一行,@装饰器名字

import timeimport random#装饰器def timmer(func):    def wrapper():        start_time = time.time()        func()        stop_time = time.time()        print('run time is %s' % (stop_time-start_time))    return wrapper#被装饰函数@timmer #index=timmer(index)def index():    time.sleep(random.randrange(1, 5))    print('welcome to index page')@timmer #home=timmer(home)def home():    time.sleep(random.randrange(1, 3))    print('welcome to HOME page')index() #wrapper()home()结果:welcome to index pagerun time is 4.0002288818359375welcome to HOME pagerun time is 1.0000574588775635

#加多个装饰器

定义阶段外部函数的执行顺序是自下而上

调用阶段内部函数的执行顺序是自上而下

import timeimport random#装饰器def timmer(func):    def wrapper():        start_time = time.time()        func()        stop_time = time.time()        print('run time is %s' % (stop_time-start_time))    return wrapperdef auth(func):    def deco():        name = input('name: ')        password = input('password: ')        if name == 'egon' and password == '123':            print('login successful')            func() #wrapper()        else:            print('login err')    return deco#被装饰函数@auth #index=auth(wrapper) #index=deco                      #index=auth(wrapper) #index=deco@timmer #index=timmer(index) #index=wrapperdef index():    # time.sleep(random.randrange(1,5))    time.sleep(3)    print('welcome to index page')@timmer@authdef home():    time.sleep(random.randrange(1, 3))    print('welcome to HOME page')index() #deco()home()结果:name: egonpassword: 123login successfulwelcome to index pagerun time is 3.000171661376953name: egonpassword: 123login successfulwelcome to HOME pagerun time is 5.638322591781616

#装饰器修订

import timeimport random#装饰器def timmer(func):    def wrapper(*args, **kwargs):        start_time = time.time()        res = func(*args, **kwargs)        stop_time = time.time()        print('run time is %s' % (stop_time-start_time))        return res    return wrapper#被装饰函数@timmerdef index():    time.sleep(random.randrange(1, 5))    print('welcome to index page')@timmerdef home(name):    time.sleep(random.randrange(1, 3))    print('welcome to %s HOME page' % name)    return 123123123123123123123123123123123123123123res1 = index()print('index return %s' % res1)res2 = home('egon') #wraper()print('home return %s' % res2)结果:welcome to index pagerun time is 1.0000572204589844index return Nonewelcome to egon HOME pagerun time is 2.0001142024993896home return 123123123123123123123123123123123123123123

 

作业:一:编写函数,(函数执行的时间是随机的)二:编写装饰器,为函数加上统计时间的功能三:编写装饰器,为函数加上认证的功能四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码    注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式五:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果六:为题目五编写装饰器,实现缓存网页内容的功能:    具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中七:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作

 

答案:# 一:编写函数,(函数执行的时间是随机的)# import time# import random# def index():#     time.sleep(random.randrange(1,5))#     print('welcome to index page')## index()# 二:编写装饰器,为函数加上统计时间的功能# import time# import random# def timmer(func):#     # func=index#     def wrapper():#         start_time = time.time()#         func() #index()#         stop_time = time.time()#         print('running time is %s'%(stop_time -start_time ))#     return wrapper## @timmer# def index():#     time.sleep(random.randrange(1,5))#     print('welcome to index page')## index()# 三:编写装饰器,为函数加上认证的功能# import time# import random# def auth(func):#     #func=register#     def wrapper():#         username=input('name: ')#         password=input('password: ')#         if username =='kaylee' and password =='123':#             print('login successful')#             func()#         else:#             print('login error')#     return wrapper## @auth# def register():#     time.sleep(random.randrange(1,5))#     print('welcome to register page')## register()# 四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码#     注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式# user_dic={
'egon':'123',# 'kaylee':'234',# 'lily':'345'}# with open('decorate2.txt','w',encoding='utf-8') as f:# f.write(str(user_dic))# decorate2_path=r'C:\Users\Administrator\PycharmProjects\untitled\decorate2.txt'# # with open('decorate2.txt','r',encoding='utf-8') as f:# # res=f.read()# # user_dic=eval(res)## login_dic={
'user':None,# 'status':False}# def auth(func):# def wrapper(*args,**kwargs):# if login_dic['user'] and login_dic['status']:# res = func(*args, **kwargs)# return res# name=input('name: ')# psw=input('password: ')# with open(decorate2_path,'r',encoding='utf-8') as f:# user_dic=eval(f.read())## if name in user_dic and psw == user_dic[name]:# print('login ok')# login_dic['user']=name# login_dic['status']=True# res=func(*args,**kwargs)# return res# else:# print('login error')## return wrapper## @auth# def index():# print('welcome to index')# @auth# def home(name):# print('welcome to %s home page'%name)## index()# home('egon')# 五:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果# from urllib.request import urlopen## def get(url):# return urlopen(url).read()## print(get('http://www.baidu.com'))# 六:为题目五编写装饰器,实现缓存网页内容的功能:# 具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中# from urllib.request import urlopen# import os## cache_path=r'C:\Users\Administrator\PycharmProjects\untitled\cache.txt'# def make_cache(func):# def wrapper(*args,**kwargs):# if os.path.getsize(cache_path):# print('\033[45m========>有缓存\033[0m')# with open(cache_path,'rb') as f:# res=f.read()# else:# res=func(*args,**kwargs)# with open(cache_path,'wb') as f:# f.write(res)# return res# return wrapper## @make_cache# def get(url):# return urlopen(url).read()## print('=========>first')# print(get('https://www.python.org'))# print('=========>second')# print(get('https://www.python.org'))# print('=========>third')# print(get('https://www.python.org'))#七:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作func_dic={}def deco(key): def deco2(func): func_dic[key]=func return deco2@deco('f1')def f1(): print('from f1')@deco('f2')def f2(): print('from f2')@deco('f3')def f3(): print('from f3')print(func_dic )while True: cmd=input('>>: ').strip() if cmd in func_dic: func_dic[cmd]()

 

转载于:https://www.cnblogs.com/metianzing/p/7009616.html

你可能感兴趣的文章
文字半透明显示在图片上
查看>>
express简单原理
查看>>
ubuntu安装spark on yarn
查看>>
linux网络 (一):网络配置
查看>>
基础练习 十进制转十六进制
查看>>
关于这次软件以及pda终端的培训
查看>>
react 生命周期
查看>>
jQuery上传插件Uploadify 3.2在.NET下的详细例子
查看>>
spring11----基于Schema的AOP
查看>>
解决input框自动填充为黄色的问题
查看>>
音视频基础知识(一)
查看>>
JAVA⑤
查看>>
CyclicBarrier的使用
查看>>
thinkphp的select和find的区别
查看>>
小程序开发笔记
查看>>
Web框架高级功能之模板、拦截器、Json、打包
查看>>
如何辨别一个程序员的水平高低?是靠发量吗?
查看>>
安装scikit-learn过程记录
查看>>
数据库的标识符可以有多长
查看>>
新手村之循环!循环!循环!
查看>>