python: 设计模式(design pattern)之修饰器模式(decorator)

来源:互联网 发布:苹果激活策略查询软件 编辑:程序博客网 时间:2024/05/17 01:12

修饰器模式是面向对象编程领域的一种设计模式。 通过向一个类对象动态的添加新的行为而实现。 修饰器可以给某个对象添加一些功能, 而不是整个类添加功能, 所以很灵活。 

在python中, 我们可以使用decorators对callable的objects(注意包括functions, methods, 或者类classes)进行一些简单的修饰, 使得这些objects具有一些新的行为。

要理解decorators, 我们必须首先知道, python中, functions也是一个对象, 就想string, variables, class , integers等一样。 函数是根据给定的参数吐出产生的值的。

我们可以将一个函数作为一个参数传递给另一个函数, 也可以将一个函数赋值给一个变量, 或者函数返回一个函数。 

如下, 返回一个函数:

def foobar(original_function):     # make a new function    def new_function():        # some code     return new_function

decorator的定义:

一个decorator就是一个function, 吃callable object作为其参数, 然后将添加修饰后的callable obeject(例如函数)作为一个value返回去。

如下例就是对类进行修饰。例如, 下面就是一个修饰器:

def verbose(original_function):     # make a new function that prints a message when original_function starts and finishes    def new_function(*args, **kwargs):        print("Entering", original_function.__name__)        original_function(*args, **kwargs)        print("Exiting ", original_function.__name__)     return new_function

要使用decorator也很简单。 

我们可以将一个函数传进去, 得到新一个新的修饰后的函数talkative_widget_func。 如下:

def widget_func():    # some code talkative_widget_func = verbose(widget_func)


另外, 注意, 我们不光可以使用一个新的修饰后的函数去替换掉原有的函数, 我们还可以去得到一个增强版的原始的函数, 注意, 这不是新的函数, 而是在原来函数的基础上加上了一层封装, 如下:

def widget_func():    # some code widget_func = verbose(widget_func)
注意上述返回的函数命名为widget_func, 和传进去的原始函数一样。


除此之外, python还有一个decortation syntax。 也就是使用“@”去创建decoration lines. 这个feature就是语法糖(syntax sugar), 是的我们可以将上面的例子重新写为如下形式:

@verbosedef widget_func():    # some code

上述产生的结果和上面的增强版本一样了。 这样我们的新的widget_func函数就具有原始的widget_func所有的行为在加上varbose修饰器添加的行为了。

不光可以修饰函数, 也可以对类进行修饰。程序如下:

#!/usr/bin/python#decotor design patternimport sysYELLOW = '\033[93m'RED = '\033[91m'NORMAL = '\033[0m'class Person(object):    def __init__(self, name, age): # constructor        self.name = name        self.age = age        def __str__(self):        return '%s is %s' %(self.name, self.age)# decorator class to wrapps an object passed in#for age < 20, print NOEMAL#for 20 < age < 20, print YELLOW#for age > 30, print REDclass PersonDecorator(Person):    def __init__(self, person):        self._person = person            def __getattr__(self, name):        return getattr(self._person, name)            def __str__(self):        age = self._person.age        colour = NORMAL        if age >= 30:            colour = RED        elif age >= 20:            colour = YELLOW                return '%s%s%s' %(colour, self._person.__str__(), NORMAL)def main():    p = [] # list of person    p.append(Person('Micheal', 25))    p.append(Person('Kate', 2))    p.append(Person('Mark', 48))    p.append(Person('Matt', 21))        for person in p:        if '-c' in sys.argv:            person = PersonDecorator(person)        print person        if __name__ == '__main__':    main()
运行结果如下:


附录: 解释(stackoverflow摘录)

*args and **kwargs

语法* 和 **默认使用的。 基本上学习python的时候用不上。
当你不确定函数中应该传进去几个参数的时候,可以使用它。 即it allows you to pass an arbitary number of arguments to your function:
如下:

Similarly, **kwargs allows you to handle named arguments that you have not defined in advance:

如下:

>>> def table_things(**kwargs):...     for name, value in kwargs.items():...         print '{0} = {1}'.format(name, value)...>>> table_things(apple = 'fruit', cabbage = 'vegetable')cabbage = vegetableapple = fruit
You can also use both in the same function definition but *args must occur before **kwargs.

http://www.wklken.me/posts/2013/12/21/how-to-use-args-and-kwargs-in-python.html

解释如下:在函数中使用*和**是用于传递可变长参数, *args用于传递非命名剪枝可变长参数列表。 **kwargs用作传递键值可变长参数列表。 如下, 传递一个位置参数和两个可变长参数。

def test_var_args(farg, *args):    print "formal arg:", farg    for arg in args:        print "another arg:", argtest_var_args(1, "two", 3)





def test_var_kwargs(farg, **kwargs):    print "formal arg:", farg    for key in kwargs:        print "another keyword arg: %s: %s" % (key, kwargs[key])test_var_kwargs(farg=1, myarg2="two", myarg3=3)

运行结果如下:

上述语法也可以出现在调用函数中, 如下:

def test_var_args_call(arg1, arg2, arg3):    print "arg1:", arg1    print "arg2:", arg2    print "arg3:", arg3args = ("two", 3)test_var_args_call(1, *args)
如下:


或者:

def test_var_args_call(arg1, arg2, arg3):    print "arg1:", arg1    print "arg2:", arg2    print "arg3:", arg3kwargs = {"arg3": 3, "arg2": "two"} #ie dictionarytest_var_args_call(1, **kwargs)


0 0
原创粉丝点击