python 装饰器学习

python 装饰器学习

@(Python学习笔记)[装饰器, stackoverflow]

本篇学习笔记主要是针对stackoverflow对于装饰器的经典回答1,然后结合自己的理解进行简单总结(其实大部分都是在翻译)。

准备工作

首先,要理解python的装饰器,要理解在Python中,函数也是一种对象,因此可以进行对象的操作。

def shout(arg="hello"):
    return arg
print(shout())      #输出   hello
scream = shout
print(scream())     #输出 hello
del shout
#print(shout())     #提示 NameError
print(scream())     #正常输出 hello

其次,python中一个函数可以在另外一个函数的内部定义。

def talk():
    def whisper():
        print("whisper...")
    whisper()
talk()
#输出 whisper...
#直接调用whisper()
try:
    whisper()
except NameError as e:
    print(e)
#输出 name 'whisper' is not defined

记住:python中函数可以
- 被赋值给变量
- 在另一个函数内部定义

ok,利用上述两点我们就可以做些有趣的事了,比如,用一个函数返回另一个函数

def talk(kind = "shout"):
    def shout(word="yes"):
        return word.capitalize()
    def whisper(word="yes"):
        return word.lower()
    if kind =="shout":
        return shout
    else:
        return whisper
shout = talk()
print(shout())  #输出 Yes

既然我们可以返回一个函数,那么我们是不是也可以把函数作为参数进行传递呢?答案是当然能。下面来看一下:

def scream(word="yes"):
    return word.capitalize()
def doSomethingBefore(func):
    print("先做一些别的事,然后再执行你给我的函数")
    print(func())
doSomethingBefore(scream)   
#输出:
#先做一些别的事,然后再执行你给我的函数
#Yes

好了,到现在为止准备工作基本完成。在python中装饰器其实是在扮演一个包裹者的角色,它可以让你在执行你包裹的函数之前和之后做一些事情但是不改变原有函数的结构。其实,原话是这样的:

Decorators are “wrappers”, which means that they let you execute code before and after the function they decorate without modifying the function itself.

装饰器基础知识

手写一个装饰器

#装饰器是把函数作为参数的函数
def my_shiny_new_decorator(a_func_to_decorate):
    def the_wrapper_around_the_original_function():
        print("doSomething before the func runs")
        a_func_to_decorate()
        print("doSomething after the func runs")
    return the_wrapper_around_the_original_function
def a_stand_alone_func():
    print("i'm a stand alone func")
a_stand_alone_func()
#输出:i'm a stand alone func
#下面,你可以把a_stand_alone_func作为参数传递为my_shiny_new_decorator,然后得到一个新的函数,
#这个新的函数已经把a_stand_alone_func装饰过了
a_stand_alone_func_decorated = my_shiny_new_decorator(a_stand_alone_func)
a_stand_alone_func_decorated()
#输出:
# doSomething before the func runs
# i'm a stand alone func
# doSomething after the func runs

在实际工作中,我们可能希望直接调用a_stand_alone_func()就能得到与a_stand_alone_func_decorated()相同的效果,那么,我们可以这么写:

a_stand_alone_func = my_shiny_new_decorator(a_stand_alone_func)
a_stand_alone_func()

实际中,python里的装饰器是这样用的:

@my_shiny_new_decorator
def another_stand_alone_func():
    print("leave me alone")
another_stand_alone_func()

对之前的工作都理解了之后,你会发现python中的装饰器其实只是一种缩写形式。
another_stand_alone_func = my_shiny_new_decorator(another_stand_alone_func)简化了而已。

装饰器的叠加使用

装饰器的叠加跟单个使用的原理是一样的,只是给一个被装饰的函数多套上基层东西而已。

#来个手抓饼的装饰器
#来个饼
def cake(func):
    def wrapper():
        print(" /....\\")
        func()
        print("\\...../")
    return wrapper
#加蛋和蔬菜
def ingredients(func):
    def wrapper():
        print("~vegetables~")
        func()
        print("..eggs..")
    return wrapper
def hand_cake(food="ham"):
    print(food)
hand_cake = cake(ingredients(hand_cake))
hand_cake()
#输出:
# /....\
# ~vegetables~
# ham
# ..eggs..
# \...../

直接使用装饰器语法:

@cake
@ingredients
def hand_cake(food="ham"):
    print(food)
hand_cake()

注意:装饰器的位置对于最后的输出结果是有影响的。

装饰器进阶

向装饰器中被装饰的函数传递参数

有时我们会对带有参数的函数进行装饰,这个时候在写装饰器的时候,wrapper需要是带参的。

def a_decorator_passing_arguments(func_to_decorate):
    def wrapper(arg1,arg2):
        print("dosomething before func runs")
        func_to_decorate(arg1,arg2)
        print("dosomething after func runs")
    return wrapper

@a_decorator_passing_arguments
def printFullName(firstname,lastname):
    print("my name is {0} {1}".format(firstname,lastname))
printFullName("peter",'pan')
#输出:
# dosomething before func runs
# my name is peter pan
# dosomething after func runs

当在调用printFullName(firstname,lastname)的时候,其实是在调用wrapper(arg1,arg2)

装饰方法

对类中的方法进行装饰,其实跟直接装饰函数很像,只是在方法中第一个参数是self,这一点注意一下就行了。

def method_friendly_decorator(method_to_decorate):
    def wrapper(self, lie):
        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 sayYourAge(self, lie):
        print("I am {0}, what did you think?".format(self.age + lie))

l = Lucy()
l.sayYourAge(-3)
#outputs: I am 26, what did you think?

向装饰器传递参数

注意,这与上面提到的向被装饰的函数中传递参数是不一样的。

首先来回顾一下,当我们在使用@func_decorator时发生了什么?

def func_decorator(func):
    print("I am an ordinary function")
    def wrapper():
        print("I am function returned by the decorator")
        func()
    return wrapper


@func_decorator
def lazy_function():
    print("zzzzzzzz")
#outputs: I am an ordinary function

很明显,当我们在@func_decorator时,对应的函数func_decorator会被执行。
记住这一点,接下来的会好理解一点。

def decorator_maker():

    print("I make decorators! I am executed only once: "
          "when you make me create a decorator.")

    def my_decorator(func):

        print("I am a decorator! I am executed only when you decorate a function.")

        def wrapped():
            print("I am the wrapper around the decorated function. "
                  "I am called when you call the decorated function. "
                  "As the wrapper, I return the RESULT of the decorated function.")
            return func()

        print("As the decorator, I return the wrapped function.")

        return wrapped

    print("As a decorator maker, I return a decorator")
    return my_decorator
#调用decorator_maker(),看看会发生什么
new_decorator = decorator_maker()
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator

#然后用生成的装饰器装饰函数
def decorated_function():
    print("I am the decorated function.")
decorated_function = new_decorator(decorated_function)
#outputs:
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function

#最后是调用这个函数
decorated_function()
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

下面用@的语法重新写一次

@decorator_maker()
def decorated_function():
    print("I am the decorated function.")
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.
decorated_function()    
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

OK,万事俱备,只欠参数了。上面的都理解了,其实传参就很简单了。下面看一个例子。

def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):

    print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))

    def my_decorator(func):
        # The ability to pass arguments here is a gift from closures.
        # If you are not comfortable with closures, you can assume it’s ok,
        # or read: http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
        print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))

        # Don't confuse decorator arguments and function arguments!
        def wrapped(function_arg1, function_arg2) :
            print("I am the wrapper around the decorated function.\n"
                  "I can access all the variables\n"
                  "\t- from the decorator: {0} {1}\n"
                  "\t- from the function call: {2} {3}\n"
                  "Then I can pass them to the decorated function"
                  .format(decorator_arg1, decorator_arg2,
                          function_arg1, function_arg2))
            return func(function_arg1, function_arg2)

        return wrapped

    return my_decorator
@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
    print("I am the decorated function and only knows about my arguments: {0}"
           " {1}".format(function_arg1, function_arg2))

decorated_function_with_arguments("Rajesh", "Howard")
#outputs:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#I am the wrapper around the decorated function. 
#I can access all the variables 
#   - from the decorator: Leonard Sheldon 
#   - from the function call: Rajesh Howard 
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard

看懂了吗?其实就是在原本装饰器的基础上,再在外边套了一层函数,然后在@decorator_maker_with_arguments("Leonard", "Sheldon")的时候,调用了这个函数,然后返回了一个装饰器。这里还用到了闭包的思想,就是在返回装饰器的时候,传递到decorator_maker_with_arguments中的两个参数Leonard,Sheldon已经脱离了原有的函数,但是他们依然能起作用。

ok,以上就是我对python中装饰器的认识,刚开始学习,如有错误,欢迎指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值