闭包函数、无参装饰器

一、闭包函数

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

name='alex'     #定义全局变量name='alex'
def func():
    name='egon'  #定义局部变量name='egon'
    def bar():
        print(name)
    return bar    #返回bar函数名

b=func()    #执行func()结果为bar的函数名  相当于b=bar
name='haha'  #重新定义全局变量name='haha'
print(b)    #打印bar
b()       #执行bar(),name='egon'

执行结果: <function func.<locals>.bar at 0x000000000222BAE8> egon
def func():
    name='egon'
    x=1000000000000000000000
    def bar():
        print(name)
        print(x)
    return bar
f=func()
print(f.__closure__)    #打印出闭包函数外层作用域的变量
print(f.__closure__[0].cell_contents)  #打印出闭包函数外层作用域的第一个变量的值
print(f.__closure__[1].cell_contents)  #打印出闭包函数外层作用域的第一个变量的值
运行结果:
(<cell at 0x0000000001E16528: str object at 0x0000000001E993B0>, <cell at 0x0000000001E16558: int object at 0x0000000001EA3C10>)
egon
1000000000000000000000

闭包函数:1 内部函数 2 包含对外部作用域而非全局作用域的引用
闭包函数的特点:
    自带作用域
    延迟计算

以上两个实例都是包一层,闭包函数可以包多层:

#包两层的闭包函数
def func():
    name='egon'     #第2层作用域的变量
    def wrapper():
        money=1000      #第1层作用域的变量
        def tell_info():
            print('my namn is %s' % name)   #使用第2层作用域的变量
            print('egon have money %s' %(money))    #使用第1层作用域的变量
        print(tell_info.__closure__)        #打印闭包函数的变量
        return tell_info
    return wrapper
w=func()
tell_info=w()
print(tell_info.__closure__[0].cell_contents)  #闭包函数变量位置在定义时就定下来了,不因为使用的顺序变化
print(tell_info.__closure__[1].cell_contents)
运行结果:
(<cell at 0x0000000001E16558: int object at 0x000000000048AED0>, <cell at 0x0000000001E16528: str object at 0x0000000001E993B0>)
1000
egon

定义闭包函数的基本形式:

def 外部函数名():
    内部函数需要的变量
    def 内部函数():
        引用外部变量
    return 内部函数

def deco():
    x=1
    def wrapper():
        print(x)
    return wrapper

wrapper=deco()
print(wrapper)
#包两层
def deco1():
    y=2
    def deco():
        x=1
        def wrapper():
            print(x)
            print(y)

        return wrapper
    return deco
deco=deco1()
wrapper=deco()
wrapper()

二、无参装饰器

1、开放封闭原则,对扩展是开放的,对修改是封闭的

2、装饰器,装饰器本质可以任意可调用对象,被装饰的对象也可以是任意
可调用对象,
装饰器的功能是:
在不修改被装饰对象源代码以及调用方式的前提下为期添加新功能

原则:
1.不修改源代码
2.不修改调用方法
目标:添加新功能

无参装饰器=高级函数+函数嵌套

基本框架

#这就是一个实现一个装饰器最基本的架子
def timer(func):
    def wrapper():
        func()
    return wrapper

加上参数

def timer(func):
    def wrapper(*args,**kwargs):
        func(*args,**kwargs)
    return wrapper

加上功能

import time
def timer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)
        stop_time=time.time()
        print('函数[%s],运行时间是[%s]' %(func.__name__,stop_time-start_time))
    return wrapper

加上返回值

import time
def timer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('函数[%s],运行时间是[%s]' %(func.__name__,stop_time-start_time))
        return res
    return wrapper

使用装饰器的方法一:

import time
def timer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('函数[%s],运行时间是[%s]' %(func.__name__,stop_time-start_time))
        return res
    return wrapper

def cal(array):
    res=0
    for i in array:
        res+=i
    return res

cal=timer(cal)
cal(range(100000))

运行结果:
函数[cal],运行时间是[0.0070002079010009766]

使用@装饰器名称的方法:

import time
def timer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('函数[%s],运行时间是[%s]' %(func.__name__,stop_time-start_time))
        return res
    return wrapper
@timer
def cal(array):
    res=0
    for i in array:
        res+=i
    return res

cal(range(100000))
函数[cal],运行时间是[0.007000446319580078]

上课实例:

#装饰器修订
import time
import 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
#被装饰函数

@timmer
def index():
    time.sleep(random.randrange(1,5))
    print('welecome to index page')
@timmer
def home(name):
    time.sleep(random.randrange(1,3))
    print('welecome to %s HOME page' %name)
    return 1231231231

index()

res1=index()
print('index return %s' %res1)
res2=home('egon') #wraper()
print('home return %s' %res2)


运行结果:
welecome to index page
run time is 2.0001144409179688
welecome to index page
run time is 3.001171350479126
index return None
welecome to egon HOME page
run time is 1.0000574588775635
home return 1231231231

 三、课后作业

一:编写函数,(函数执行的时间是随机的)

 1 import time
 2 import random
 3 def pool():
 4     s=1
 5     chi=[]
 6     for i in range(1,random.randrange(2,20)):
 7         s*=i
 8     time.sleep(0.05)
 9         chi.append(i)
10     print('%s的乘积为%d' %(chi,s))
11 pool()
参考答案

二:编写装饰器,为函数加上统计时间的功能

 1 import time
 2 import random
 3 def dec(func):
 4     def war():
 5         start_time=time.time()
 6         func()
 7         stop_time=time.time()
 8         print('%s函数的运行时间为%f'%(func.__name__,stop_time-start_time))
 9     return war
10 def pool():
11     s=1
12     chi=[]
13     for i in range(1,random.randrange(2,20)):
14         s*=i
15         time.sleep(0.05)
16         chi.append(i)
17     print('%s的乘积为%d' %(chi,s))
18 pool=dec(pool)
19 pool()
参考答案

三:编写装饰器,为函数加上认证的功能

 1 import time
 2 import random
 3 def approve(func):
 4     def war():
 5         while True:
 6             name=input('name:')
 7             pwd=input('password:')
 8             if name=='ogen' and pwd=='123456':
 9                 func()
10                 break
11             else:
12                 print('请重新输入')
13                 continue
14     return war
15 
16 def dec(func):
17     def war():
18         start_time=time.time()
19         func()
20         stop_time=time.time()
21         print('%s函数的运行时间为%f'%(func.__name__,stop_time-start_time))
22     return war
23 @approve
24 @dec
25 def pool():
26     s=1
27     chi=[]
28     for i in range(1,random.randrange(2,20)):
29         s*=i
30         time.sleep(0.05)
31         chi.append(i)
32     print('%s的乘积为%d' %(chi,s))
33 pool()
参考答案

四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
    注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
{'name': 'egon', 'password': '123'}

{'name': 'alex', 'password': '123'}

{'name': 'wupeiqi', 'password': '123'}

{'name': 'yuanhao', 'password': '123'}

{'name': 'fengsongtao', 'password': '123'}

 1 import time
 2 import random
 3 tag = True
 4 def approve(func):
 5     def war(*k,**kk):
 6         global tag
 7         app=[]
 8         with open('a.txt', encoding='utf-8') as f:
 9             for line in f:
10                 app.append(eval(line))
11         while tag:
12             name=input('name:')
13             pwd=input('password:')
14             for i in app:
15                 if name == i['name'] and pwd == i['password']:
16                     print('login successful')
17                     func(*k,**kk)
18                     tag=False
19                     break
20             else:continue
21             break
22         else:
23             func(*k, **kk)
24     return war
25 def dec(func):
26     def war(*k,**kk):
27         start_time=time.time()
28         func(*k,**kk)
29         stop_time=time.time()
30         print('%s函数的运行时间为%f'%(func.__name__,stop_time-start_time))
31     return war
32 @approve
33 @dec
34 def pool():
35     s=1
36     chi=[]
37     for i in range(1,random.randrange(2,20)):
38         s*=i
39         time.sleep(0.05)
40         chi.append(i)
41     print('%s的乘积为%d' %(chi,s))
42 @approve
43 @dec
44 def cal(array):
45     array=range(array)
46     res=0
47     for i in array:
48         res+=i
49     print('%s的和为%s'%(array,res))
50     return res
51 pool()
52 cal(10010)
参考答案
 1 db_path=r'E:\PycharmProjects\qz5\db.txt'
 2 user_dic={
 3     'egon':'123',
 4     'alex':'alex3714',
 5     'wupeiqi':'123',
 6     'yunahao':'123',
 7 }
 8 with open(db_path,'w',encoding='utf-8') as f:
 9     f.write(str(user_dic))
10 login_dic={
11     'user':None,
12     'status':False,
13 }
14 def auth(func):
15     def wrapper(*args,**kwargs):
16         with open(db_path,'r',encoding='utf-8') as f:
17             user_dic=eval(f.read())
18         while not login_dic['user'] and not login_dic['status']:
19             name = input('name:')
20             pwd = input('password:')
21             if name in user_dic:
22                 if pwd==user_dic[name]:
23                     print('login successful')
24                     login_dic['user']=name
25                     login_dic['status']=True
26                     res=func(*args,**kwargs)
27                     return res
28                 else:
29                     print('password err')
30                     continue
31             else:print('no user')
32         else:
33             res = func(*args, **kwargs)
34             return res
35     return wrapper
36 @auth
37 def index():
38     print('welcome to index')
39 @auth
40 def home(name):
41     print('welcome %s to home page'%name)
42 index()
43 home('egon')
教师讲解

五:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果

1 from urllib.request import urlopen
2 def get(url):
3     url='http://'+url
4     print(urlopen(url).read())
5     return url
6 url=input('请输入网址:')
7 get(url)
参考答案
1 def get(url):
2     return urlopen(url).read()
3 print(get('https://www.python.org'))
教师讲解

六:为题目五编写装饰器,实现缓存网页内容的功能:
    具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中

 1 from urllib.request import urlopen
 2 def dec(func):
 3     def huan(url):
 4         tag=False     #做一个标志位
 5         with open('url.txt',encoding='utf-8') as f:
 6             for i in f:         #遍历文件的每行
 7                 if i.startswith(url):       #如果是以网址开头
 8                     tag=True                #标志位置为True
 9                     continue               #跳出本次循环
10                 if tag:                     #读取到网址开头行的下一行内容
11                     print(i)                #答应当前行内容
12                     break                  #退出循环
13             else:                          #上面的循环正常结束说明文件中无网址的内容则进行写操作
14                 with open('url.txt', 'a+', encoding='utf-8') as f:
15                     url=func(url)           #运行原函数,打印内容,返回网址
16                     word = urlopen(url).read()
17                     f.write('%s\n%s\n' % (url[7:], word))       #文件写操作
18     return huan
19 @dec
20 def get(url):
21     url='http://'+url
22     print(urlopen(url).read())
23     return url
24 url=input('请输入网址:')
25 get(url)
参考答案
 1 from urllib.request import urlopen
 2 import os
 3 cache_path=r'E:\PycharmProjects\qz5\cache.txt'
 4 def make_cache(func):
 5     def wrapper(*args,**kwargs):
 6         if os.path.getsize(cache_path):
 7             #有缓存
 8             print('\033[45m=======>有缓存\033[0m')
 9             with open(cache_path,'rb') as f:
10                 res=f.read()
11         else:
12             res=func(*args,**kwargs)
13             with open(cache_path,'wb') as f:
14                 f.write(res)
15         return res
16     return wrapper
17 @make_cache
18 def get(url):
19     return urlopen(url).read()
20 print('========>first')
21 print(get('https://www.python.org'))
22 print('========>second')
23 print(get('https://www.python.org'))
24 print('========>third')
25 print(get('https://www.python.org'))
教师讲解

选做内容:

基于上面网页缓存装饰器的基础上,实现缓存不同网页的功能
要求,用户提交的不同url,都能缓存下来,对相同的url发起下载请求,优先从缓存里取内容。

 1 from urllib.request import urlopen
 2 import os
 3 import os.path
 4 files_hash = []     #给缓存文件的文件名哈希值存放在一个列表内
 5 cache_path = r'E:\PycharmProjects\qz5\cache'    #缓存网页的文件夹
 6 for root,dirs,files in os.walk(cache_path):        #遍历文件夹下的内容 root为根目录  dirs为子文件夹  files为文件名
 7     for fn in files:
 8         files_hash.append(hash(fn))     #将文件名遍历出来并将其哈希值存放在列表中
 9 # print(files_hash)
10 def make_cache(func):
11     def wrapper(*args,**kwargs):
12         url=args[0][12:]+'.txt'     #www.baidu.com的网页以baidu.com.txt命名
13         #print(url,type(url))
14         if hash(url) in files_hash and os.path.getsize(root+'\\'+url):  #文件存在且大小不为零
15             #有缓存
16             print('\033[45m=======>有缓存\033[0m')
17             with open(cache_path+'\\'+url,'rb') as f:
18                 res=f.read()
19         else:
20             res=func(*args,**kwargs)
21             with open(cache_path+'\\'+url,'wb') as f:
22                 f.write(res)
23         return res
24     return wrapper
25 @make_cache
26 def get(url):
27     return urlopen(url).read()
28 print(get('https://www.python.org'))
29 print(get('https://www.baidu.com'))
参考答案

七:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作

 1 hanshu={}
 2 def dic(func):
 3     def war(*kwargs):
 4         func(*kwargs)
 5         hanshu[func.__name__]=func
 6     return war
 7 @dic
 8 def pool():
 9     print('fouc1')
10 @dic
11 def get(url):
12     print(url)
13 pool()
14 get('fouc2')
15 print(hanshu)
参考答案
 1 func_dic={}
 2 def deco(key):
 3     def deco2(func):
 4         func_dic[key]=func
 5     return deco2
 6 @deco('f1')
 7 def f1():
 8     print('from f1')
 9 @deco('f2')
10 def f2():
11     print('from f2')
12 @deco('f3')
13 def f3():
14     print('from f3')
15 while True:
16     cmd=input('>>:').strip()
17     if cmd in func_dic:
18         func_dic[cmd]()
19         print(func_dic)
20     print()
教师讲解

 

posted @ 2017-06-14 16:20  maple-shaw  阅读(314)  评论(0编辑  收藏  举报