函数嵌套调用: 再调用一个函数的过程中,又调用了其他的函数
函数的嵌套定义:在一个函数的内部,又定义另外一个函数,函数内部定义的变量,在外部不能被调用
名称空间:一种隔离的概念,专门用来存放名字的地方,准确的说是存放名字与变量值绑定关系的地方,一个内存空间与另一个内存空间完全隔离
python中,有哪些名称空间,名称空间和函数没毛关系
内置名称空间:python自带的名字,在python解释器启动时产生,存放一些python内置的名字
全局名称空间:再执行文件时,存放文件级别定义的名字,没有缩进定义的名字,就是全局的名字
局部名称空间:在执行文件的过程中,如果调用了函数,则会产生该函数的局部名称空间,用来存放该函数内定义的名字
该名字在函数调用时生效,在函数调用结束后失效。也就是函数内定义的名字。
优先掌握
加载顺序: 内置---》全局---》局部
取值顺序: 局部---》全局---》内置 (参照点为局部) ,需要注意的是:在全局无法查看局部的,在局部可以查看全局的,
1、作用域即范围
- 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效 globals()
- 局部范围(局部名称空间属于该范围):临时存活,局部有效 locals()
2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,
global 在局部位置修改全局参数
nonlocal 只在局部生效,在局部位置修改上层参数
优先掌握: 作用域关系,在函数定义时就已经固定,与调用位置无关
再调用函数时,必须必须回到函数原来定义的位置去找作用域关系。
LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间
闭包函数:闭合起来 , 包裹关系,内部函数,包含对外部作用域的一个引用
1、定义在函数内部的函数
2、包含对外部作用域名字的引用,而不是对全局作用域名字的引用,那么该内部函数就称为闭包函数
1 #闭包函数 2 x=1 3 def f1(): 4 x=11111111111 5 def f2(): #定义在函数f1内部的函数 6 print(x) #调用外部作用域的名字x,此x并不是全局下的x 7 return f2 #任何得地方都可以调用,打破只限于局部使用 8 9 func=f1() #func 拿到的是f2内存地址,无论func在哪运行,都以x=1111111111 这个值为准 10 print(func) 11 12 x=100 13 func() 14 print(func()) #空值 15 #f2()称为闭包函数 #结论 <function f1.<locals>.f2 at 0x00000000067FFC80> 11111111111 11111111111 None
闭包函数有什么用
闭包函数应用:延迟计算、惰性计算
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
应用领域:延迟计算(原来我们是传参,现在我们是包起来)
1 #爬网页小程序 2 import requests #导入request模块 3 4 def get(url): #定义一个get函数,参数为url 5 return requests.get(url).text #返回得到网站内容的文本信息 6 print(get('http://www.baidu.com')) 7 8 #方法二: 9 def index(url): #定义页面函数 10 def get(): #定义get函数 11 print(requests.get(url).text) #输出得到的网站内容文本信息 12 return get #返回get信息,即返回网址内容文本信息,使得任意位置可以调用baidu_web = index('http://www.baidu.com') #调用index()函数 13 baidu_web() #执行函数 14 15 #反法三 16 17 def index(url): 18 x = 'xxxx' 19 y = 'yyyy' 20 def wrapper(): 21 x 22 y 23 return requests.get(url).text 24 return wrapper 25 baidu_web = index('http://www.baidu.com') 26 baidu = baidu_web.__closure__ #closure 是内部 闭合 函数 , 生成一个元组, 元组内容是 x y url 的值构成的元素 27 baidu0 = baidu_web.__closure__[0].cell_contents # 查看元组里面的值 28 baidu1 = baidu_web.__closure__[1].cell_contents 29 baidu2 = baidu_web.__closure__[2].cell_contents 30 print(baidu) 31 print(type(baidu)) 32 print(baidu0) 33 print(type(baidu0)) 34 print(baidu1) 35 print(type(baidu1)) 36 print(baidu2) 37 print(type(baidu2)) 38 39 ##结果 40 (<cell at 0x0000000008DF0C48: str object at 0x0000000008A5C5D0>, <cell at 0x0000000008DF0E88: str object at 0x0000000006782D88>, <cell at 0x0000000008DF0B88: str object at 0x0000000006782F10>) 41 <class 'tuple'> 42 http://www.baidu.com 43 <class 'str'> 44 xxxx 45 <class 'str'> 46 yyyy 47 <class 'str'>
装饰器 : (闭包函数的一种应用场景) 装饰他人的工具,装饰器目的是为他人添加新功能
开放封闭原则:对扩展是开放的,对修改是封闭的
装饰器本身可以是任意可调用对象,被装饰的对象本身也可以是任意可调用对象
装饰器所遵循的原则:
1、不修改被装饰对象的源代码
2、不修改被调用对象的调用方式
装饰器的目的:
在循序1和2原则的前提下,为其他新功能函数添加
1 #装饰器 2 #统计函数执行时间,函数执行为源代码,新增统计时间 3 4 #@装饰器名A,必须写在被装饰对象B的正上方,并且是单独一行,把正下方的函数名B当做参数传给@后的函数名A,然后吧返回值在赋值给这个函数B 5 6 import time 7 8 def timmer(func): #装饰器 定义一个timmer函数,参数为 func 9 # func=index 10 def wrapper(): #定义wrapper函数 11 start=time.time() #开始时间 12 func() #运行函数 13 stop=time.time() #结束时间 14 print('run time is %s' %(stop-start)) #统计函数执行时间 15 return wrapper #返回函数内存地址 16 17 18 @timmer # 相当于 index=timmer(index) 19 def index(): # 源代码 定义 index函数 并用 index()方式调用 20 time.sleep(3) 21 print('welcome to index') 22 @timmer # 相当于 home=timmer(home) 23 def home(): ## 源代码 定义 home函数 并用 home()方式调用 24 time.sleep(2) 25 print('welcome to home page') 26 27 #index=timmer(index) 相当于上面的 @timmer 有其一即可 28 #home=timmer(home) 相当于上面的 @timmer 有其一即可 29 30 index() #调用函数 31 home() 32 33 ####结论 #### 34 welcome to index 35 run time is 3.000300168991089 36 welcome to home page 37 run time is 2.000199794769287
上面为无参函数, def index(): def home(): ,如果是有参函数 def home(name):
就需要传参,则
def wrapper(*args, **kwargs): # 以保障可以允许任意场景使用
1 def timmer(func): #装饰器 定义一个timmer函数,参数为 func 2 # func=index 3 def wrapper(*agrs, **kwargs): #定义wrapper函数,添加参数, 4 start=time.time() #开始时间 5 func(*agrs, **kwargs) #运行函数 6 stop=time.time() #结束时间 7 print('run time is %s' %(stop-start)) #统计函数执行时间 8 return wrapper #返回函数内存地址
无参装饰器
1 #无参装饰器 2 #原函数为 index() ,现在添加装饰器,满足登录认证,从文件db.txt中验证账号密码,确认后进入页面 from index 3 4 current_user={'user':None} # 建立记录用户名的字典 5 def auth(func): #定义认证函数,带func参数 6 def wrapper(*args,**kwargs): #定义wrapper函数,可传任意参数 7 if current_user['user']: #判断用户名 8 return func(*args,**kwargs) #返回认证函数 func 9 10 name=input('name: ').strip() #输入用户名 11 password=input('password: ').strip() #输入密码 12 13 with open('db.txt', encoding='utf-8') as f: #打开数据库字典文件 14 user_dic = eval(f.read()) #eval用来执行f.read()读取函数,并把读取的内网返回给user_dic 15 if name in user_dic and password == user_dic[name]: #判断账号密码如果都正确 16 res=func(*args,**kwargs) #执行func函数,并赋值给res 17 current_user['user']=name #将输入的name传给用户字典 18 return res #返回res 内存地址 19 else: 20 print('user or password error') #用户名和密码有错的话,提示有错 21 return wrapper #返回wrapper函数内存地址 22 23 @auth #index=auth(index) index=wrapper #应用装饰器 24 def index(): #定义index函数 25 print('from index') 26 index() #执行index函数
1 #有参装饰器版本 2 #可以通过多个认证方式认证 file MySQL 等等 3 4 current_user={'user':None} 5 def auth(auth_type='file'): 6 def deco(func): 7 def wrapper(*args, **kwargs): 8 if auth_type == 'file': 9 if current_user['user']: 10 return func(*args, **kwargs) 11 name = input('name: ').strip() 12 password = input('password: ').strip() 13 14 with open('db.txt', encoding='utf-8') as f: 15 user_dic = eval(f.read()) 16 if name in user_dic and password == user_dic[name]: 17 res = func(*args, **kwargs) 18 current_user['user'] = name 19 return res 20 else: 21 print('user or password error') 22 elif auth_type == 'mysql': 23 print('mysql') 24 25 elif auth_type == 'ldap': 26 print('ldap') 27 else: 28 print('not valid auth_type') 29 return wrapper 30 return deco 31 @auth(auth_type='mysql') #@deco #index=deco(index) 32 def index(): 33 print('from index') 34 @auth(auth_type='file') 35 def home(name): 36 print('welcome %s' %name) 37 index() #wrapper() 38 home('egon')
装饰器补充内容: ‘’‘ ’‘’ 三引号
查看函数备注可以通过 help 查看 : print(help(func))
1 #未加wraps 时 2 def wrapper(f): #定义修饰函数 3 def wrapper_function(*args, **kwargs): 4 """这个是修饰函数""" 5 return f(*args, **kwargs) 6 return wrapper_function 7 8 @wrapper 9 def wrapped(): #定义被修饰函数 10 """这个是被修饰的函数""" 11 print('wrapped') 12 13 print(wrapped.__doc__) # 输出`这个是修饰函数` 14 print(wrapped.__name__) # 输出`wrapper_function` 15 16 ####结果 17 这个是修饰函数 18 wrapper_function
1 # 添加 wraps 后 2 from functools import wraps 3 4 def wrapper(f): 5 @wraps(f) #在最内层的函数上面添加 6 def wrapper_function(*args, **kwargs): 7 """这个是修饰函数""" 8 return f(*args, **kwargs) 9 return wrapper_function 10 11 @wrapper 12 def wrapped(): 13 """这个是被修饰的函数 14 """ 15 print('wrapped') 16 17 print(wrapped.__doc__) # 输出`这个是被修饰的函数` 18 print(wrapped.__name__) # 输出`wrapped` 19 20 ####结果 21 这个是被修饰的函数 22 wrapped
一个函数头顶上有多个装饰器吗? 可以。
1 ##多个装饰器 2 import time 3 from functools import wraps 4 5 current_user={'user':None} 6 7 def timmer(func): 8 @wraps(func) 9 def wrapper(*args,**kwargs): 10 start=time.time() 11 res=func(*args,**kwargs) 12 stop=time.time() 13 print('run time is %s' %(stop-start)) 14 return res 15 return wrapper 16 def auth(auth_type='file'): 17 def deco(func): 18 def wrapper(*args, **kwargs): 19 if auth_type == 'file': 20 if current_user['user']: 21 return func(*args, **kwargs) 22 name = input('name: ').strip() 23 password = input('password: ').strip() 24 25 with open('db.txt', encoding='utf-8') as f: 26 user_dic = eval(f.read()) 27 if name in user_dic and password == user_dic[name]: 28 res = func(*args, **kwargs) 29 current_user['user'] = name 30 return res 31 else: 32 print('user or password error') 33 elif auth_type == 'mysql': 34 print('mysql') 35 36 elif auth_type == 'ldap': 37 print('ldap') 38 else: 39 print('not valid auth_type') 40 return wrapper 41 return deco 42 43 # 多个装饰器,那个在前先生效那个 44 # 直接修饰正下方的函数 45 @auth() # @deco #index=deco(index) #wrapper 46 @timmer #index=timmer(wrapper) 47 # @auth() # @deco #index=deco(index) #wrapper 48 def index(): 49 '''这是index函数''' 50 time.sleep(3) 51 print('welcome to index') 52 return 123 53 54 # print(index.__doc__) 55 # print(help(index)) 56 57 index() 58 59 60 #####结果 61 name: lalala 62 password: 123 63 welcome to index 64 run time is 3.0002999305725098
####练习题####
一:编写函数,(函数执行的时间是随机的)
二:编写装饰器,为函数加上统计时间的功能
三:编写装饰器,为函数加上认证的功能
四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录
六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
七:为题目五编写装饰器,实现缓存网页内容的功能:
具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中
八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定
注意:时间格式的获取
import time
time.strftime('%Y-%m-%d %X')
######################################
迭代器 (Iterator):是一个重复的过程,每一次重复,都是基于上一次的结果而来
取值:就是一个循环的过程,
依赖于索引的方式迭代取值:字符串 列表 元组
如列表取值:
l=['a','b','c','d']
count=0
while count < len(l):
print(l[count])
count+=1
不依赖于索引的方式取值:字典取值(无索引,非序列类型) 该方式就是迭代器
可迭代对象 iterable: 凡是对象下有 __iter__ 方法: 对象.__iter__ , 该对象就是可迭代对象
# s='hello'
# l=['a','b','c','d']
# t=('a','b','c','d')
# dic={'name':'egon','sex':'m',"age":18}
# set1={1,2,3}
# f=open('db.txt')
# s.__iter__()
# l.__iter__()
# t.__iter__()
# dic.__iter__()
# set1.__iter__()
# f.__iter__()
执行iter的到的是迭代器对象,是一个内存地址
迭代器对象有next方法
使用next方法可以去到字典的key键值
迭代器对象:
#1 有__iter__ , 执行得到仍然是迭代本身,执行 iter之后才会有 next
#2 有__next__ , 一次取一个值
# l=['a','b','c','d'] 列表
# i=iter(l) 迭代器
迭代器对象的优点:
#1:提供了一种统一的,可以不依赖于索引的迭代方式
#2:迭代器本身,比起其他数据类型更省内存 (同一时间只有一个值)
迭代器对象的缺点:
#1:一次性,只能逐步往后取值,不能回退,不如索引取值灵活
#2:无法预知什么时候取值结束,即无法预知长度
文件是迭代器对象,既可以__iter__ , 又可以 __next__
1 #迭代器 2 l=['a','b','c','d'] #列表 3 dic={'name':'lalala','sex':'m',"age":28} #字典 4 iter_l=iter(l) #取值,取的是元素的内存地址 5 iter_dic=iter(dic) #取值,取得是字典key键的内存地址 6 while True: #建立循环 取值 7 try: #消除异常 8 # print(next(iter_l)) #挨个取列表的值 9 # print(next(iter_dic)) #取字典的key值 10 k=next(iter_dic) # 11 print(k,dic[k]) #取字典的key值和value值 12 except StopIteration: #如果出现此异常,则终止循环 13 break 14 15 #### 结果 16 name lalala 17 sex m 18 age 28
for循环原理
1 # for循环原理 实际上就是迭代器 for循环比while循坏更便捷 2 # 先调用__iter__() 方法 3 # 凡是能被for循环执行循环的,都是可迭代器对象 4 l = ['a', 'b' , 'c', 'd',] 5 for item in l : #iter_l = l.__iter__() #从l中逐个取值,赋值给item, 这里的item可以任意定义 6 print(item) 7 8 with open('a.txt') as f: 用with方式打开文件 9 # for line in f: #i=f.__iter__() #for循环文件内容,调用__iter__(),然后执行next,将结果返回给 line line = f.__iter__() 10 # print(line) 11 print(f is f.__iter__()) #常见的类型里面,只有文件是迭代器对象,其余都是可迭代器对象,迭代器对象既要有 __iter__(),又要有 next()
生成器 (generator) : 只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码
生成器就是迭代器,满足迭代器所有的特点,在next()函数才会往下执行
1 #生成器 2 #生成器就是迭代器,因此可以这么取值 3 # res=next(g) 4 # print(res) 5 #yield的功能: 6 # 1 把函数的结果做生迭代器(以一种优雅的方式封装好__iter__,__next__) 7 # 2 函数暂停与再继续运行的状态是由yield保存 8 9 def func(): 10 print('first') # 11 yield 11111111 #碰到 yield赞停,将后面的返回值返回 12 print('second') #再次执行的时候,从yield之后开始执行 13 yield 2222222 14 print('third') 15 yield 33333333 16 print('fourth') 17 18 g=func() #执行该函数 19 # print(g) 20 # next(g) 21 22 # from collections import Iterator 23 # print(isinstance(g,Iterator)) 24 25 # print(next(g)) #可以通过next取值 26 # print('======>') 27 # print(next(g)) 28 # print('======>') 29 # print(next(g)) 30 # print('======>') 31 # print(next(g)) 32 33 for i in g: #i=iter(g) #可以通过for循环取值 34 print(i) 35 36 37 ####结果 38 first 39 11111111 40 second 41 2222222 42 third 43 33333333 44 fourth
1 #用生成器实现range功能 2 # nu = range(1, 10, 2) range(start, stop, [step]) 3 # l1 = list(nu) 4 # t1 = tuple(nu) 5 # print(l1) 6 # print(t1) 7 ###结果 8 #[1, 3, 5, 7, 9] 9 #(1, 3, 5, 7, 9) 10 ########## 11 def range_fun(start, stop, step): 12 while start < stop : 13 yield start 14 start += step 15 nu = range_fun(1, 10, 2) 16 # print(nu) #<generator object range_fun at 0x0000000006B522B0> 17 # print(next(nu)) #1 18 # print(next(nu)) #3 19 20 for i in nu: 21 print(i) 22 23 #####结果 24 1 25 3 26 5 27 7 28 9
实现一个有无穷值的类型:
1 #实现无穷值函数,该函数的值同一时间在内存中只存在一个值,所以不会撑爆内存 2 def func(n): 3 print('=== start ===') 4 while True: #死循环 5 yield n #生成器,遇到yield 暂停,返回n,内存中同一时间只有这一个值 6 n+=1 7 8 g=func(0) #调用函数 9 10 # print(next(g)) 11 # print(next(g)) 12 # print(next(g)) 13 for i in g: #取值 14 print(i)
1 #生成器 实现一个无穷的序列 2 def my_range(start,stop): 3 while True: 4 if start == stop: 5 raise StopIteration #满足条件时,抛出异常。 raise 是自己抛出异常 StopIteration 6 yield start #2 返回 start的值 7 start+=1 #3 返回值+1 8 9 g=my_range(1,3) 10 # 11 # print(next(g)) 12 # print(next(g)) 13 # print(next(g)) 14 15 for i in my_range(5,10): #for循环遇到异常自动停止循环 16 print(i) 17 18 ###结果 19 5 20 6 21 7 22 8 23
#yield与return的比较?
#相同:都有返回值的功能
#不同:return只能返回一次值,而yield可以返回多次值,可以挂起/保存函数的运行状态
1 #生成器实现过滤功能 2 #实现管道符 # python3 tail.py -f access.log | grep 'error' 3 4 import time 5 6 def tail(filepath): 7 with open(filepath, 'r') as f: 8 f.seek(0, 2) 9 while True: 10 line = f.readline() 11 if line: 12 yield line 13 else: 14 time.sleep(0.2) 15 16 17 def grep(pattern,lines): #定义管道函数,参数patteron表示 | 18 for line in lines: 19 if pattern in line: 20 print(line,end='') 21 22 grep('error',tail('access.log')) #读取文件 access.log , 过滤 error , 只显示 error相关