先来点补充。
x=1 def foo(): print(x) x=2 foo() 结果: 2
x=1 def foo(): global x x=10000000000000 print(x) foo() print(x) 结果: 10000000000000 10000000000000
x=1 def 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内部的打印 1000000000 f1内部的打印 2 1
def foo(): x = 1 def wrapper(): nonlocal x x = 1000 print(x) return wrapper f = foo() print(f.__closure__) 结果: (<cell at 0x0000000002147498: int object at 0x00000000601CB440>,)
闭包函数
函数内部定义的函数,称为内部函数,该内部函数包含对外部作用域,而不是对全局作用域名字的引用,那么该内部函数称为闭包函数
name='alex' def func(): name='egon' def bar(): print(name) return bar b=func() print(b) 结果: <function func.<locals>.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) 结果: egon 1000000000000000000000
name='alex' def func(): name='egon' x=1000000000000000000000 def bar(): print(name) print(x) a=1 b=2 print(bar.__closure__) func() 结果: (<cell at 0x0000000002167498: str object at 0x00000000021EE030>, <cell at 0x00000000021674C8: int object at 0x00000000021F7350>)
闭包函数的特点:
-
- 自带作用域
- 延迟计算
name='alex' def func(): def bar(): print(name) return bar f=func() print(f.__closure__) f() 结果: None alex
money=1000 def tell_ino(name): print('%s have money %s' %(name,money)) tell_ino('egon') def f1(): money=10 tell_ino('egon') f1() 结果: egon have money 1000 egon have money 1000
money=1000 def 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() 结果: <function wrapper.<locals>.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 wrapper wrapper = aaa() tell_info = wrapper() print(tell_info.__closure__[0].cell_contents) print(tell_info.__closure__[1].cell_contents) 结果: 1000 egon
我们可以看到,wrapper中引用了外围作用域变量name,但name信息存在于wrapper的定义之外。我们称name为wrapper的环境变量;
同样的,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 wrapper wrapper = deco() print(wrapper) 结果: <function deco.<locals>.wrapper at 0x00000000027B0730>
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
from urllib.request import urlopen print(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'xefxbbxbf<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">... b'<!doctype html> <!--[if lt IE 7]> <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]--> <!--[if IE 7]> <html class="no-js ie7 lt-ie8 lt-ie9">... b'xefxbbxbf<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">...
from urllib.request import urlopen def index(url): # url='http://www.xiaohua100.cn/' def get(): return urlopen(url).read() return get xiaohua=index('http://www.xiaohua100.cn/') python=index('https://www.python.org') baidu=index('http://www.baidu.com') print(python()) 结果: b'<!doctype html> <!--[if lt IE 7]> <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]--> <!--[if IE 7]> <html class="no-js ie7 lt-ie8 l t-ie9">...
装饰器
'''
一:开放封闭原则,对扩展是开放的,对修改是封闭的
二:装饰器,装饰器本质可以是任意可调用对象,被装饰的对象也可以是任意
可调用对象,
装饰器的功能是:
在不修改被装饰对象源代码以及调用方式的前提下为期添加新功能
原则:
1.不修改源代码
2.不修改调用方法
目标:添加新功能
'''
import time import 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=wrapper home = timmer(home) index() #wrapper() home() 结果: welcome to index page run time is 1.0000572204589844 welcome to HOME page run time is 1.0000572204589844
#装饰器的语法:在被装饰对象的正上方的单独一行,@装饰器名字
import time import 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 page run time is 4.0002288818359375 welcome to HOME page run time is 1.0000574588775635
#加多个装饰器
定义阶段外部函数的执行顺序是自下而上
调用阶段内部函数的执行顺序是自上而下
import time import 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 def 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=wrapper def index(): # time.sleep(random.randrange(1,5)) time.sleep(3) print('welcome to index page') @timmer @auth def home(): time.sleep(random.randrange(1, 3)) print('welcome to HOME page') index() #deco() home() 结果: name: egon password: 123 login successful welcome to index page run time is 3.000171661376953 name: egon password: 123 login successful welcome to HOME page run time is 5.638322591781616
#装饰器修订
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('welcome to index page') @timmer def home(name): time.sleep(random.randrange(1, 3)) print('welcome to %s HOME page' % name) return 123123123123123123123123123123123123123123 res1 = index() print('index return %s' % res1) res2 = home('egon') #wraper() print('home return %s' % res2) 结果: welcome to index page run time is 1.0000572204589844 index return None welcome to egon HOME page run time is 2.0001142024993896 home 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:UsersAdministratorPycharmProjectsuntitleddecorate2.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:UsersAdministratorPycharmProjectsuntitledcache.txt' # def make_cache(func): # def wrapper(*args,**kwargs): # if os.path.getsize(cache_path): # print('