Python-进阶-装饰器小结

基本概念

具体概念自己google

装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理, Web权限校验, Cache等。

很有名的例子,就是咖啡,加糖的咖啡,加牛奶的咖啡。
本质上,还是咖啡,只是在原有的东西上,做了“装饰”,使之附加一些功能或特性。

例如记录日志,需要对某些函数进行记录

笨的办法,每个函数加入代码,如果代码变了,就悲催了

装饰器的办法,定义一个专门日志记录的装饰器,对需要的函数进行装饰,搞定

优点

抽离出大量函数中与函数功能本身无关的雷同代码并继续重用

即,可以将函数“修饰”为完全不同的行为,可以有效的将业务逻辑正交分解,如用于将权限和身份验证从业务中独立出来

概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能

Python中的装饰器

在Python中,装饰器实现是十分方便的

原因是:函数可以被扔来扔去。

函数作为一个对象:

A.可以被赋值给其他变量,可以作为返回值

B.可以被定义在另外一个函数内

def:

装饰器是一个函数,一个用来包装函数的函数,装饰器在函数申明完成的时候被调用,调用之后返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问(申明的函数被换成一个被装饰器装饰过后的函数)

当我们对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。

由此我们可以用decorator改变某个原有函数的功能,添加各种操作,或者完全改变原有实现

分类:

装饰器分为无参数decorator,有参数decorator

* 无参数decorator

生成一个新的装饰器函数

* 有参decorator

有参装饰,装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰

装饰器有参/无参,函数有参/无参,组合共4种

具体定义:

decorator方法

A.把要装饰的方法作为输入参数,

B.在函数体内可以进行任意的操作(可以想象其中蕴含的威力强大,会有很多应用场景),

C.只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函数, 或者是一个新函数)

无参数装饰器 – 包装无参数函数

不需要针对参数进行处理和优化

1
2
3
4
5
6
7
def  decorator(func):
     print  "hello"
return  func
@decorator
def  foo():
     pass
foo()

foo()
等价于:

foo = decorator(foo)
foo()

无参数装饰器 – 包装带参数函数

1
2
3
4
5
6
7
8
9
10
def  decorator_func_args(func):
def  handle_args( * args,  * * kwargs):  #处理传入函数的参数
     print  "begin"
func( * args, * * kwargs)    #函数调用
     print  "end"
     return  handle_args
@decorator_func_args
def  foo2(a,b = 2 ):
     print  a,b
foo2( 1 )

foo2(1)
等价于

foo2 = decorator_func_args(foo2)
foo2(1)

带参数装饰器 – 包装无参数函数

1
2
3
4
5
6
7
8
9
10
11
def  decorator_with_params(arg_of_decorator): #这里是装饰器的参数
     print  arg_of_decorator
#最终被返回的函数
     def  newDecorator(func):
         print  func
         return  func
     return  newDecorator
@decorator_with_params ( "deco_args" )
def  foo3():
     pass
foo3()

与前面的不同在于:比上一层多了一层封装,先传递参数,再传递函数名

第一个函数decomaker是装饰函数,它的参数是用来加强“加强装饰”的。由于此函数并非被装饰的函数对象,所以在内部必须至少创建一个接受被装饰函数的函数,然后返回这个对象(实际上此时foo3= decorator_with_params(arg_of_decorator)(foo3))

带参数装饰器– 包装带参数函数

1
2
3
4
5
6
7
8
9
10
11
12
def  decorator_whith_params_and_func_args(arg_of_decorator):
     def  handle_func(func):
         def  handle_args( * args, * * kwargs):
             print  "begin"
             func( * args, * * kwargs)
             print  "end"
             print  arg_of_decorator,func,args,kwargs
         return  handle_args
     return  handle_func
@decorator_whith_params_and_func_args ( "123" )
def  foo4(a,b = 2 ):
     print  "Content" foo4( 1 ,b = 3 )

内置装饰器

内置的装饰器有三个:staticmethod,classmethod, property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class  A():
     @ staticmethod
     def  test_static():
         print  "static"
     def  test_normal( self ):
         print  "normal"
     @ classmethod
     def  test_class( cls ):
         print  "class" , cls
=  A()
A.test_static()
a.test_static()
a.test_normal()
a.test_class()

结果:

static
static
normal
class __main__.A

A.test_static

staticmethod 类中定义的实例方法变成静态方法

基本上和一个全局函数差不多(不需要传入self,只有一般的参数),只不过可以通过类或类的实例对象来调用,不会隐式地传入任何参数。

类似于静态语言中的静态方法

B.test_normal

普通对象方法:
普通对象方法至少需要一个self参数,代表类对象实例

C.test_class

类中定义的实例方法变成类方法

classmethod需要传入类对象,可以通过实例和类对象进行调用。

是和一个class相关的方法,可以通过类或类实例调用,并将该class对象(不是class的实例对象)隐式地当作第一个参数传入。

就这种方法可能会 比较奇怪一点,不过只要你搞清楚了python里class也是个真实地存在于内存中的对象,而不是静态语言中只存在于编译期间的类型,就好办了。正常的方法就是和一个类的实例对象相关的方法,通过类实例对象进行调用,并将该实例对象隐式地作为第一个参数传入,这个也和其它语言比较像。

D.区别

staticmethod,classmethod相当于全局方法,一般用在抽象类或父类中。一般与具体的类无关。

类方法需要额外的类变量cls,当有子类继承时,调用类方法传入的类变量cls是子类,而不是父类。

类方法和静态方法都可以通过类对象和类的实例对象访问

定义方式,传入的参数,调用方式都不相同。

E.property

对类属性的操作,类似于java中定义getter/setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class  B():
     def  __init__( self ):
         self .__prop  =  1
     @ property
     def  prop( self ):
         print  "call get"
     return  self .__prop
     @prop.setter
     def  prop( self ,value):
         print  "call set"
         self .__prop  =  value
     @prop.deleter
     def  prop( self ):
         print  "call del"
         del  self .__prop

其他

A.装饰器的顺序很重要,需要注意

1
2
3
4
@A
@B
@C
def  f():

等价于

f = A(B(C(f)))

B.decorator的作用对象可以是模块级的方法或者类方法

C.functools模块提供了两个装饰器。
这个模块是Python 2.5后新增的。

functools.wraps(func)
total_ordering(cls)
这个具体自己去看吧,后续用到了再补充

一个简单例子

通过一个变量,控制调用函数时是否统计时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#@author:wklken@yeah.net、
#@version: a test of decorator
#@date:20121027
#@desc:just a test
import  logging
from  time  import  time
logger  =  logging.getLogger()
logger.setLevel(logging.DEBUG)is_debug  =  True
def  count_time(is_debug):
     def  handle_func(func):
         def  handle_args( * args, * * kwargs):
             if  is_debug:
                 begin  =  time()
                 func( * args, * * kwargs)
                 logging.debug( "["  +  func.__name__  +  "] -> "  +  str (time()  -  begin))
             else :
                 func( * args, * * kwargs)
         return  handle_args
     return  handle_func
def  pr():
     for  in  range ( 1 , 1000000 ):
         =  *  2
         print  "hello world"
def  test():
     pr()
@count_time (is_debug)
def  test2():
     pr()
@count_time ( False )
def  test3():
     pr()
if  __name__  = =  "__main__" :
     test()
     test2()
     test3()

结果:

hello world
hello world
DEBUG:root:[test2] -> 0.0748538970947
hello world

The end!

转自: http://wklken.sinaapp.com/

本文转自 gswljy 51CTO博客,原文链接:http://blog.51cto.com/guoshiwei/1931093



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值