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

作者: Kulbear | 来源:发表于2016-07-30 20:17 被阅读864次

    讲在开始


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

    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

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

    相关文章

      网友评论

      • bbjoe:楼主推荐的那本书有中文译本了额。。。https://github.com/eastlakeside/interpy-zh
        Kulbear:@bbjoe 嗯应该早就有了 :yum:
      • bbjoe:值得回看。阅读一遍记不住。。谢谢翻译。
        Kulbear:@bbjoe 我的失误,已更新
        bbjoe:@Kulbear 其实我点开了答案链接。。但我发现并不能很快分辨出你到底翻译的是哪一个
        Kulbear:@bbjoe 嗯…原答案现在大概完了一半多点,写的确实好,我来搬运:yum:

      本文标题:【译】Python装饰方法漫谈(二)

      本文链接:https://www.haomeiwen.com/subject/xyyojttx.html