课前补充个新的概念:为什么要定义函数?
为了使用。函数即变量。变量怎么定义的x=1调用函数分为两部分:先找到当前你要调用的函数有没有那个名字。有了这个函数名字,加括号,才是执行这个函数里面的代码。定义函数就相当于在定义了一个变量,函数名就是变量名,函数值就是函数体里面的代码。如果没有事先定义函数,而直接引用,就相当于在引用一个不存在的变量。所以函数的使用一定要遵循先定义后使用的原则。
第一节:函数对象
函数对象指的是函数可以当做数据去传递!(什么叫数据呢?x=1相当于定义了一个数据,那x与什么特性,函数就应该有什么特性!特性如下:)
1.可以被引用
2.可以当参数传递
3.返回值可以是函数
4.可以当做容器类型的元素
那分别解析这几种特性:
可以被引用-------->怎么叫引用函数呢?
#!/usr/bin/python # -*- coding:utf-8 -*- def foo(): print('from foo') func = foo #func和foo指向相同的内存地址 print(foo) print(func) func()#有了内存地址,加括号就可以直接调用 输出结果: <function foo at 0x001C6738> <function foo at 0x001C6738> from foo
可以当参数传递-------->下面的数字为函数执行顺序
#!/usr/bin/python # -*- coding:utf-8 -*-
def foo():
print('from foo')#4、打印
def bar(func): print(func)#2、打印func就相当于打印的是foo的内存地址 func()#3、func已经获得foo的内存地址,那么加括号就可以直接运行 bar(foo)#1、func需要一个参数,这个参数可以把foo这个名字传进去,这就相当于传了一个内存地址给他 输出结果: <function foo at 0x001D6738> from foo
返回值可以是函数-------->return
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 def foo(): 4 print('from foo') 5 def bar(func): 6 return func 7 f = bar(foo)#bar相当于传了一个内存地址,赋值给f之后,f加括号直接调用 8 print(f) 9 f() 10 输出结果: 11 <function foo at 0x002F6738> 12 from foo
可以当做容器类型的元素-------->
x = 1 dic = {'x':x}#把x当做数据进行传递,就相当于{'x':1}.函数就是变量,函数有第一类对象的感念,可以当数据进行传递,意味着他可以当容器类型的一个元素
#那么就可以这样写:
1 def foo(): 2 print('from foo') 3 dic = {'func':foo}#func的值为foo这个函数的内存地址。如果这句我写成dic = {'func':foo()} 表示此处的值就是调用foo这个函数的结果了 4 print(dic['func'])#进行调用,得到foo的内存地址 5 dic['func']()#记上括号就可以直接运行 6 输出结果: 7 <function foo at 0x00566738> 8 from foo
#这样写有什么好处呢?看下面的应用:
1 def select(sql): 2 print('======>select') 3 def insert(sql): 4 print('====>insert') 5 def delete(sql): 6 print('=====>delete') 7 def update(sql): 8 print('========>update') 9 10 #定义好函数之后,要根据用户的输入进行不同函数的调用 11 #我可以根据函数对象来这样写: 12 func_dic = { 13 'select':select, 14 'update':update, 15 'insert':insert, 16 'delete':delete, 17 18 } 19 def main(): 20 #涉及到用户交互的要加上while循环 21 while True: 22 sql = input('>>:')#用户输入sql 23 if not sql:continue#如果不加这句,执行代码之后直接回车,会报 list index out of range 24 l = sql.split()#对sql语句进行切分 25 print(l) 26 #这种写法很冗余,所以我们用 func_dic 这个字典来完成 27 # if l[0] == 'select':#判断看select是否在l中,如果在,就调用select函数 28 # select(l) 29 # elif l[0] == 'insert': 30 # insert(l) 31 # elif l[0] == 'delte': 32 # delete(l) 33 # elif l[0] == 'update': 34 # update(l) 35 36 cmd = l[0] 37 if cmd in func_dic: 38 func_dic[cmd](1) 39 main() 40 输出结果: 41 >>:select * from mysql.db 42 ['select', '*', 'from', 'mysql.db'] 43 ======>select 44 >>:delete * from user 45 ['delete', '*', 'from', 'user'] 46 =====>delete 47 >>:
第二节:函数嵌套
函数的嵌套分为两种:1)函数的嵌套调用 2)函数的嵌套定义
分别来解析一下:
函数的嵌套调用------->在一个函数内部再去调用其他的函数
上节课我们讲了求两个数的最大值,那求四个数的最大值呢?:
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 def max2(x,y): 4 return x if x > y else y 5 6 #那如果求四个值中最大的值呢?思路:我先两个比较,比较的大的结果和第三个比较,然后在和最后一个比较 7 def max4(a,b,c,d): 8 res1 = max2(a,b) 9 res2 = max2(res1,c) 10 res3 = max2(res2,d) 11 return res3 12 print(max4(10,99,31,22))
函数的嵌套定义------->注意:一层一层执行,最后调用的时候也要看好层次,巧用折叠的方式
1 def f1(): 2 def f2(): 3 print('from f2') 4 def f3(): 5 print('from f3') 6 f3() 7 f2() 8 f1()#如果不调用f2(),那么调用f1()不输出任何结果。如果加上f2(),就会输出from f2 .加上f3()就会输出from f3 9 输出结果: 10 from f2 11 from f3
第三节:函数名称空间与作用域
一、名称空间
Python当中名字和值之间是一种绑定的关系。
Python中分三种名称空间:
1)内置名称空间 2)全局名称空间 3)局部名称空间
分别解析一下:
内置名称空间------->存放内置的名字,内置到Python解释器里面的名字
典型的如下:
print(sum)#输出结果:<built-in function sum> 含built-in的称为内置名字
名称空间什么时候会产生呢?Python解释器只要一启动,这个名称空间就会产生,里面的名字就会定义好
内置函数的特点:无需定义,直接调用
怎么查看内置函数的名字呢:
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 import builtins 4 for i in dir(builtins): 5 print(i) 6 输出结果: 7 ArithmeticError 8 AssertionError 9 AttributeError 10 BaseException 11 BlockingIOError 12 BrokenPipeError 13 BufferError 14 BytesWarning 15 ChildProcessError 16 ConnectionAbortedError 17 ConnectionError 18 ConnectionRefusedError 19 ConnectionResetError 20 DeprecationWarning 21 EOFError 22 Ellipsis 23 EnvironmentError 24 Exception 25 False 26 FileExistsError 27 FileNotFoundError 28 FloatingPointError 29 FutureWarning 30 GeneratorExit 31 IOError 32 ImportError 33 ImportWarning 34 IndentationError 35 IndexError 36 InterruptedError 37 IsADirectoryError 38 KeyError 39 KeyboardInterrupt 40 LookupError 41 MemoryError 42 NameError 43 None 44 NotADirectoryError 45 NotImplemented 46 NotImplementedError 47 OSError 48 OverflowError 49 PendingDeprecationWarning 50 PermissionError 51 ProcessLookupError 52 RecursionError 53 ReferenceError 54 ResourceWarning 55 RuntimeError 56 RuntimeWarning 57 StopAsyncIteration 58 StopIteration 59 SyntaxError 60 SyntaxWarning 61 SystemError 62 SystemExit 63 TabError 64 TimeoutError 65 True 66 TypeError 67 UnboundLocalError 68 UnicodeDecodeError 69 UnicodeEncodeError 70 UnicodeError 71 UnicodeTranslateError 72 UnicodeWarning 73 UserWarning 74 ValueError 75 Warning 76 WindowsError 77 ZeroDivisionError 78 __build_class__ 79 __debug__ 80 __doc__ 81 __import__ 82 __loader__ 83 __name__ 84 __package__ 85 __spec__ 86 abs 87 all 88 any 89 ascii 90 bin 91 bool 92 bytearray 93 bytes 94 callable 95 chr 96 classmethod 97 compile 98 complex 99 copyright 100 credits 101 delattr 102 dict 103 dir 104 divmod 105 enumerate 106 eval 107 exec 108 exit 109 filter 110 float 111 format 112 frozenset 113 getattr 114 globals 115 hasattr 116 hash 117 help 118 hex 119 id 120 input 121 int 122 isinstance 123 issubclass 124 iter 125 len 126 license 127 list 128 locals 129 map 130 max 131 memoryview 132 min 133 next 134 object 135 oct 136 open 137 ord 138 pow 139 print 140 property 141 quit 142 range 143 repr 144 reversed 145 round 146 set 147 setattr 148 slice 149 sorted 150 staticmethod 151 str 152 sum 153 super 154 tuple 155 type 156 vars 157 zip
全局名称空间------->什么时候会产生的呢?文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入该空间。
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 x = 1 4 def func(): 5 money = 2000 6 x = 2 #这个x的定义不会影响上面的x=1 7 print('func') 8 print(x) 9 print(func) 10 func() 11 print(money)#现在找不到money,因为他是在函数级别定义的
局部名称空间------->在函数内部定义的!局部名称空间什么时候会产生呢?调用函数时会产生局部名称空间。局部名称空间里面存的就是局部的名字。
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 x = 10000 4 def func(): 5 x =1 6 def f1():#f1只能在func这个函数级别里面使用,这种名字和值的绑定关系,什么时候有效,什么时候失效呢?只在函数调用时临时绑定,调用结束解除绑定。 7 pass 8 f1()
以上三个名称空间是彼此互相隔离的!
二、作用域
域指的是一个范围,指的是在哪个范围内找名字!分为 1) 全局作用域 2)局部作用域
解析如下:
全局作用域------>包含哪几个空间呢?内置名称空间,全局名称空间
局部作用域------>局部名称空间
作用域里面有个找名字的过程,是在哪个范围里面找名字。名字的查找顺序:局部名称空间---》全局名称空间---》内置名称空间
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 x = 1 4 def func(): 5 x = 2#如果把这句注释掉,就说明局部名称空间里面没有,就到全局名称空间里面找,x 为1 6 print(x)#先在局部范围里面找,如果没有在到全局范围里面找 7 func()
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 def func(): 4 sum = 123123#如果注释此句,那么局部空间不存在名字,就去全局空间找,也不存在,所以最终去内置名称空间找,输出内存地址<built-in function sum> 5 print(sum)#sum首先在局部空间进行查找,结果为123123 6 func()
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 def func(): 4 x = 2 5 func() 6 print(x)#已经处于全局范围了,只能从全局名称空间及以上开始找。不能找内置名称空间了
查看全局作用域和局部作用域里面名字的方法:
查看全局作用域内的名字:gloabls()
查看局部作用域内的名字:locals()
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 x = 1000 4 def func(): 5 x = 2 6 print(globals()) 7 print(locals())#在全局里面查看局部,那得到的还是全局 8 print(globals() is locals())
在全局看不到局部,他的局部就是自己,局部就是函数内部定义的名字,全局是文件级别的。
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 x = 1000 4 def func(y): 5 x = 2 6 print(locals())#{'x': 2, 'y': 1} 7 print(globals())#可以输出 8 func(1)
全局作用域是全局有效,在当前文件的任何位置都能被访问到,除非del删掉,否则会一直存活到文件执行完毕!
局部作用域的名字:局部有效。只能在局部范围调用,只在函数调用时才有效,函数调用结束才失效。
在全局看不到局部,他的局部就是自己,局部就是函数内部定义的名字,全局是文件级别的。
第四节 闭包函数
闭包:内部函数包含对外部作用域而非全局作用域的引用,该内部函数就称为闭包函数
如下就是一个闭包函数的定义:
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 def f1(): 4 x = 1 5 def f2(): 6 print(x)
如果代码写成如下,就不是闭包函数了,因为x引用的是全局作用域的名称了
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 x = 1 4 def f1(): 5 def f2(): 6 print(x)
闭包函数需要满足的条件:1)必须是内部定义的函数 2)要引用自己外部作用域,而不是对全局作用域中名字的引用
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 4 def f1(): 5 x = 1#2、先定义了一个名字 6 def f2():#3、又定义了一个名字 7 print(x)#5、里面有个 打印的操作,这个x在f2这个函数里面没有,就要去上一层级别,他要拿到的就是x=1 8 return f2()#4、拿到一个结果f2,此时这个f2就是f1() 9 f1()#1、执行f1
闭包函数可以做什么用呢?闭包应用:惰性计算:
如果想爬虫一个网页,怎么操作呢?
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 from urllib.request import urlopen 4 res = urlopen('http://crm.oldboyedu.com').read() 5 print(res.decode('utf-8'))
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 def index(url): 4 def get():#这个get可以获取url 5 return urlopen(url).read() 6 return get#return这个值的时候不仅仅是函数,还有一层作用域关系 7 oldboy = index('http://crm.oldboyedu.com') 8 print(oldboy().decode('utf-8'))
闭包函数的特点:拿到函数,在任何位置执行,要找他里面引用的值优先从哪里找?优先从自己包的那层作用域开始找,也就相当于这个函数自带的一种状态。
辅助验证闭包函数的概念,可以看出他外面包的内容:
print(oldboy.__closure__[0].cell_contents)
输出结果:http://crm.oldboyedu.com
闭包函数的核心:内部函数,包含对外部作用域的引用。return把这个函数返回来。
第五节 装饰器
装饰器指的是修饰别人的工具。修饰指的是添加功能,工具指的是函数。装饰器本身可以是任何可调用(名字加括号可以执行的,这就是可调用的)对象,被装饰的对象也可以是任意可调用对象。
为什么要用装饰器啊?
开放封闭原则:对修改是封闭的,对扩展是开放的,(比如你在公司写了一款游戏,这个游戏最终要上线,上线完了之后这些代码就一直不动了么?会有改的需求,那么问题来了!假如你写的代码是一个一个的函数,让你进行功能的修改,那么你打算怎样去做?去改源代码的话风险特别大,因为你一个函数可能很多地方都在用,你把定义的源代码给改掉了,影响的可能不是一个点,可能影响到好多其他地方,牵一发而动全身,如果你改对了,那就没问题,一旦你改错了呢?就会导致一系列的连锁反应,可能这个软件都会垮掉,所以,已经上线的程序,源代码要尽量避免修改,但是,难道就不改了么?对修改源代码这种操作应该封闭起来,但是对于扩展功能你还不得不做,你的代码一定要为后期的这种扩展留下可能性,而且还不能动源代码,这就用到了装饰器的功能。)装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为其添加新功能。
应用:
1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 #模拟统计一个函数执行了多长时间 4 import time 5 def index(): 6 time.sleep(3) 7 print('welcome to index') 8 index() 9 10 #现在就统计上面的按个函数执行用了多长时间,如果用下面这种写法,就违反了不可修改源代码的规则 11 import time 12 def index(): 13 start_time = time.time() 14 time.sleep(3) 15 print('welcome to index') 16 stop_time = time.time() 17 print('run time is %s' %(stop_time-start_time)) 18 index() 19 20 #要保证源代码不能动,调用方式也不能动 21 import time 22 def timmer(func): 23 def wrapper(*args,**kwargs): 24 start_time = time.time() 25 res = func(*args,**kwargs) 26 stop_time = time.time() 27 print('run time is %s' %(stop_time-start_time)) 28 return wrapper 29 @timmer 30 def index(): 31 time.sleep(3) 32 print('welcome to index') 33 index() 34 输出结果: 35 welcome to index 36 run time is 3.0002999305725098
上面例子也可以用闭包函数实现:
1 import time 2 def timmer(func): 3 def wrapper(): 4 start_time = time.time() 5 func()#调的是index() 6 stop_time = time.time() 7 print('run time is %s' %(stop_time - start_time)) 8 return wrapper 9 def index(): 10 time.sleep(3) 11 print('welcome to index') 12 13 index = timmer(index) 14 index()
不能每次都调用,也可以使用装饰器完成:
1 import time 2 def timmer(func): 3 def wrapper(): 4 start_time = time.time() 5 func()#调的是index() 6 stop_time = time.time() 7 print('run time is %s' %(stop_time - start_time)) 8 return wrapper 9 @timmer #他会把他正下方的这个函数名当成是参数传进来,结果在给index。index=timmer(index) 10 def index(): 11 time.sleep(3) 12 print('welcome to index') 13 14 index()
我们来分析一下装饰器的流程
1 #代码是从上而下解释执行 2 3 4 import time#1、只是定义一个名字而已,没有任何意义 5 def timmer(func):#2、这就是定义了一个名字,没有意义,把他折叠合上 6 def wrapper():#4、首先这定义的是一个函数,把他折叠上,他就是一个名字而已 7 #8、展开wrapper,一执行, 8 start_time = time.time()#9执行这句 9 func()#10、调的是最原始的index(),是第六步的index 10 stop_time = time.time()#14、继第13步 11 print('run time is %s' %(stop_time - start_time))#15、统计一下func()的时间,至此wrapper函数才算执行完 12 return wrapper#5、在wrapper函数合上的情况下,执行第三部就相当于只是得到wrapper的返回值,第三部就相当于index=wrapper,把函数当做数据传递 13 @timmer #3、他会把他正下方的这个函数名当成是参数传进来,而且这种方式是固定的,这就是装饰器的语法,这个函数的返回值在重新给index,打开折叠的index函数,看看他都执行了什么 14 def index():#6继续往下走,到这部,这个函数就是一个定义,没意义,折叠上 15 #11被第十步调用 16 time.sleep(3)#12、执行这句,先睡3秒 17 print('welcome to index')#13、然后打印这句话,紧接着在执行第十四步 18 19 index()#7、index函数折叠之后,就到这部了,调用index,现在的这个index已经是wrapper了,所以现在执行的是wrapper(),那看wrapper是怎样执行的
流程分析完了,我们想想这有什么问题?见如下代码,为什么返回的是null而不是1?倒数第二行表明了其原因!现阶段,你只要学会了这个流程,写装饰器怎么写?把闭包函数的格式套过来。装饰器就是闭包函数的一种实现方式。
1 import time 2 def timmer(func): 3 def wrapper(): 4 start_time = time.time() 5 func()#调的是index() 6 stop_time = time.time() 7 print('run time is %s' %(stop_time - start_time)) 8 return wrapper 9 @timmer #他会把他正下方的这个函数名当成是参数传进来,结果在给index。index=timmer(index) 10 def index(): 11 time.sleep(3) 12 print('welcome to index') 13 return 1 14 15 res = index()#现在执行的index还是你之前的index么?执行的是wrapper(),所以你拿的是wrapper的返回值。而wrapper是没有返回值的。 16 print(res)
那有同学这样写:把return 1 放在了wrapper里面
1 import time 2 def timmer(func): 3 def wrapper(): 4 start_time = time.time() 5 func()#调的是index() 6 stop_time = time.time() 7 print('run time is %s' %(stop_time - start_time)) 8 return 1#把return 1 加在此处,如果这样的话,上面的func()就拿不到东西了 9 return wrapper 10 @timmer #他会把他正下方的这个函数名当成是参数传进来,结果在给index。index=timmer(index) 11 def index(): 12 time.sleep(3) 13 print('welcome to index') 14 return 1 15 16 res = index()#现在执行的index还是你之前的index么?执行的是wrapper(),所以你拿的是wrapper的返回值。而wrapper是没有返回值的。 17 print(res)
那该怎样修正呢?如下修正,返回结果就是1了
1 import time 2 def timmer(func): 3 def wrapper(): 4 start_time = time.time() 5 res=func()#(2)这里调的就是index(),我们已经把这个返回值func()暂存下来了res。等所有功能加载完之后,我们在把最原始函数的返回值当做wrapper函数的返回值在返回就可以。 6 stop_time = time.time() 7 print('run time is %s' %(stop_time - start_time)) 8 return res 9 return wrapper 10 @timmer #他会把他正下方的这个函数名当成是参数传进来,结果在给index。index=timmer(index) 11 def index(): 12 time.sleep(3) 13 print('welcome to index') 14 return 1 #(1)因为这个返回值就要看什么时候调用index 15 16 res = index() 17 print(res)
在考虑下,如果index换成有参函数呢?
1 import time 2 def timmer(func): 3 def wrapper(): 4 start_time = time.time() 5 res=func() 6 stop_time = time.time() 7 print('run time is %s' %(stop_time - start_time)) 8 return res 9 return wrapper 10 @timmer 11 def index(): 12 time.sleep(3) 13 print('welcome to index') 14 return 1 15 @timmer 16 def foo(name): 17 time.sleep(1) 18 print('from foo') 19 # res = index() 20 # print(res) 21 res1 = foo('egon')#res1= wrapper('egon')。相当于调用了wrapper,并传了一个参数进去.而看一下参数 22 print(res1) 23 输出结果: 24 D: oolspythonpython.exe "E:PyCharmPyCharm 2017.1helperspydevpydevd.py" --multiproc --qt-support --client 127.0.0.1 --port 47326 --file D:/python2017/20170423/s17_zc/day04/装饰器.py 25 pydev debugger: process 4748 is connecting 26 27 Connected to pydev debugger (build 171.3780.115) 28 Traceback (most recent call last): 29 File "E:PyCharmPyCharm 2017.1helperspydevpydevd.py", line 1578, in <module> 30 globals = debugger.run(setup['file'], None, None, is_module) 31 File "E:PyCharmPyCharm 2017.1helperspydevpydevd.py", line 1015, in run 32 pydev_imports.execfile(file, globals, locals) # execute the script 33 File "E:PyCharmPyCharm 2017.1helperspydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile 34 exec(compile(contents+" ", file, 'exec'), glob, loc) 35 File "D:/python2017/20170423/s17_zc/day04/装饰器.py", line 93, in <module> 36 res1 = foo('egon') 37 TypeError: wrapper() takes 0 positional arguments but 1 was given 38 39 Process finished with exit code 1
上面函数报错,wrapper函数需要0个参数,但是确给了一个!原因是foo('egon')就相当于调用了wrapper,并传了一个参数进去。那这种情况如何解决呢?加一个参数就可以了,如下代码:
1 import time 2 def timmer(func): 3 def wrapper(name):#加上name参数 4 start_time = time.time() 5 res=func(name)#加上name参数 6 stop_time = time.time() 7 print('run time is %s' %(stop_time - start_time)) 8 return res 9 return wrapper 10 @timmer #他会把他正下方的这个函数名当成是参数传进来,结果在给index。index=timmer(index) 11 def index(): 12 time.sleep(3) 13 print('welcome to index') 14 return 1 #(1)因为这个返回值就要看什么时候调用index 15 @timmer 16 def foo(name): 17 time.sleep(1) 18 print('from foo') 19 # res = index() 20 # print(res) 21 res1 = foo('egon') 22 print(res1) 23 返回结果: 24 from foo 25 run time is 1.0001001358032227 26 None
如果将如上的代码注释掉的内容
# res = index()
# print(res)
释放开,执行之后还是报错如图:
1 import time 2 def timmer(func): 3 def wrapper(name): 4 start_time = time.time() 5 res=func(name) 6 stop_time = time.time() 7 print('run time is %s' %(stop_time - start_time)) 8 return res 9 return wrapper 10 @timmer 11 def index(): 12 time.sleep(3) 13 print('welcome to index') 14 return 1 15 @timmer 16 def foo(name): 17 time.sleep(1) 18 print('from foo') 19 res = index() 20 print(res) 21 res1 = foo('egon') 22 print(res1) 23 输出结果: 24 D: oolspythonpython.exe "E:PyCharmPyCharm 2017.1helperspydevpydevd.py" --multiproc --qt-support --client 127.0.0.1 --port 50255 --file D:/python2017/20170423/s17_zc/day04/装饰器.py 25 pydev debugger: process 896 is connecting 26 27 Connected to pydev debugger (build 171.3780.115) 28 Traceback (most recent call last): 29 File "E:PyCharmPyCharm 2017.1helperspydevpydevd.py", line 1578, in <module> 30 globals = debugger.run(setup['file'], None, None, is_module) 31 File "E:PyCharmPyCharm 2017.1helperspydevpydevd.py", line 1015, in run 32 pydev_imports.execfile(file, globals, locals) # execute the script 33 File "E:PyCharmPyCharm 2017.1helperspydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile 34 exec(compile(contents+" ", file, 'exec'), glob, loc) 35 File "D:/python2017/20170423/s17_zc/day04/装饰器.py", line 91, in <module> 36 res = index() 37 TypeError: wrapper() missing 1 required positional argument: 'name' 38 39 Process finished with exit code 1
发现将注释放开之后,上面的wrapper又不需要参数了,这就是用到了可变长的概念,可能有也可能没有,长度不固定。那怎么解决这个问题!如下利用*args和**kwargs
1 import time 2 def timmer(func): 3 def wrapper(*args,**kwargs): 4 start_time = time.time() 5 res=func(*args,**kwargs) 6 stop_time = time.time() 7 print('run time is %s' %(stop_time - start_time)) 8 return res 9 return wrapper 10 @timmer 11 def index(): 12 time.sleep(3) 13 print('welcome to index') 14 return 1 15 @timmer 16 def foo(name): 17 time.sleep(1) 18 print('from foo') 19 res = index() 20 print(res) 21 res1 = foo('egon') 22 print(res1) 23 返回结果: 24 welcome to index 25 run time is 3.0 26 1 27 from foo 28 run time is 1.0 29 None
接下来写一个装饰器,实现一个认证功能。现在定义一堆函数,在这些函数真实的功能执行之前,加上一段认证功能,只有认证通过了了,才能去执行他的一个真实的功能。
比如对如下函数进行认证
1 def index(): 2 print('welcome to index page') 3 def home(name): 4 print('%s welcome to home page' %name) 5 index() 6 home('egon')
登录成功进行打印,否则提示登录错误,代码如下:
1 def auth(func): 2 def wrapper(*args,**kwargs): 3 name = input('>>:') 4 password = input('>>:') 5 if name == 'egon' and password == '123': 6 print('