所谓模式,是一种编程约定,是经过长期实践验证能够解决某类问题的代码框架。
模式存在的意义,首先它能解决一些通用性的问题(使得代码能适应需求变化,能轻松扩展)。其次是它能帮助程序员之间对代码的沟通,比如你告诉某个程序员说这段代码使用了XX模式,因为大家都知道这个模式是怎么实现的,所以对方也会很快地理解代码的实现。
装饰器模式是什么样的一种模式呢?
在Java或者C#语言里,一个类完A成特定单一的功能,当我们需求有变,需要扩展这个类A,运用装饰器模式,只要新增一个完成扩展功能的类B,然后再把原有功能的A的对象a注入到新类B中,这样新类B就具有了原来A的特性,同时又扩展了新功能。当然,这样做原有的类代码不更改,我们更希望调用者User使用方式也不更改,则新扩展的类B要有一套更原有类一样的接口I,使用者仍然是使用旧接口,就可调用扩展功能。
例如A类实现了登录功能,登录时匹配用户名密码是否与数据库保存的一致,B类则扩展了该登录功能,它扩展了验证码机制,防止攻击性密集型登录。
因此,装饰器模式约定了
1、原有类与扩展类实现了同一个接口,例如上例中A和B都实现了登录接口,这样用户调用方式不变
2、扩展类可以注入旧类的对象(扩展一个类有2种方式,一种是继承旧类,一种是新类与旧类的对象进行组合),上例中把A类的对象a注入到B类中,B认证通过后调用a的登录接口,使用原有功能
3、扩展类可以被更新扩展的类注入,例如,当我们需要更多功能时,希望B也可以注入到新类中
言归正传,在Python语言里,装饰器特性已经不是模式约定那么简单了,它已经上升为一种语法,使用这样的语法就可以轻松实现装饰器模式。
在理解Python装饰器之前,Python的几个特性需要大家理解:
1、在Python里,任何东西都可以称为对象,类是对象,方法是对象,类的对象更是对象。
2、任何对象都可以当作方法来使用。类是方法,对象也是方法。
第1点大家就当作Python语言设计之初的一种理念好了,不必过分拘泥,第2点可能对初次接触Python的人来说,可能不好理解,下面用例子说明。
假如有一个类A,它重写了初始化函数__init__;重写了__call__方法,美其名曰实现了callable接口。
#coding:UTF-8
class A:
def __init__(self):
print 'init function'
def __call__(self):
print 'callable function'
#1、类可以当作方法使用
a = A() #其实就相当于调用了初始化函数__init__
#2、对象也可以作为方法使用
a() #其实就相当于调用了__call__方法
因此,类可以当作方法来使用,对象也可以当作方法来使用,方法自然也是可以当作方法来使用了
进入正题,什么是Python装饰器呢?
Python定义了一种语法来实现装饰器的(decorator)模式:
@[decorator]
def [function]():
do something
来看具体例子,这是一个最简单的Python装饰器语法的例子
def decorator(func):
def extendFunc():
print 'extend function'
func()
return extendFunc
@decorator
def function():
print 'this is ordinary function'
function()
输出结果是:
extend function
this is ordinary function
它很符合我们之前对装饰器模式的约定:
1、在扩展功能extendFunc中有一个原有功能function的对象,在不修改function的情况下可以扩展新的功能。
2、对用户来说,调用方式不变,仍然是调用function,却实现了extendFunc的功能。
对Python装饰器语法的结论就是。
1、装饰器decorator是一个方法(也可以是一个类,或者一个对象,我们上文说过,类也是方法,对象也是方法)。
2、装饰器decorator必须有一个入参,接受被装饰的方法
3、装饰器decorator必须返回一个方法(也可以返回一个类,或者一个对象,因为类也是方法,对象也是方法),该方法扩展了一些function没有的功能,同时也具有function的功能(当然你也可以不用function的功能)
4、在function被decorator装饰后,首先function被当作入参传递到了decorator,然后decorator返回一个方法extendFunc来代替function
5、由于function被extendFunc代替,以后在调用function的地方,其实就是调用extendFunc
那么Python装饰器最基本的东西就在上面体现了,现在我们来看复杂一点的,高级一点的:
一、跨类装饰(用一个类中的方法装饰另一个类的方法)
1、用Deco.dec来装饰myFunc,实际上是把myFunc作为参数传递给了Deco.dec方法,并返回另一个方法extendFunc
使得以后调用myFunc的地方会转调用extendFunc
2、由于装饰器语法的限制,用 Deco.dec装饰myFunc时必须用Deco类来引用(Deco.dec是通过类引用方法)
因此注定了dec方法为类方法
3、必须返回一个方法,这是装饰器的约定,否则在调用myFunc的时候就不知道调什么方法了
4、调用了myFunc的地方就转而调用extendFunc,所以在执行extendFunc的时候第一个实参是mycls
5、必须至少保留一个形参,因为你不知道调用者会传什么参数,所以我们这里可以使用可变参数*arg1和**arg2
6、被装饰的方法变成了普通方法,而装饰的方法则替代了被装饰的方法
二、用类来装饰方法
下面这个留给大家思考吧