首页 > 技术文章 > python之旅5【第五篇】

Dicky-Zhang 2017-08-20 15:53 原文

装饰器详解

函数刚开始不解析内部,只是放进内存

装饰器是函数,只不过该函数可以具有特殊的含义,装饰器用来装饰函数或类,使用装饰器可以在函数执行前和执行后添加相应操作

 1 下面以一个函数开始,理解下面概念

1 def  foo():
2     print 'foo'
3 
4 foo 是函数
5 foo()  是执行函数

2 看下面的一个函数执行过程

1 def  f1(arg):
2     arg()
3 
4 def func():
5     print '123'
6 
7 f1(func)

3 上面的理解之后,看下面一个函数执行过程

1 def auth(func):
2     def wrapper():
3         print "before"
4         func()
5     return wrapper
6 def f1():
7     print "f1"
8 auth(f1)

 4 接着上边的,如何执行内部的内容, 引入装饰器

还是上面的函数,换了执行方式

1 def auth(func):
2     def wrapper():
3         print "before"
4         func()
5     return wrapper
6 def f1():
7     print "f1"
8 ret=auth(f1)
9 ret()

 5 装饰器引入

 1 def auth(func):
 2 def wrapper():
 3     print "before"
 4     func()
 5 return wrapper
 6 
 7 @auth
 8 def f1():
 9 print "f1
10 
11 @auth=>auth(f1)==>f1=auth(f1)=def wrapper():
12                                print "before"
13                                func()
14 接下来再执行f1()就相当于执行了wrapper()函数
主要过程是执行auth函数,被装饰的函数作为参数传入auth--auth(f1),auth函数的返回值,赋值给被装饰的函数的函数名,---f1=wrapper

6 装饰器含参数(一个参数)

 1 def auth(func):
 2     def inner(arg):
 3         print "before"
 4         func(arg)
 5     return inner
 6 @auth
 7 def f2(arg):
 8     print "f2" ,arg
 9 f2('test')
10 
11 结果
12 before
13 f2 test

7 多个参数,动态参数

 1 def auth(func):
 2     def inner(*args,**kwargs):
 3         print "before"
 4         func(*arg,**kwargs)
 5         print 'after'
 6     return inner    
 7 
 8 @auth
 9 def f2(*args,**kwargs):
10     print "f2"    
11 
12 f2('1.1.1.1') 

 8 含有返回值的装饰器

 1 def auth1(func):
 2     def inner(*arg,**kwargs):
 3         print 'before'
 4         temp = func(*arg,**kwargs)
 5         print 'after'
 6         return temp   #inner函数有了返回值
 7     return inner
 8 
 9 @auth1
10 def fetch_list(arg):
11     print "fetch_list"
12     server_list = [11,22,33]
13     return server_list
14 
15 ret = fetch_list('test')
16 print ret 
17 
18 结果
19 before
20 fetch_list
21 after
22 [11, 22, 33]

9 装饰器实现登录验证

主要就是增加了一个login函数

 1 def login():
 2     name = 'dicky'
 3     if name == 'dicky1':
 4         return True
 5     else:
 6         return False
 7 
 8 def auth3(func):
 9     def inner(*args,**kwargs):
10         is_login = login()
11         if not is_login:
12             return "invild"
13         temp = func(*args,**kwargs)
14         return temp
15     return inner
16 @auth3
17 def fetch_list(arg):
18     print "fetch_list"
19     server_list = [11,22,33]
20     return server_list
21 ret=fetch_list('test')
22 print ret
23 
24 结果
25 invild

10 装饰器实现token验证,跟上面的login差不多

 1 def login1(key):
 2     name = 'alex1'
 3     local = 'afcadfsgdgwegsfbdfgsgsfwe'
 4     if local == key:
 5         return True
 6     else:
 7         return False
 8 def auth3(func):
 9     def inner(*arg,**kwargs):
10         # key=kwargs['token']
11         # del kwargs['token']
12         key = kwargs.pop('token')  #删除多传入的参数
13         is_login = login1(key)
14         if not is_login:
15             return "invild"
16         print 'before'
17         temp = func(*arg,**kwargs)  #上面删除了多传入的参数,这里就不会报错了
18         print 'after'
19         return temp
20     return inner
21 
22 @auth3
23 def fetch_list(arg):
24     print "fetch_list"
25     server_list = [11,22,33]
26     return server_list
27 
28 key = 'afcadfsgdgwegsfbdfgsgsfwe'
29 ret = fetch_list('test',token=key)
30 print "llllllllll"
31 print ret

 11 多装饰器

先执行外部装饰器,然后在执行内部装饰器

 1 def w1(func):
 2     def inner():
 3         print 'w1 before'
 4         func()
 5         print 'w1 after'
 6     return inner
 7 def w2(func):
 8     def inner():
 9         print 'w2 after'
10         func()
11         print 'w2 after'
12     return inner
13 @w2
14 @w1
15 def foo():
16     print 'foo'
17 
18 foo()
19 结果
20 w2 after
21 w1 before
22 foo
23 w1 after
24 w2 after

12 装饰器附加参数

 1 def Before(request,kargs):
 2     print 'before'
 3       
 4 def After(request,kargs):
 5     print 'after'
 6   
 7   
 8 def Filter(before_func,after_func):
 9     def outer(main_func):
10         def wrapper(request,kargs):
11               
12             before_result = before_func(request,kargs)
13             if(before_result != None):
14                 return before_result;
15               
16             main_result = main_func(request,kargs)
17             if(main_result != None):
18                 return main_result;
19               
20             after_result = after_func(request,kargs)
21             if(after_result != None):
22                 return after_result;
23               
24         return wrapper
25     return outer
26       
27 @Filter(Before, After)
28 def Index(request,kargs):
29     print 'index'

过程如下

 @Filter(Before, After)

先执行Filter(Before, After)函数,获取返回值ret,获取返回值后,拼接成@ret,也就是相当于引入了两个参数,以至于后来可以使用这两个参数。

13 functools.wraps 的作用

上述的装饰器虽然已经完成了其应有的功能,即:装饰器内的函数代指了原函数,注意其只是代指而非相等,原函数的元信息没有被赋值到装饰器函数内部。例如:函数的注释信息

看下面的两个例子


def outer(func):
    def inner(*args, **kwargs):
        print(inner.__doc__)  # None
        return func()
    return inner

@outer
def function():
    """
    asdfasd
    :return:
    """
    print('func')

function()
结果
None #并没有输出__doc__的信息

加上@functools.wraps之后

  import functools
1
def outer(func): 2 @functools.wraps(func) 3 def inner(*args, **kwargs): 4 print(inner.__doc__) # None 5 return func() 6 return inner 7 8 @outer 9 def function(): 10 """ 11 asdfasd 12 :return: 13 """ 14 print('func') 15 16 function() 17 结果 18 asdfasd 19 :return: 20 21 func 22 23 #输出了原函数的__doc__内容

 二 递归

1 编写数列

1 斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233377610987159725844181676510946177112865746368
View Code

函数如下

1 def  func(arg1,arg2):
2     if arg1==0:
3         print arg1,arg2
4     arg3=arg1+arg2
5     print arg3
6     func(arg2,arg3)
7 
8 
9 func(0,1)
View Code

 2 递归的返回值

先看下面的一个简单例子

 1 def n5():
 2     return '1234'
 3 def n4():
 4     n5()
 5 def n3():
 6     n4()
 7 def n2():
 8     n3()
 9 def n1():
10     n2()
11 ret=n1()
12 print ret
13 
14 结果None

分析如下

上面的函数首先执行
n1()---n2()--n3()---n4()--n5()
到执行到n5的时候有返回值,n5函数的返回值返回给调用n5函数的函数n4
即res=n5()----def n4(): 但是n4没有返回值,n3也没有返回值,n2也没有返回值,n1也没有返回值,所以最后返回none
改进一下,让他有返回值

 1 def n5():
 2     return '1234'
 3 def n4():
 4     return n5()
 5 def n3():
 6     return n4()
 7 def n2():
 8     return n3()
 9 def n1():
10     return n2()
11 ret=n1()
12 print ret
13 
14 
15 结果
16 1234
17 这样让它每次都有返回值
View Code

 3 看下面的递归例子(改进了一下上边的数列例子)

 1 def  func(arg1,arg2):
 2     if arg1==0:
 3         print arg1,arg2
 4     arg3=arg1+arg2
 5     if arg3>1000:
 6         return arg3
 7     func(arg2,arg3)
 8 
 9 ret = func(0,1)
10 print ret
11 
12 结果为None

分析如下

执行第一次func(0,1)没有返回值
执行第二次func(1,1)也没有返回值
..........
当执行到func(610,987)的时候,接着执行上面,arg=1597,满足条件,return返回1597,谁调用的返回给谁,也就是返回给func(610,987),但是在这之前执行func()函数却没有返回值,跟上面的例子差不多,所以最后返回None

改进之后:

 1 def  func(arg1,arg2):
 2     if arg1==0:
 3         print arg1,arg2
 4     arg3=arg1+arg2
 5     if arg3>1000:
 6         return arg3
 7     return func(arg2,arg3)
 8 
 9 ret = func(0,1)
10 print ret
11 
12 结果
13 1597
View Code

 三模块

 模块分为三种:

  • 自定义模块
  • 内置模块
  • 开源模块

自定义模块

1

2

导入模块

1 import module
2 from module.xx.xx import xx
3 from module.xx.xx import xx as rename  
4 from module.xx.xx import *

导入模块其实就是告诉Python解释器去解释那个py文件

  • 导入一个py文件,解释器解释该py文件
  • 导入一个包,解释器解释该包下的 __init__.py 文件
1 模块路径
2 import sys
3 print sys.path
4 结果
5 ['F:\\untitled5\\test', 'F:\\untitled5', 'C:\\Windows\\system32\\python27.zip', 'C:\\Python27\\DLLs', 'C:\\Python27\\lib', 'C:\\Python27\\lib\\plat-win', 'C:\\Python27\\lib\\lib-tk', 'C:\\Python27', 'C:\\Python27\\lib\\site-packages', 'C:\\Python27\\lib\\site-packages\\pip-9.0.1-py2.7.egg']

常用内置模块

os模块

 1 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
 2 os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
 3 os.curdir  返回当前目录: ('.')
 4 os.pardir  获取当前目录的父目录字符串名:('..')
 5 os.makedirs('dirname1/dirname2')    可生成多层递归目录
 6 os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
 7 os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
 8 os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
 9 os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
10 os.remove()  删除一个文件
11 os.rename("oldname","newname")  重命名文件/目录
12 os.stat('path/filename')  获取文件/目录信息
13 os.sep    输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
14 os.linesep    输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
15 os.pathsep    输出用于分割文件路径的字符串
16 os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
17 os.system("bash command")  运行shell命令,直接显示
18 os.environ  获取系统环境变量
19 os.path.abspath(path)  返回path规范化的绝对路径
20 os.path.split(path)  将path分割成目录和文件名二元组返回
21 os.path.dirname(path)  返回path的目录。其实就是os.path.split(path)的第一个元素
22 os.path.basename(path)  返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
23 os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
24 os.path.isabs(path)  如果path是绝对路径,返回True
25 os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False
26 os.path.isdir(path)  如果path是一个存在的目录,则返回True。否则返回False
27 os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
28 os.path.getatime(path)  返回path所指向的文件或者目录的最后存取时间
29 os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间
View Code

sys模块

1 sys.argv           命令行参数List,第一个元素是程序本身路径
2 sys.exit(n)        退出程序,正常退出时exit(0)
3 sys.version        获取Python解释程序的版本信息
4 sys.maxint         最大的Int值
5 sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
6 sys.platform       返回操作系统平台名称
View Code

 

推荐阅读