Python基础(函数部分)
写在前面
加勒比海盗今天上映!
一、函数的基本概念
- 函数是什么?
函数,就是一个'锤子',一个具有特定功能的'锤子',使用者可以在适当的时候使用这个'锤子',而不用再去从头做一个'锤子';即可以直接拿来使用;
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。
- 不使用函数会有什么问题?
1.组织结构不清晰,可读性差;
2.代码冗余;
3.无法统一管理且维护难度大;
- 函数的分类
1.内置函数;Python解释器自带的具有某些基础功能的'锤子';
2.自定义函数;用户根据需求,自己定义写好的'锤子';
- 函数的两个阶段
1.定义阶段;
2.使用阶段;
二、函数的定义
- 为何要定义函数?
函数即变量,变量必须先定义后使用,未定义而直接引用函数,就相当于在引用一个不存在的变量名,必然会出错;
- 函数的定义都干了什么?
只检测语法,不执行代码;
函数调用阶段才会去执行具体代码;
- 如何定义函数?
1 def functionname( parameters ): 2 "函数_文档字符串" 3 function_suite 4 return [expression]
- 定义函数的三种形式
1.无参函数;
应用场景仅仅只是执行一些操作,比如与用户交互,打印; print()
2.有参函数;
需要根据外部传进来的参数,才能执行相应的逻辑; len(string)
3.空函数;
编写代码之初,用来设计代码结构; def func():pass
- 三元表达式
1 x = 1 2 y = 7 3 res = x if x > y else y 4 print(res) 5 6 --- 7 7
三、函数的调用
- 函数的调用
1.先找到函数名字;
2.根据 名字+() 调用函数,执行函数体内的代码;
1 def foo(): 2 print("From foo func") 3 print(foo) # 打印foo函数代码的内存地址 4 foo() 5 6 --- 7 <function foo at 0x0000000000B5C268> 8 From foo func
- 函数调用的三种形式
1.语句形式:foo()
1 def my_max(x,y): 2 res=x if x >y else y 3 return res 4 5 res = my_max(1,9) 6 print(res) 7 8 --- 9 9
2.表达式形式:3*len('hello')
1 def my_max(x,y): 2 res=x if x >y else y 3 return res 4 5 res = my_max(1,9)*100 6 print(res) 7 8 --- 9 900
3.函数的执行结果当做另外一个函数的参数:range(len('hello'))
1 def my_max(x,y): 2 res=x if x >y else y 3 return res 4 5 res=my_max(my_max(10,20),30) 6 print(res) 7 8 --- 9 30
四、函数的参数
- 函数参数的分类
- 形参
即函数定义处()里的变量标识符;
- 实参
即函数调用处()里的变量;
- 意义
形参即变量名,实参即变量值;函数调用则将值绑定到形参变量上,函数调用结束,解除绑定;
- 具体应用
- 1.位置参数
按照从左到右的顺序定义的参数;位置实参需要按照顺序一一对应给位置形参传值绑定;
1 def foo(name,age): 2 print(name,age) 3 4 foo('alex',20) 5 6 --- 7 alex 20
- 2.关键字参数
按照 key=value 的形式定义实参;位置实参无需按照位置顺序给形参传值;
注意:关键字实参必须在位置实参右面,并且对同一个形参不能重复传值;
1 def foo(name,age): 2 print(name,age) 3 4 foo('alex',age=19) # ok 5 6 foo(age=19,'alex') # SyntaxError: positional argument follows keyword argument 7 8 foo('alex',name='egon') # TypeError: foo() got multiple values for argument 'name'
- 3.默认参数
形参在定义时就已经为其赋值,可以传值也可以不传值;经常需要变的参数定义成位置形参,变化较小的参数定义成默认参数(形参);
注意:
1. 只在定义时赋值一次;
2. 默认参数的定义应该在位置形参右面;
3. 默认参数通常应该定义成不可变类型;
1 def foo(name,gender='male'): 2 print(name,gender) 3 4 foo('alex') 5 foo('kelly','female') 6 7 --- 8 alex male 9 kelly female
- 4.可变长参数
参考:egon函数草稿
针对实参在定义时长度不固定的情况,应该从形参的角度找到可以接收可变长实参的方案,这就是可变长参数(形参);
而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参也应该有两种解决方案,分别是 *args,**kwargs ;
- 按位置的可变长参数示例1:
1 # * 会把溢出的按位置定义的实参都接收,以元组的形式赋值给args,可以通过 *args 得到溢出的各个参数; 2 def foo(x,y,*args): 3 print("x -> {}, y -> {}, args -> {}".format(x,y,args)) 4 print("*args is: ", *args) 5 6 foo(1,2) 7 foo(1,2,3) 8 foo(1,2,[1,2],{'k1':'v1'},10,'AAAAA') 9 10 --- 11 x -> 1, y -> 2, args -> () 12 *args is: 13 x -> 1, y -> 2, args -> (3,) 14 *args is: 3 15 x -> 1, y -> 2, args -> ([1, 2], {'k1': 'v1'}, 10, 'AAAAA') 16 *args is: [1, 2] {'k1': 'v1'} 10 AAAAA
- 按位置的可变长参数示例2:
1 def foo(x, y, *args): 2 print(x, y) 3 print(args) 4 print(*args) 5 6 foo(1, 2, *[3, 4, 5]) 7 8 --- 9 1 2 10 (3, 4, 5) 11 3 4 5
- 按位置的可变长参数示例3:
1 def foo(x, y, z): 2 print(x, y, z) 3 4 foo(100,*(2, 3,)) 5 6 --- 7 100 2 3
- 按位置的可变长参数示例4:
1 def foo(x, y, z): 2 print(x, y, z) 3 4 foo(*(1,2, 3,)) 5 foo(*[1,2, 3,]) 6 7 --- 8 1 2 3 9 1 2 3
- 按位置的可变长参数示例5:
1 def foo(x,y,*args): 2 print("x -> {}, y -> {}, args -> {}".format(x,y,args)) 3 for item in args: 4 print(type(item),item) 5 print('------------------------') 6 7 foo(1,2,[1,2],{'k1':'v1'},10,'AAAAA') 8 9 --- 10 x -> 1, y -> 2, args -> ([1, 2], {'k1': 'v1'}, 10, 'AAAAA') 11 <class 'list'> [1, 2] 12 <class 'dict'> {'k1': 'v1'} 13 <class 'int'> 10 14 <class 'str'> AAAAA 15 ------------------------
- 按关键字的可变长参数示例1:
1 # ** 会把溢出的按关键字定义的实参都接收,以字典的形式赋值给 kwargs 2 def foo(x, y, **kwargs): 3 print(x, y) 4 print(kwargs) 5 print(*kwargs) 6 7 foo(1, y=2, a=1, b=2, c=3) 8 9 --- 10 1 2 11 {'a': 1, 'c': 3, 'b': 2} 12 a c b
- 按关键字的可变长参数示例2:
1 def foo(x, y, **kwargs): 2 print(x, y) 3 print(kwargs) 4 print(*kwargs) 5 6 foo(1, y=2, **{'a': 1, 'b': 2, 'c': 3}) 7 8 --- 9 1 2 10 {'a': 1, 'c': 3, 'b': 2} 11 a c b
- 按关键字的可变长参数示例3:
1 def foo(x, y, z): 2 print("x is %s\ny is %s\nz is %s" % (x, y, z)) 3 4 foo(**{'z': 1, 'x': 2, 'y': 3}) 5 6 --- 7 x is 2 8 y is 3 9 z is 1
- 按关键字的可变长参数示例4:
1 def foo(x, y, z): 2 print("x is %s\ny is %s\nz is %s" % (x, y, z)) 3 4 foo(x=99,**{'z': 1, 'y': 3}) 5 6 --- 7 x is 99 8 y is 3 9 z is 1
- 按关键字的可变长参数示例5:
1 # ** 会把溢出的按关键字定义的实参都接收,以字典的形式赋值给kwargs;可以通过 *kwargs 得到溢出的参数的key值;但不能用 **kwargs 获取其中的值; 2 def foo(x,y,**kwargs): 3 print("x -> {}, y -> {}, kwargs -> {}".format(x, y, kwargs)) 4 print(*kwargs) 5 for key,value in kwargs.items(): 6 print(key,value) 7 print('------------------------') 8 9 foo(1,y=9) 10 foo(x=1,y=9) 11 foo(1,y=9,m=88,n=99) 12 foo(1,y=9,m=[3,4],n={'k1':'v1'},l=(1,2,)) 13 14 --- 15 x -> 1, y -> 9, kwargs -> {} 16 17 ------------------------ 18 x -> 1, y -> 9, kwargs -> {} 19 20 ------------------------ 21 x -> 1, y -> 9, kwargs -> {'n': 99, 'm': 88} 22 n m 23 n 99 24 m 88 25 ------------------------ 26 x -> 1, y -> 9, kwargs -> {'n': {'k1': 'v1'}, 'm': [3, 4], 'l': (1, 2)} 27 n m l 28 n {'k1': 'v1'} 29 m [3, 4] 30 l (1, 2) 31 ------------------------
- 5.命名关键字参数
- * 后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递; 可以保证,传入的参数中一定包含某些关键字;
- 命名关键字参数示例1:
1 def foo(name,age,*,sex='male',height): 2 print(name,age) 3 print(sex) 4 print(height) 5 6 foo('egon',17,height='185') 7 print('---------') 8 foo('egon',17,sex='female',height='185') 9 10 --- 11 egon 17 12 male 13 185 14 --------- 15 egon 17 16 female 17 185
- 命名关键字参数示例2:
1 def foo(x, y, *args, a=1, b, **kwargs): 2 print(x, y) 3 print(args) 4 print(a) 5 print(b) 6 print(kwargs) 7 8 foo(1, 2, 3, 4, 5, b=3, c=4, d=5) 9 10 --- 11 1 2 12 (3, 4, 5) 13 1 14 3 15 {'d': 5, 'c': 4}
- 命名关键字参数示例3:
1 def foo(name,age=10,*args,sex='male',height,**kwargs): 2 print(name) 3 print(age) 4 print(args) 5 print(sex) 6 print(height) 7 print(kwargs) 8 9 foo('alex',1,2,3,4,5,sex='female',height='150',a=1,b=2,c=3) 10 11 --- 12 alex 13 1 14 (2, 3, 4, 5) 15 female 16 150 17 {'c': 3, 'a': 1, 'b': 2}
- 扩展和思考
1 def foo(x,y): 2 print(x,y) 3 4 foo(**{'y': 2, 'x': 100}) # 等价于 foo(x=100,y=2) 5 6 --- 7 100 2
1 def foo(x,y,z): 2 print(x,y,z) 3 4 foo(*[1,2,3]) #foo(1,2,3) 5 foo(*(1,2,3)) #foo(1,2,3) 6 7 --- 8 1 2 3 9 1 2 3
1 def foo(*args): 2 print(args) 3 print(*args) 4 5 foo(1,2,4) 6 7 --- 8 (1, 2, 4) 9 1 2 4
1 def foo(**kwargs): 2 print(kwargs) 3 print(*kwargs) 4 5 foo(x=1,y=['a','b','c'],z='hello') 6 7 --- 8 {'y': ['a', 'b', 'c'], 'x': 1, 'z': 'hello'} 9 y x z
1 def wrapper(*args,**kwargs): 2 print(args) 3 print(kwargs) 4 5 wrapper(1,2,3,a=1,b=2) 6 7 --- 8 (1, 2, 3) 9 {'a': 1, 'b': 2}
五、函数对象
- 1 可以被引用
1 def foo(): 2 print('from foo') 3 4 func=foo 5 print(foo) 6 print(func) 7 func() 8 9 --- 10 <function foo at 0x0000000000B6C268> 11 <function foo at 0x0000000000B6C268> 12 from foo
- 2 可以当作参数传递
1 def foo(): 2 print('from foo') 3 4 def bar(func): 5 print(func) 6 func() 7 8 bar(foo) 9 10 --- 11 <function foo at 0x000000000072C268> 12 from foo
- 3 返回值可以是函数
1 def foo(): 2 print('from foo') 3 def bar(func): 4 return func 5 6 f=bar(foo) 7 print(f) 8 f() 9 10 --- 11 <function foo at 0x00000000010DC268> 12 from foo
- 4 可以当作容器类型的元素
1 # 可以当做容器类的元素 2 def select(): 3 print('---->>> select') 4 def update(): 5 print('---->>> update') 6 def delete(): 7 print('---->>> delete') 8 def insert(): 9 print('---->>> insert') 10 11 func_dict = { 12 'select':select, 13 'update':update, 14 'delete':delete, 15 'insert':insert 16 } 17 18 def main(): 19 while True: 20 sql = input(">>> ").strip() 21 if not sql: 22 continue 23 elif 'Q' == sql.upper(): 24 print('Bye...') 25 exit(0) 26 else: 27 cmd_list = sql.split() 28 if cmd_list[0] in func_dict: 29 func_dict[cmd_list[0]]() 30 else: 31 print('Input invalid') 32 33 main()
六、函数的嵌套
- 嵌套调用
1 def max2(x,y): 2 return x if x > y else y 3 def max4(a,b,c,d): 4 res1=max2(a,b) 5 res2=max2(res1,c) 6 res3=max2(res2,d) 7 return res3 8 9 print(max4(10,99,31,22)) 10 11 --- 12 99
- 嵌套定义
1 def f1(): 2 print('from f1') 3 def f2(): 4 print('from f2') 5 def f3(): 6 print('from f3') 7 f3() 8 f2() 9 10 f1() 11 12 --- 13 from f1 14 from f2 15 from f3
七、命名空间和作用域
- 命名空间
- 1.内置名称空间:随着Python解释器的启动而产生;会一直保留,直到Python解释器退出;
1 print(max([1,2,3]))
1 import builtins 2 for i in dir(builtins): 3 print(i)
1 ArithmeticError 2 AssertionError 3 AttributeError 4 BaseException 5 BlockingIOError 6 BrokenPipeError 7 BufferError 8 BytesWarning 9 ChildProcessError 10 ConnectionAbortedError 11 ConnectionError 12 ConnectionRefusedError 13 ConnectionResetError 14 DeprecationWarning 15 EOFError 16 Ellipsis 17 EnvironmentError 18 Exception 19 False 20 FileExistsError 21 FileNotFoundError 22 FloatingPointError 23 FutureWarning 24 GeneratorExit 25 IOError 26 ImportError 27 ImportWarning 28 IndentationError 29 IndexError 30 InterruptedError 31 IsADirectoryError 32 KeyError 33 KeyboardInterrupt 34 LookupError 35 MemoryError 36 NameError 37 None 38 NotADirectoryError 39 NotImplemented 40 NotImplementedError 41 OSError 42 OverflowError 43 PendingDeprecationWarning 44 PermissionError 45 ProcessLookupError 46 RecursionError 47 ReferenceError 48 ResourceWarning 49 RuntimeError 50 RuntimeWarning 51 StopAsyncIteration 52 StopIteration 53 SyntaxError 54 SyntaxWarning 55 SystemError 56 SystemExit 57 TabError 58 TimeoutError 59 True 60 TypeError 61 UnboundLocalError 62 UnicodeDecodeError 63 UnicodeEncodeError 64 UnicodeError 65 UnicodeTranslateError 66 UnicodeWarning 67 UserWarning 68 ValueError 69 Warning 70 WindowsError 71 ZeroDivisionError 72 __build_class__ 73 __debug__ 74 __doc__ 75 __import__ 76 __loader__ 77 __name__ 78 __package__ 79 __spec__ 80 abs 81 all 82 any 83 ascii 84 bin 85 bool 86 bytearray 87 bytes 88 callable 89 chr 90 classmethod 91 compile 92 complex 93 copyright 94 credits 95 delattr 96 dict 97 dir 98 divmod 99 enumerate 100 eval 101 exec 102 exit 103 filter 104 float 105 format 106 frozenset 107 getattr 108 globals 109 hasattr 110 hash 111 help 112 hex 113 id 114 input 115 int 116 isinstance 117 issubclass 118 iter 119 len 120 license 121 list 122 locals 123 map 124 max 125 memoryview 126 min 127 next 128 object 129 oct 130 open 131 ord 132 pow 133 print 134 property 135 quit 136 range 137 repr 138 reversed 139 round 140 set 141 setattr 142 slice 143 sorted 144 staticmethod 145 str 146 sum 147 super 148 tuple 149 type 150 vars 151 zip
- 2.全局名称空间:文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入该空间;在文件/模块被Python解释器读入时创建,通常也会一直保存到解释器退出;
- 3.局部名称空间:调用函数时会产生局部名称空间,只在函数调用时临时绑定,调用结束解绑定;
1 info = "Adress : " 2 def func_father(country): 3 def func_son(area): 4 city= "Shanghai " #此处的city变量,覆盖了父函数的city变量 5 print(info + country + city + area) 6 city = " Beijing " 7 #调用内部函数 8 func_son("ChaoYang "); 9 10 func_father("China ") 11 12 --- 13 Adress : China Shanghai ChaoYang
- 作用域
- 1.全局作用域
包含内置名称空间和全局名称空间;
查看全局作用域内的名字:gloabls()
生命周期:全局有效,在任何位置都能被访问到,除非del删掉,否则会一直存活到文件执行完毕;
- 2.局部作用域
只包含局部名称空间;
查看局局作用域内的名字:locals()
生命周期:局部有效,只能在局部范围调用,只在函数调用时才有效,调用结束就失效;
- 3.名字的查找顺序
1 LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__ 2 3 1.locals 是函数内的名字空间,包括局部变量和形参 # 局部名称空间 4 2.enclosing 外部嵌套函数的名字空间(闭包中常见) 5 3.globals 全局变量,函数定义所在模块的名字空间 # 全局名称空间 6 4.builtins 内置模块的名字空间 # 内置名称空间
如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name 'aa' is not defined;
示例:
1 x=1000 2 def func(): 3 x=2 4 y=('a','b',) 5 print(x,y) 6 print(globals()) 7 print(locals()) 8 func() 9 10 --- 11 2 ('a', 'b') 12 {'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000565C88>, 'func': <function func at 0x00000000006EC268>, '__file__': 'D:/soft/work/Python_17/day04/functions.py', '__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__doc__': None, '__cached__': None, '__package__': None, 'x': 1000, '__spec__': None} 13 {'y': ('a', 'b'), 'x': 2}
八、闭包的概念
- 闭包的定义
1. 定义在函数内部的函数;
2. 该内部函数包含对外部作用域而不是对全局作用域的引用;
- 扩展:
__closure__
属性定义的是一个包含 cell 对象的元组,其中元组中的每一个 cell 对象用来保存作用域中变量的值;
1 def f1(): 2 x = 1 3 user_list = ['alex','egon'] 4 def f2(): 5 print(x) 6 print(user_list) 7 return f2 8 9 f = f1() # 返回一个闭包函数对象:即f2; 10 print(f) 11 print(f.__closure__) # 查看该闭包函数所包含的元素; 12 for item in f.__closure__: 13 print(item) 14 print(item.cell_contents) # 打印出每一个元素的值 15 16 --- 17 <function f1.<locals>.f2 at 0x00000000006CC400> 18 (<cell at 0x0000000000697D68: list object at 0x00000000006D0F48>, <cell at 0x0000000000697D98: int object at 0x0000000060BE01D0>) 19 <cell at 0x0000000000697D68: list object at 0x00000000006D0F48> 20 ['alex', 'egon'] 21 <cell at 0x0000000000697D98: int object at 0x0000000060BE01D0> 22 1
- 闭包的意义
- 返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域;
- 这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域;
- 应用领域:延迟计算/惰性计算
- f = index('http://www.baidu.com') 这样只是得到了一个函数对象,并没有执行具体操作,需要执行的时候直接使用 f() 即可;
1 # 内部函数包含对外部作用域而非全局作用域的引用; 2 # 返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域; 3 # 闭包应用:惰性计算/延迟计算 4 from urllib.request import urlopen 5 6 def index(url): 7 def get(): 8 return urlopen(url).read() 9 return get 10 11 f = index('http://www.baidu.com') 12 print(f.__closure__) 13 print(f.__closure__[0].cell_contents) 14 15 # print(f()) 16 print(f().decode('utf-8'))
九、装饰器
参考:Advanced Uses of Python Decorators
- 什么是装饰器?
- 修饰别人的工具,在原来基础上修饰添加功能;
- 装饰器本身可以是任何可调用对象,被装饰的对象也可以是任意可调用对象;
- 为什么要用装饰器?
- 开放封闭原则:对修改是封闭的,对扩展是开放的;
- 装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为其添加新功能;
- 装饰器应用
- 示例:
1 # 开放封闭原则:对修改封闭,对扩展开放; 2 # 装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象; 3 # 装饰器就是闭包的一种实现,只不过参数是函数对象; 4 5 import time 6 7 def timer(func): 8 def wrapper(): 9 start_time = time.time() 10 func() 11 end_time = time.time() 12 print('Run time is {}'.format(end_time-start_time)) 13 return wrapper 14 15 @timer # 等价于 index = timer(index) 16 def index(): 17 print('Start to work...') 18 time.sleep(3) 19 print('End the work...') 20 21 index() 22 23 --- 24 Start to work... 25 End the work... 26 Run time is 3.0
- 原理分析:
1.在 index()函数定义的正上方加上 @timer,即表示用 timer()函数来装饰 index()函数;
2.加上 @timer 后,再调用 index()的时候就相当于:timer(index);而 timer(index) 的返回值是 wrapper() 函数;
3.所以装饰之后,调用index() 等价于 调用 wrapper(),而wrapper()是一种闭包,包含了timer函数里参数(即 index)的引用;
- 装饰器扩展示例1:被装饰的对象(本例中就是函数),可以有参数和返回值;
1 import time 2 3 def timer(func): 4 def wrapper(*args,**kwargs): 5 start_time = time.time() 6 res = func(*args,**kwargs) 7 end_time = time.time() 8 print('Run time is {}'.format(end_time-start_time)) 9 return res 10 return wrapper 11 12 @timer # 等价于 index = timer(index) 13 def index(): 14 time.sleep(3) 15 print('Welcome hahaha...') 16 return 123 17 18 res = index() 19 print(res) 20 21 print('---------------') 22 23 @timer # 等价于 foo = timer(foo) 24 def foo(name): 25 time.sleep(1) 26 print('From foo func...') 27 return '--->>> ' + name 28 29 res = foo('standby') 30 print(res) 31 32 --- 33 Welcome hahaha... 34 Run time is 3.0 35 123 36 --------------- 37 From foo func... 38 Run time is 1.0 39 --->>> standby
- 装饰器扩展示例2:要求用户输入用户名密码,验证通过才能进行相关操作;
1 def auth_user(func): 2 def wrapper(*args,**kwargs): 3 u_name = input('Name is >>>\t'.expandtabs(20)) 4 u_pwd = input('Password is >>>\t'.expandtabs(20)) 5 if 'standby' == u_name and '123' == u_pwd: 6 res = func(*args,**kwargs) 7 return res 8 else: 9 print('Invalid input...') 10 return wrapper 11 12 @auth_user 13 def index(): 14 print('Welcome to index page...') 15 16 res1 = index() 17 print(res1) 18 19 print('--------------') 20 21 @auth_user 22 def home(name): 23 print('{}, welcome to index page...'.format(name)) 24 return '--->>> ' + name 25 res2 = home('standby') 26 print(res2) 27 28 --- 29 Name is >>> standby 30 Password is >>> 123 31 Welcome to index page... 32 None 33 -------------- 34 Name is >>> standby 35 Password is >>> 123 36 standby, welcome to index page... 37 --->>> standby
- 装饰器扩展示例3:有参装饰器;
1 user_dict = { 2 'name':None, 3 'status':False 4 } 5 6 def auth_type(type='ldap'): 7 def auth(func): 8 def wrapper(*args,**kwargs): 9 if user_dict['name'] and user_dict['status']: 10 res = func(*args, **kwargs) 11 return res 12 else: 13 name = input('Name >>> ') 14 pwd = input('Pass >>> ') 15 if 'ldap' == type: 16 print('ldap') 17 if 'standby' == name and '123' == pwd: 18 user_dict['name'] = name 19 user_dict['status'] = True 20 res = func(*args,**kwargs) 21 return res 22 else: 23 print('auth failed...') 24 elif 'mysql' == type: 25 print('mysql') 26 if 'standby' == name and '123' == pwd: 27 user_dict['name'] = name 28 user_dict['status'] = True 29 res = func(*args,**kwargs) 30 return res 31 else: 32 print('auth failed...') 33 else: 34 print('Invalid auth type.') 35 return wrapper 36 return auth 37 38 @auth_type('ldap') 39 def index(): 40 print('Welcome to index page.') 41 42 @auth_type('mysql') 43 def home(name): 44 print('{}, welcome home.'.format(name)) 45 return 'Hello ' + name 46 index() 47 res = home('standby') 48 print(res)
- 装饰器扩展示例4:装饰器叠加
1 def decorator1(func): 2 def wrapper(): 3 print('hello python, from decorator1') 4 func() 5 return wrapper 6 7 def decorator2(func): 8 def wrapper(): 9 func() 10 print('hello python, from decorator2') 11 return wrapper 12 13 @decorator1 14 @decorator2 15 def test(): 16 print('hello python!') 17 18 test() 19 20 --- 21 hello python, from decorator1 22 hello python! 23 hello python, from decorator2
十、迭代器
- 迭代的概念
1 迭代器 2 迭代的概念:重复+上一次迭代的结果为下一次迭代的初始值 3 重复的过程称为迭代,每次重复即一次迭代, 4 并且每次迭代的结果是下一次迭代的初始值
- 为什么要有迭代器?
为什么要有迭代器?
对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式;
- 可迭代对象 和 迭代器对象
- 可迭代对象
- 只要它定义了可以返回一个迭代器的__iter__
方法(即:内置__iter__方法的),那么他就是可迭代对象;
- 如下所示:list str tuple dict set 这些都是可迭代对象:
1 iter1 = [1,2].__iter__() 2 iter2 = 'hello'.__iter__() 3 iter3 = (1,2).__iter__() 4 iter4 = {'a':1,'b':2}.__iter__() 5 iter5 = {1,2,3}.__iter__() 6 print(type(iter1)) 7 print(type(iter2)) 8 print(type(iter3)) 9 print(type(iter4)) 10 print(type(iter5)) 11 12 --- 13 <class 'list_iterator'> 14 <class 'str_iterator'> 15 <class 'tuple_iterator'> 16 <class 'dict_keyiterator'> 17 <class 'set_iterator'>
- 迭代器对象
- 执行__iter__方法,得到的结果就是迭代器,迭代器对象有__next__方法;
- 迭代器是通过 next()
来实现的,每调用一次他就会返回下一个元素,当没有下一个元素的时候返回一个 StopIteration
异常;
1 i = {'k1':1,'k2':9,'k3':7}.__iter__() 2 print(type(i)) 3 print(i) 4 print(i.__next__()) 5 print(i.__next__()) 6 print(i.__next__()) 7 # print(i.__next__()) #抛出异常:StopIteration 8 9 --- 10 <class 'dict_keyiterator'> 11 <dict_keyiterator object at 0x00000000006C3778> 12 k2 13 k3 14 k1
示例1:
1 dic = { 2 'k1':'v1', 3 'k2':'v2', 4 } 5 6 f = dic.__iter__() # 得到一个迭代器对象 f 7 print(type(f)) # 迭代器类型 8 print(f) 9 print(f.__iter__()) # f.__iter__() 得到的还是 f 自身;(迭代器执行 __iter__() 得到的还是迭代器自身) 10 print(f.__next__()) # 迭代dic,获取 key 11 print(f.__next__()) 12 13 --- 14 <class 'dict_keyiterator'> 15 <dict_keyiterator object at 0x00000000006D2778> 16 <dict_keyiterator object at 0x00000000006D2778> 17 k1 18 k2
示例二: 模拟字典的for循环
1 dic={'a':1,'b':2,'c':3} 2 i=dic.__iter__() 3 while True: 4 try: 5 key=i.__next__() # 获取dic的key 6 print(dic[key]) # 根据key获取对应的value 7 except StopIteration: 8 break 9 10 --- 11 2 12 3 13 1
示例三: 枚举类型也是迭代器
1 l=[10000,2,3,4,5] 2 3 i=enumerate(l) 4 print(type(i),i) 5 print(next(i)) 6 print(next(i)) 7 print(next(i)) 8 print(next(i)) 9 print(next(i)) 10 11 --- 12 <class 'enumerate'> <enumerate object at 0x0000000000A10AF8> 13 (0, 10000) 14 (1, 2) 15 (2, 3) 16 (3, 4) 17 (4, 5)
- 扩展示例: len() 等价于 __len__()
1 s='hello' 2 print(s.__len__()) 3 print(len(s)) 4 5 --- 6 5 7 5
- 哪些是可迭代的对象?
1 #如何判断一个对象是可迭代的对象,还是迭代器对象 2 from collections import Iterable,Iterator 3 4 'abc'.__iter__() # 具有 __iter__() 方法的都是可迭代对象 5 ().__iter__() 6 [].__iter__() 7 {'a':1}.__iter__() 8 {1,2}.__iter__() 9 f=open('a.txt',mode='r',encoding='utf-8') 10 f.__iter__() 11 12 13 # 判断是否是可迭代的对象 14 print(isinstance('abc',Iterable)) 15 print(isinstance([],Iterable)) 16 print(isinstance((),Iterable)) 17 print(isinstance({'a':1},Iterable)) 18 print(isinstance({1,2},Iterable)) 19 print(isinstance(f,Iterable)) 20 21 print('--------------------') 22 # 判断是否是迭代器对象 23 print(isinstance('abc',Iterator)) 24 print(isinstance([],Iterator)) 25 print(isinstance((),Iterator)) 26 print(isinstance({'a':1},Iterator)) 27 print(isinstance({1,2},Iterator)) 28 print(isinstance(f,Iterator)) # 只有文件对象是迭代器对象 29 30 --- 31 True 32 True 33 True 34 True 35 True 36 True 37 -------------------- 38 False 39 False 40 False 41 False 42 False 43 True
- 迭代器优缺点
1 迭代器的优点和缺点 2 优点: 3 1.提供了一种不依赖下标的迭代方式; 4 2.就跌迭代器本身来说,更节省内存,每次只把一个元素放入内存中,依次迭代,而非全部放入内存中; 5 6 缺点: 7 1. 无法获取迭代器对象的长度; 8 2. 不如序列类型取值灵活,是一次性的,只能往后取值,不能往前退;
- 迭代器协议
- 对象有__next__()方法,即 next();
- 对象有__iter__()方法,对于迭代器对象来说,执行__iter__()方法,得到的结果仍然是它本身;
- 迭代器应用
- for循环遍历
1 dic={'name':'egon','age':18,'height':'180'} 2 print(dic.items()) 3 for k,v in dic.items(): 4 print(k,v) 5 6 --- 7 dict_items([('age', 18), ('name', 'egon'), ('height', '180')]) 8 age 18 9 name egon 10 height 180
- 迭代器内部实现
1 dic={'name':'egon','age':18,'height':'180'} 2 i=iter(dic) 3 while True: 4 try: 5 k=next(i) 6 print(k + " " + str(dic[k])) 7 except StopIteration: 8 break 9 10 --- 11 name egon 12 age 18 13 height 180
十一、生成器
- 什么是生成器?
- 只要函数体包含yield关键字,该函数就是生成器函数;
- 生成器就是迭代器;生成器是构造迭代器的最简单有力的工具,与普通函数不同的只有在返回一个值的时候使用yield
来替代return
,然后yield
会为函数封装好 next() 和 iter();
- 外部通过调用 next() 来触发迭代器(函数)的执行;
- yield
语句可以让普通函数变成一个生成器,并且相应的 __next__()
方法返回的是 yield
后面的值。
- 一种更直观的解释是:程序执行到 yield
会返回值并暂停,再次调用 next()
时会从上次暂停的地方继续开始执行;
1 return只能返回一次值,函数就终止了; 2 yield能返回多次值,每次返回都会将函数暂停; 3 下一次next会从上一次暂停的位置继续执行
- 示例:
- 示例1:
1 def foo(): 2 print('first') 3 yield 1 4 print('second') 5 yield 2 6 print('third') 7 yield 3 8 print('fourth') 9 yield 4 10 print('fifth') 11 12 g=foo() 13 print(type(g)) 14 print(g) 15 print(next(g)) #触发迭代器g的执行,进而触发函数的执行 16 print(next(g)) 17 print(next(g)) 18 print(next(g)) 19 # print(next(g)) # StopIteration 异常 20 21 --- 22 <class 'generator'> 23 <generator object foo at 0x00000000007DB468> 24 first 25 1 26 second 27 2 28 third 29 3 30 fourth 31 4
- 示例2:
1 def foo(): 2 print('first') 3 yield 1 4 print('second') 5 yield 2 6 print('third') 7 yield 3 8 print('fourth') 9 yield 4 10 print('fifth') 11 12 g=foo() # 得到一个生成器; 13 print(type(g)) 14 print(g) 15 for i in g: # 遍历这个生成器; 16 print("返回值是 %s" % i) 17 18 --- 19 <class 'generator'> 20 <generator object foo at 0x00000000010EB4C0> 21 first 22 返回值是 1 23 second 24 返回值是 2 25 third 26 返回值是 3 27 fourth 28 返回值是 4 29 fifth
- 生成器应用
1 # 模拟Linux中命令:tail -f a.txt | grep 'python' 2 3 import time 4 def tail(filepath): 5 with open(filepath,encoding='utf-8') as f: 6 f.seek(0,2) # 定位到文件末尾 7 while True: 8 line=f.readline().strip() # 从最后一行开始读内容 9 if line: # 如果读到内容则返回该行内容,然后进入下一轮 while循环,并暂停,等待下一次调用; 10 yield line 11 else: 12 time.sleep(0.2) 13 14 def grep(pattern,lines): 15 for line in lines: # 循环tail函数中yield返回的文件新增内容; 16 if pattern in line: # 如果包含关键字 patten,则返回该行内容,然后进入下一次for循环并暂停在这一步,等待下一次调用; 17 yield line 18 19 g=grep('python',tail('a.txt')) # patten 即 'python' 20 print(g) # 得到一个生成器对象:<generator object grep at 0x0000000000A3B570> 21 for i in g: # 遍历该生成器(即 迭代器),循环调用 grep() 和 tail() 函数; 22 print(i) # 将grep()函数返回的内容打印出来;
十二、练习
要求:
操作文件:haproxy.cfg,实现对这个文件配置的新增、删除以及查询的功能;
- 新增的时候要考虑backend是否已存在,没有则新增backend;有则在进一步判断是否存在响应的record,有则不能重复插入,没有则可以插入;
- 删除的时候要判断是否存在用户想要删除的record,不存在则反馈用户提示信息;存在的话就需要考虑backend含有几个record,大于1个,则删除record,如果只有1个record,则需要把record和backend都要删除;
- 查询则是列出用户所输入的backend下的所有record信息;
代码实现:
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 4 5 def get_backend_dict(): 6 with open('haproxy.cfg',mode='r',encoding='utf-8') as rf: 7 content_list = rf.readlines() 8 backend_dict = {} 9 for i in range(0, len(content_list)): 10 if content_list[i].startswith('backend '): 11 backend_dict[content_list[i].split()[1]] = i 12 return content_list,backend_dict 13 14 def select(data): 15 content_list,backend_dict = get_backend_dict() 16 if data not in backend_dict: 17 print('No backend record found.') 18 else: 19 for i in range(backend_dict[data],len(content_list)-1): 20 if content_list[i+1].startswith('backend '): 21 return 22 print(content_list[i+1],end='') 23 24 def add(data): 25 content_list,backend_dict = get_backend_dict() 26 sub_record_info = " server " + data['record']['server'] + " " \ 27 + data['record']['server'] + " weight " \ 28 + str(data['record']['weight']) + " maxcon " \ 29 + str(data['record']['maxcon']) + "\n" 30 backend_info = "\n" + "backend " + data['backend'] + "\n" + sub_record_info 31 if data['backend'] in backend_dict: 32 index = -1 33 tmp_list = [] 34 print('The backend:{} already exist, now to append a record...'.format(data['backend'])) 35 for i in range(backend_dict[data['backend']],len(content_list)-1): 36 if content_list[i+1].startswith('backend '): 37 index = i 38 break 39 else: 40 tmp_list.append(content_list[i+1]) 41 if sub_record_info not in tmp_list: 42 if -1 == index: 43 content_list.append(sub_record_info) 44 else: 45 content_list.insert(index,sub_record_info) 46 with open('haproxy.cfg', mode='w', encoding='utf-8') as wf: 47 wf.writelines(content_list) 48 print("The backend:{} already exist, append a record successfully.".format(data['backend'])) 49 else: 50 print("The record:{} already exist, add failed.".format(sub_record_info.strip())) 51 else: 52 with open('haproxy.cfg',mode='a+',encoding='utf-8') as wf: 53 wf.write(backend_info) 54 print("New backend added successfully.") 55 56 def remove(data): 57 content_list, backend_dict = get_backend_dict() 58 if data['backend'] not in backend_dict: 59 print("The backend:{} doesn't exist, remove failed.".format(data['backend'])) 60 else: 61 tmp_list = [] 62 sub_record_info = " server " + data['record']['server'] + " " \ 63 + data['record']['server'] + " weight " \ 64 + str(data['record']['weight']) + " maxcon " \ 65 + str(data['record']['maxcon']) + "\n" 66 if sub_record_info not in content_list: 67 print("The record:{} doesn't exist, remove failed.".format(sub_record_info.strip())) 68 return 69 else: 70 content_list.remove(sub_record_info) 71 for i in range(backend_dict[data['backend']],len(content_list)-1): 72 if content_list[i+1].startswith('backend '): 73 break 74 else: 75 tmp_list.append(content_list[i+1]) 76 if 0 == len(tmp_list): 77 content_list.remove(data['backend']) 78 with open('haproxy.cfg',mode='w',encoding='utf-8') as wf: 79 wf.writelines(content_list) 80 print("The record:{} removed successfully.".format(sub_record_info.strip())) 81 82 if __name__ == '__main__': 83 info = ''' 84 1:查询 85 2:增加 86 3:删除 87 4:退出程序 88 ''' 89 menu_dict = { 90 '1':select, 91 '2':add, 92 '3':remove, 93 '4':exit, 94 } 95 while True: 96 print(info) 97 chioce = input('请输入操作序号>>> ').strip() 98 if '4' == chioce: break 99 if 0 == len(chioce) or chioce not in menu_dict:continue 100 elif '1' == chioce: 101 backend = input('请输入要查询的backend>>> ').strip() 102 if 0 == len(backend): 103 print('Invalid input...') 104 else: 105 select(backend) 106 else: 107 notice = 'Input Fomat: backend domain|server(domain/ip)|weight value|maxcon value' 108 print(notice) 109 data = input('请输入数据>>> ').strip() 110 data_list = data.split('|') 111 if 4 == len(data_list) and data_list[2].isdigit() and data_list[3].isdigit(): 112 input_dict = { 113 'backend':data_list[0], 114 'record':{ 115 'server':data_list[1], 116 'weight':int(data_list[2]), 117 'maxcon':int(data_list[3]), 118 }, 119 } 120 menu_dict[chioce](input_dict) 121 else: 122 print('Input invalid...')
文件:
1 frontend www 2 bind *:80 3 acl web hdr_beg(host) 10.207.252.45 4 acl web1 hdr_beg(host) 10.207.252.46 5 use_backend webserver if web 6 use_backend web1server if web1 7 8 backend www.oldboy1.org 9 server 192.168.1.110 192.168.1.110 weight 10 maxcon 3007 10 server 192.168.1.120 192.168.1.120 weight 10 maxcon 3009 11 server 192.168.1.110 192.168.1.110 weight 10 maxcon 3009 12 13 backend www.oldboy2.org 14 server 192.168.1.130 192.168.1.130 weight 20 maxcon 3000 15 server 192.168.1.131 192.168.1.131 weight 20 maxcon 3000 16 backend www.qq.com 17 server 3.3.3.3 3.3.3.3 weight 90 maxcon 100 18 server 3.3.3.100 3.3.3.100 weight 90 maxcon 700 19 server 8.8.8.9 8.8.8.9 weight 20 maxcon 200
出处:http://www.cnblogs.com/standby/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。