Python之装饰器、迭代器和生成器
一、基础准备
1.函数嵌套
- 函数嵌套定义
- 函数嵌套调用
-
1 #函数的嵌套定义:在一个函数的内部,又定义另外一个函数 2 def f1(): 3 print('f1') 4 def f2(): 5 print('f2') 6 f2() 7 f1() 8 f2() #会报错,why?->名称空间和作用域有关
2.名称空间和作用域
什么是名称空间?
存放名字与变量值绑定关系的地方
名称空间的分类
内置名称空间:在python解释器启动时产生,存放一些python内置的名字
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 max=1 2 print(globals()) 3 print(globals()['__builtins__']) 4 print(dir(globals()['__builtins__'])). #dir查看模块的方法 5 '''{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x1005da4a8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/yanlingzhao/Desktop/python/day4/day4/test.py', '__cached__': None, 'max': 1} 6 <module 'builtins' (built-in)> 7 ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'] 8 '''
全局名称空间:在执行文件时产生,存放文件级别定义的名字(除了内置名称空间和函数内定义的变量之外的变量)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 x=1 2 def func(): 3 y=2 4 def f1():pass 5 print 6 class Foo: 7 def f(self): 8 print('test f') 9 if x==1:z=3 10 print(globals()) #查看全局名称空间中的存储内容 11 '''{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000000000215A470>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/study/code/day4/day4/test.py', '__cached__': None, 'x': 1, 'func': <function func at 0x0000000001D53E18>, 'Foo': <class '__main__.Foo'>, 'z': 3} 12 '''
局部名称空间
#局部名称空间:在执行文件的过程中,如果调用了函数,则会产生该函数的局部名称空间
#用来存放该函数内定义的名字,该名字在函数调用时生效,在函数调用结束后失效
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 x=1 2 def func(): 3 y=2 4 def f1():pass 5 print(locals()) #查看局部名称空间内容 6 func() 7 '''{'f1': <function func.<locals>.f1 at 0x00000000026DD9D8>, 'y': 2} 8 ''' 9 10 class Foo: 11 def f(self): 12 print(locals()) 13 '''没有调用函数f,所有不会打印局部名称空间内容'''
名称空间的加载和查找顺序
#加载顺序:内置---》全局---》局部
#优先掌握一:名字的查找顺序是:局部-》全局-》内置
作用域
#作用域:作用的范围,
#全局作用域:全局存活,全局有效:globals()
#局部作用域:临时存活,局部有效:locals()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 max=1 #全局作用域,函数内部和文件级别都可以访问 2 def foo(): 3 max=2 #局部作用域,只能在函数内部访问 4 print(max) 5 foo() 6 print(max) 7 '''2 8 1''' #先找全局max=1的名称空间 9 '''<built-in function max>''' #注释掉max=1时找到内置变量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 x=11111111111111111111111111111111111111111111 2 def f1(): 3 x=1 4 y=2 5 def f2():pass 6 print(locals()) #局部 7 print(globals()) #全局 8 9 f1() 10 print(locals() is globals()) #在文件级别全局就等于局部,从本层开始寻找命名空间 11 print(dir(globals()['__builtins__']))
改变作用域规则的两个关键词global和nonlocal
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #改变作用域规则的两个关键词:global nonlocal 2 x=1 3 def f1(): 4 global x #使用全局变量 5 x=2 6 print(globals()) 7 print(locals()) 8 f1() 9 print(x) 10 '''{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x1007da4a8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/yanlingzhao/Desktop/python/day4/day4/test.py', '__cached__': None, 'x': 2, 'f1': <function f1 at 0x100762e18>} 11 {} 12 2 13 ''' 14 15 16 17 l=[] 18 def f2(): 19 l.append('f2') #列表是可变类型,所以可以在局部改变全局的值 20 21 f2() 22 print(l) 23 24 25 #nonlocal可以改变上一层的局部变量 26 x=0 27 def f1(): 28 x=1 29 def f2(): 30 x=2 31 def f3(): 32 nonlocal x 33 x=3 34 print('f3:%s'%x) 35 f3() 36 print('f2:%s'%x) 37 f2() 38 print('f3:%s'%x) 39 f1() 40 '''f3:3 41 f2:3 42 f3:1'''
函数调用时,必须去函数定义的位置去找作用域关系(作用域关系,在函数定义时已经固定了,与函数调用位置无关)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 x=1 2 def f1(): 3 def f2(): 4 print(x) 5 return f2 6 7 func=f1() 8 x=10000000 9 10 def foo(func): 11 x=300000000 12 func() #f2() 13 foo(func) 14 x=10000000000000000000000
3.闭包函数
函数对象-》打破函数层级调用关系(函数可以作为参数使用)
作用域->在函数定义时,已经决定了作用域跟调用无关
------》》
闭包函数:1.定义在函数内部的函数
2.包含对外部作用域名字的引用
3.而不是对全局作用域名字的引用
则该内部函数被称作闭包函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #闭包函数的应用 2 import requests 3 def index(url): 4 #url = 'www.baidu.com' 5 x=1 6 def wrapper(): 7 print(x) 8 return requests.get(url).text 9 return wrapper 10 baidu_web=index('http://www.baidu.com') #包裹了百度url的wrapper闭包函数 11 netease_web=index('http://www.163.com') #包裹了网易url的wrapper闭包函数 12 print(baidu_web()) #执行闭包函数 13 print(netease_web()) 14 15 print(baidu_web.__closure__[0].cell_contents) #输出结果'''http://www.baidu.com''' 16 print(baidu_web.__closure__[1].cell_contents) #输出结果'''1'''
二、装饰器
1.开放封闭原则:对扩展是开放的,对修改是封闭的
2.什么是装饰器:装饰它人的工具;装饰器本身可以是任意可调用对象,被装饰的对象本身也可以是任意可调用对象
3.装饰器遵循的原则:1)不修改被装饰对象的源代码 2)不修改被装饰对象的调用方式
4.装饰器的目的:在遵循装饰器原则的前提下,为其增加新功能
5.装饰器使用规则:@装饰器名,必须写在装饰器对象的正上方,并且单独一行
代码演示:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!_*_coding=utf-8_*_ 2 import time 3 4 def timmer(fuc): 5 def wrapper(*args,**kwargs): 6 start=time.time() 7 time.sleep(0.3) 8 res=fuc(*args,**kwargs) 9 end=time.time() 10 print('total time:%s'%(end-start)) 11 return res 12 return wrapper 13 14 @timmer #index=timmer(index) 15 def index(): 16 print('wellcome to index') 17 x=1 18 return x 19 @timmer #home=timmer(home) 20 def home(name): 21 print('wellcome to home:%s'%name) 22 23 print(index()) 24 print(home('zyl')) 25 ''' 26 wellcome to index 27 total time:0.303838014603 28 1 29 wellcome to home:zyl 30 total time:0.300426006317 31 None'''
6.装饰器分类:无参装饰器和有参装饰器
无参装饰器实例:
账户认证装饰器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!_*_coding=utf-8_*_ 2 3 ''' 4 {'zyl':'123'} 5 ''' 6 current_user={'user':None} 7 def auth(fuc): 8 def wrapper(*args,**kwargs): 9 if current_user['user']: 10 return fuc(*args,**kwargs) 11 name = input('name: ').strip() 12 password = input('password: ').strip() 13 with open('db.txt', 'r', encoding='utf-8') as f: 14 user_dic = eval(f.read()) 15 if name in user_dic and password == user_dic[name]: 16 res = fuc(*args, **kwargs) 17 current_user['user'] = name 18 else: 19 print('name and pw error') 20 21 22 23 return res 24 return wrapper 25 26 @auth 27 def index(): 28 print('wellcome to my index') 29 30 @auth 31 def home(account): 32 print('to the account:%s'% account) 33 34 index() 35 home('text')
有参装饰器:
1 #!_*_coding=utf-8_*_ 2 3 ''' 4 {'zyl':'123'} 5 ''' 6 current_user={'user':None} 7 def auth(auth_type='file'): 8 def deco(fuc): 9 def wrapper(*args, **kwargs): 10 if auth_type == 'file': 11 if current_user['user']: 12 return fuc(*args, **kwargs) 13 name = input('name: ').strip() 14 password = input('password: ').strip() 15 with open('db.txt', 'r', encoding='utf-8') as f: 16 user_dic = eval(f.read()) 17 if name in user_dic and password == user_dic[name]: 18 res = fuc(*args, **kwargs) 19 current_user['user'] = name 20 return res 21 else: 22 print('name and pw error') 23 24 elif auth_type == 'mysql': 25 print('mysql') 26 else: 27 print('not valid auth_type ') 28 return wrapper 29 return deco 30 31 @auth('mysql') #@deco 32 def index(): 33 print('wellcome to my index') 34 35 @auth() 36 def home(account): 37 print('to the account:%s'% account) 38 39 index() 40 home('text') 41 '''mysql 42 name: zyl 43 password: 123 44 to the account:text 45 '''
继承函数注释内容
from functools import wraps
@wraps(fuc)放到最里面函数定义的上面
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #补充一:wraps 2 3 import time 4 from functools import wraps 5 6 def timmer(func): 7 @wraps(func) 8 def wrapper(*args,**kwargs): 9 start=time.time() 10 res=func(*args,**kwargs) 11 stop=time.time() 12 print('run time is %s' %(stop-start)) 13 return res 14 return wrapper 15 16 17 @timmer # index=timmer(index) 18 def index(): 19 '''这是index函数''' 20 time.sleep(3) 21 print('welcome to index') 22 return 123 23 24 print(index.__doc__) 25 print(help(index))
多层个继承
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #补充二:一个函数头顶上可以多个装饰器 2 import time 3 from functools import wraps 4 current_user={'user':None} 5 6 def timmer(func): 7 @wraps(func) 8 def wrapper(*args,**kwargs): 9 start=time.time() 10 res=func(*args,**kwargs) 11 stop=time.time() 12 print('run time is %s' %(stop-start)) 13 return res 14 return wrapper 15 def auth(auth_type='file'): 16 def deco(func): 17 def wrapper(*args, **kwargs): 18 if auth_type == 'file': 19 if current_user['user']: 20 return func(*args, **kwargs) 21 name = input('name: ').strip() 22 password = input('password: ').strip() 23 24 with open('db.txt', encoding='utf-8') as f: 25 user_dic = eval(f.read()) 26 if name in user_dic and password == user_dic[name]: 27 res = func(*args, **kwargs) 28 current_user['user'] = name 29 return res 30 else: 31 print('user or password error') 32 elif auth_type == 'mysql': 33 print('mysql') 34 35 elif auth_type == 'ldap': 36 print('ldap') 37 else: 38 print('not valid auth_type') 39 return wrapper 40 return deco 41 42 43 44 @timmer #index=timmer(wrapper) 45 @auth() # @deco #index=deco(index) #wrapper 46 def index(): 47 '''这是index函数''' 48 time.sleep(3) 49 print('welcome to index') 50 return 123 51 52 # print(index.__doc__) 53 # print(help(index)) 54 55 index()
三、迭代器
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #迭代:是一个重复的过程,每一次重复,都是基于上一次的结果而来 2 # while True: #单纯的重复 3 # print('你瞅啥') 4 5 # l=['a','b','c','d'] 6 # count=0 7 # while count < len(l): 8 # print(l[count]) 9 # count+=1 10 11 dic={'name':'egon','sex':'m',"age":18} #上述按照索引的取值方式,不适于没有索引的数据类型 12 13 #迭代器: 14 #可迭代对象iterable:凡是对象下有__iter__方法:对象.__iter__,该对象就是可迭代对象 15 # s='hello' 16 # l=['a','b','c','d'] 17 # t=('a','b','c','d') 18 # dic={'name':'egon','sex':'m',"age":18} 19 # set1={1,2,3} 20 # f=open('db.txt') 21 22 # s.__iter__() 23 # l.__iter__() 24 # t.__iter__() 25 # dic.__iter__() 26 # set1.__iter__() 27 # f.__iter__() 28 29 30 #迭代器对象:可迭代对象执行内置的__iter__方法,得到的结果就是迭代器对象 31 32 # dic={'name':'egon','sex':'m',"age":18} 33 # 34 # i=dic.__iter__() 35 # # print(i) #iterator迭代器 36 # 37 # # i.__next__() #next(i) 38 # print(next(i)) 39 # print(next(i)) 40 # print(next(i)) 41 # print(next(i)) #StopIteration 42 # 43 # l=['a','b','c','d'] 44 # 45 # i=l.__iter__() 46 # print(next(i)) 47 # print(next(i)) 48 # print(next(i)) 49 # print(next(i)) 50 # print(next(i)) #StopIteration 51 52 53 54 55 #不依赖于索引的取值方式 56 # l=['a','b','c','d'] 57 # dic={'name':'egon','sex':'m',"age":18} 58 # iter_l=iter(l) 59 # iter_dic=iter(dic) 60 # while True: 61 # try: 62 # # print(next(iter_l)) 63 # k=next(iter_dic) 64 # print(k,dic[k]) 65 # except StopIteration: 66 # break 67 68 69 #什么是迭代器对象: 70 #1 有__iter__,执行得到仍然是迭代本身 71 #2 有__next__ 72 73 74 #迭代器对象的优点 75 #1:提供了一种统一的(不依赖于索引的)迭代方式 76 #2:迭代器本身,比起其他数据类型更省内存 77 # l=['a','b','c','d'] 78 # i=iter(l) 79 80 # dic={'a':1,'b':2} 81 # x=dic.keys() 82 # print(x) 83 # i=x.__iter__() 84 # 85 # with open('a.txt') as f: 86 # # print(next(f)) 87 # # print(next(f)) 88 # # print(next(f)) 89 # f.read() 90 91 92 #迭代器对象的缺点 93 #1:一次性,只能往后走,不能回退,不如索引取值灵活 94 #2:无法预知什么时候取值结束,即无法预知长度 95 # l=['a','b','c','d'] 96 # i=iter(l) 97 # print(next(i)) 98 # print(next(i)) 99 # print(next(i)) 100 101 102 #for循环原理 103 104 # 105 # l=['a','b','c','d'] 106 # for item in l: #iter_l=l.__iter__() 107 # print(item) 108 109 110 # for item in {1,2,3,4}: 111 # print(item) 112 113 114 # with open('a.txt') as f: 115 # # for line in f: #i=f.__iter__() 116 # # print(line) 117 # print(f is f.__iter__()) 118 119 120 121 122 123 #补充:判断可迭代对象与迭代器对象(了解) 124 125 from collections import Iterable,Iterator 126 s='hello' 127 l=['a','b','c','d'] 128 t=('a','b','c','d') 129 dic={'name':'egon','sex':'m',"age":18} 130 set1={1,2,3} 131 f=open('a.txt') 132 133 134 # print(isinstance(s,Iterable)) 135 # print(isinstance(l,Iterable)) 136 # print(isinstance(t,Iterable)) 137 # print(isinstance(dic,Iterable)) 138 # print(isinstance(set1,Iterable)) 139 # print(isinstance(f,Iterable)) 140 141 print(isinstance(s,Iterator)) 142 print(isinstance(l,Iterator)) 143 print(isinstance(t,Iterator)) 144 print(isinstance(dic,Iterator)) 145 print(isinstance(set1,Iterator)) 146 print(isinstance(f,Iterator))