【译】Python装饰方法漫谈(二)

讲在开始


这是针对以下内容的第二部分翻译

http://stackoverflow.com/a/1594484

基础部分在这里:

http://www.jianshu.com/p/c45fd47f3b85

原答案代码2.7版本下可以全部运行成功,我将其中的代码用3.5版本重写,并改正了一些没有遵循PEP8规范的代码,以便自己学习理解的更深入一些

揭秘


在之前的例子中,我们直接使用的Python的装饰器语法:

@my_shiny_new_decorator
def another_stand_alone_function():
    print("Leave me alone")


another_stand_alone_function()
# 输出 :
# Before the function runs
# Leave me alone
# After the function runs

以上就是全部了,而且很简单直观,@decorator的用法,就是简写了

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

Python中的装饰器是decorator design pattern的Python实现。当然,Python中还有很多对设计模式的实现,比如迭代器(iterator)。

下面给出一个叠加装饰器的简单例子:

def bread(func):
    def wrapper():
        print("")
        func()
        print("<\______/>")

    return wrapper


def ingredients(func):
    def wrapper():
        print("#tomatoes#")
        func()
        print("~salad~")

    return wrapper


def sandwich(food="--ham--"):
    print(food)


sandwich()
# 输出 : --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
# 输出 :
# 
# #tomatoes#
# --ham--
# ~salad~
# <\______/>

更Pythonic的用法:

@bread
@ingredients
def sandwich(food="--ham--"):
    print(food)
sandwich()

设置装饰器的顺序不能随意设定,因为

@ingredients
@bread
def sandwich(food="--ham--"):
    print(food)
sandwich()
#输出 :
##tomatoes#
#
# --ham--
#<\______/>
# ~salad~

和上一个例子的运行结果是不同的。

进阶


仅仅满足于现状是不够的,如果你希望学到更深层更Pythonic的用法,请继续读下去。

向装饰器函数中传入参数

# 这没什么神奇的,你只需要让负责“包装”的函数来传递参数就可以了
def a_decorator_passing_arguments(function_to_decorate):
    def a_wrapper_accepting_arguments(arg1, arg2):
        print("I got args! Look:", arg1, arg2)
        function_to_decorate(arg1, arg2)

    return a_wrapper_accepting_arguments


@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
    print("My name is", first_name, last_name)


print_full_name("Peter", "Venkman")
# 输出 :
# I got args! Look: Peter Venkman
# My name is Peter Venkman


def another_print_full_name(first_name, last_name):
    print("My name is", first_name, last_name)

# 直白版
a_decorator_passing_arguments((another_print_full_name("Jim", "Raynor")))
# 输出 :
# My name is Jim Raynor

在Python中一个有趣的事情是,无论是函数还是方法(指类中的方法)其实都是一样的。唯一的区别是方法的第一个参数是对当前对象的引用(即约定俗成的self)

因此,既然我们可以编写装饰函数,同样我们也可以为方法编写装饰器,只是这次我们要考虑到self的存在

def method_friendly_decorator(method_to_decorate):
    def wrapper(self, lie):
        lie -= 3  # very friendly, decrease age even more :-)
        return method_to_decorate(self, lie)

    return wrapper


class Lucy(object):
    def __init__(self):
        self.age = 32

    @method_friendly_decorator
    def say_age(self, lie):
        print("I am %s, what did you think?" % (self.age + lie))


l = Lucy()
l.say_age(-3)
# 输出 : I am 26, what did you think?

Kulbear:插一句,从注释和例子中感受一下老外的幽默感?反正我在国外待了很久仍然觉得这个有的时候会很冷...

Kulbear:在下一段开始之前,如果你不明白什么是args和什么是kwargs,可以参考这里

http://book.pythontips.com/en/latest/args_and_kwargs.html

如果你希望你的装饰器(decorator)可以更通用一些,比如你不希望受到参数数量的制约,那么只需要使用*args和**kwargs就可以了:

def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    # 这个“包装”函数接受任意参数(数量,形式)
    def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
        print("Do I have args?:")
        print(args)
        print(kwargs)
        # 现在你将这些参数传入
        function_to_decorate(*args, **kwargs)

    return a_wrapper_accepting_arbitrary_arguments


@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
    print("Python is cool, no argument here.")


function_with_no_argument()
# 输出 :
# Do I have args?:
# ()
# {}
# Python is cool, no argument here.


@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
    print(a, b, c)


function_with_arguments(1, 2, 3)
# 输出 :
# Do I have args?:
# (1, 2, 3)
# {}
# 1 2 3


@a_decorator_passing_arbitrary_arguments
def function_with_named_arguments(a, b, c, platypus="Why not ?"):
    print("Do %s, %s and %s like platypus? %s" % \
          (a, b, c, platypus))


function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")
# 输出 :
# Do I have args ? :
# ('Bill', 'Linus', 'Steve')
# {'platypus': 'Indeed!'}
# Do Bill, Linus and Steve like platypus? Indeed!


class Mary(object):
    def __init__(self):
        self.age = 31

    @a_decorator_passing_arbitrary_arguments
    def say_age(self, lie=-3):  # You can now add a default value
        print("I am %s, what did you think ?" % (self.age + lie))


m = Mary()
m.say_age()
# 输出 :
# Do I have args?:
# (<__main__.Mary object at 0xb7d303ac>,)
# {}
# I am 28, what did you think?

后记


在这里,再次推荐一个Python进阶的读物,感谢我看了半天也不会读名字的作者 Muhammad Yasoob Ullah Khalid

http://book.pythontips.com/en/latest/index.html

后面的部分会继续翻译,敬请期待

你可能感兴趣的:(【译】Python装饰方法漫谈(二))