先来点补充。
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
同样的,tell_info中引用了外围作用域变量money,但money信息存在于tell_info的定义之外。我们称money为tell_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]()