python装饰器总结

介绍

装饰器(decorator)是一种高级Python语法。可以对一个函数、方法或者类进行加工。
修饰器经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理, Web权限校验, Cache等。

装饰器的作用就是为已经存在的对象添加额外的功能。

装饰器的原理

在Python中,装饰器实现是十分方便。原因是:函数可以被扔来扔去。
Python的函数就是对象。

 Python 函数的另一个有趣的特性是,它们可以在另一个函数体内定义。

代码示例


def get_talk(type="shout"):
    # 我们先动态定义一些函数
    def shout(word="yes"):
        return word.capitalize() + "!"

    def whisper(word="yes"):
        return word.lower() + "..."

    # 然后返回其中一个
    if type == "shout":
        # 注意:我们是在返回函数对象,而不是调用函数,所以不要用到括号 "()"
        return shout
    else:
        return whisper

# 那你该如何使用呢?
# 先把函数赋值给一个变量
talk = get_talk()

# 你可以发现 "talk" 其实是一个函数对象:
print(talk)
# outputs : <function shout at 0xb7ea817c>

# 这个对象就是 get_talk 函数返回的:
print(talk())
# outputs : Yes!

# 你甚至还可以直接这样使用:
print(get_talk("whisper")())
# outputs : yes...

既然可以返回一个函数,那么也就可以像参数一样传递:

代码示例

def shout(word="yes"):
    return word.capitalize() + "!"

scream = shout

def do_something_before(func):
    print("I do something before then I call the function you gave me")
    print(func())

do_something_before(scream)
# outputs:
# I do something before then I call the function you gave me
# Yes!

装饰器实战

手写一个

# 一个装饰器是一个需要另一个函数作为参数的函数
def my_shiny_new_decorator(a_function_to_decorate):
    # 在装饰器内部动态定义一个函数:wrapper(原意:包装纸).
    # 这个函数将被包装在原始函数的四周
    # 因此就可以在原始函数之前和之后执行一些代码.
    def the_wrapper_around_the_original_function():
        # 把想要在调用原始函数前运行的代码放这里
        print("Before the function runs")

        # 调用原始函数(需要带括号)
        a_function_to_decorate()

        # 把想要在调用原始函数后运行的代码放这里
        print("After the function runs")

    # 直到现在,"a_function_to_decorate"还没有执行过 (HAS NEVER BEEN EXECUTED).
    # 我们把刚刚创建的 wrapper 函数返回.
    # wrapper 函数包含了这个函数,还有一些需要提前后之后执行的代码,
    # 可以直接使用了(It's ready to use!)
    return the_wrapper_around_the_original_function
# Now imagine you create a function you don't want to ever touch again.
def a_stand_alone_function():
    print("I am a stand alone function, don't you dare modify me")

a_stand_alone_function()
# outputs: I am a stand alone function, don't you dare modify me

# 现在,你可以装饰一下来修改它的行为.
# 只要简单的把它传递给装饰器,后者能用任何你想要的代码动态的包装
# 而且返回一个可以直接使用的新函数:

a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
# outputs:
# Before the function runs
# I am a stand alone function, don't you dare modify me
# After the function runs

装饰器语法糖

# 一个装饰器是一个需要另一个函数作为参数的函数
def my_shiny_new_decorator(a_function_to_decorate):
    # 在装饰器内部动态定义一个函数:wrapper(原意:包装纸).
    # 这个函数将被包装在原始函数的四周
    # 因此就可以在原始函数之前和之后执行一些代码.
    def the_wrapper_around_the_original_function():
        # 把想要在调用原始函数前运行的代码放这里
        print("Before the function runs")

        # 调用原始函数(需要带括号)
        a_function_to_decorate()

        # 把想要在调用原始函数后运行的代码放这里
        print("After the function runs")

    # 直到现在,"a_function_to_decorate"还没有执行过 (HAS NEVER BEEN EXECUTED).
    # 我们把刚刚创建的 wrapper 函数返回.
    # wrapper 函数包含了这个函数,还有一些需要提前后之后执行的代码,
    # 可以直接使用了(It's ready to use!)
    return the_wrapper_around_the_original_function


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


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

堆积使用装饰器

注意:装饰器放置的顺序也很重要。


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

    return wrapper


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

    return wrapper


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


sandwich()
# outputs:
# </''''''\>
# #tomatoes#
# --ham--
# ~salad~
# <\______/>

含参数的装饰器

# a new wrapper layer
def pre_str(pre=''):
    # old decorator
    def decorator(F):
        def new_F(a, b):
            print(pre + " input", a, b)
            return F(a, b)

        return new_F

    return decorator


# get square sum
@pre_str('^_^')
def square_sum(a, b):
    return a ** 2 + b ** 2


# get square diff
@pre_str('T_T')
def square_diff(a, b):
    return a ** 2 - b ** 2


print(square_sum(3, 4))
print(square_diff(3, 4))

# outputs:
# ('^_^ input', 3, 4)
# 25
# ('T_T input', 3, 4)
# -7

通用的装饰器

无视具体参数,直接使用 *args, **kwargs 就行:


def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    # The wrapper accepts any arguments
    def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
        print("Do I have args?:")
        print(args)
        print(kwargs)
        # Then you unpack the arguments, here *args, **kwargs
        # If you are not familiar with unpacking, check:
        # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
        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()
# outputs
# 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)
# outputs
# 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!")
# outputs
# 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_your_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_your_age()
# outputs
# Do I have args?:
# (<__main__.Mary object at 0xb7d303ac>,)
# {}
# I am 28, what did you think?

装饰类中的方法

Python的一个伟大之处在于:方法和函数几乎是一样的(methods and functions are really the same),除了方法的第一个参数应该是当前对象的引用(也就是 self)。这也就意味着只要记住把 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 say_your_age(self, lie):
        print("I am %s, what did you think?" % (self.age + lie))


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

装饰类

在上面的例子中,装饰器接收一个函数,并返回一个函数,从而起到加工函数的效果。在Python 2.6以后,装饰器被拓展到类。一个装饰器可以接收一个类,并返回一个类,从而起到加工类的效果


def decorator(aClass):
    class newClass:
        def __init__(self, age):
            self.total_display = 0
            self.wrapped = aClass(age)

        def display(self):
            self.total_display += 1
            print("total display", self.total_display)
            self.wrapped.display()

    return newClass


@decorator
class Bird:
    def __init__(self, age):
        self.age = age

    def display(self):
        print("My age is", self.age)


eagleLord = Bird(5)
for i in range(3):
    eagleLord.display()

内置装饰器

property 装饰器

property 装饰器用于类中的函数,使得我们可以像访问属性一样来获取一个函数的返回值。

class XiaoMing:
    @staticmethod
    def say_hello():
        print('同学你好')


XiaoMing.say_hello()

# 实例化调用也是同样的效果
# 有点多此一举
xiaoming = XiaoMing()
xiaoming.say_hello()

staticmethod 装饰器

staticmethod 装饰器同样是用于类中的方法,这表示这个方法将会是一个静态方法,意味着该方法可以直接被调用无需实例化,但同样意味着它没有 self 参数,也无法访问实例化后的对象。

class XiaoMing:
    @staticmethod
    def say_hello():
        print('同学你好')


XiaoMing.say_hello()

# 实例化调用也是同样的效果
# 有点多此一举
xiaoming = XiaoMing()
xiaoming.say_hello()

classmethod 装饰器

classmethod 依旧是用于类中的方法,这表示这个方法将会是一个类方法,意味着该方法可以直接被调用无需实例化,但同样意味着它没有 self 参数,也无法访问实例化后的对象。相对于 staticmethod 的区别在于它会接收一个指向类本身的 cls 参数。


class XiaoMing:
    name = '小明'

    @classmethod
    def say_hello(cls):
        print('同学你好, 我是' + cls.name)
        print(cls)

XiaoMing.say_hello()

wraps 装饰器

一个函数不止有他的执行语句,还有着 name(函数名),doc (说明文档)等属性,我们之前的例子会导致这些属性改变。

由于装饰器返回了 wrapper 函数替换掉了之前的 say_hello 函数,导致函数名,帮助文档变成了 wrapper 函数的了。解决这一问题的办法是通过 functools 模块下的 wraps 装饰器。


from functools import wraps

def decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """doc of wrapper"""
        print('123')
        return func(*args, **kwargs)

    return wrapper

@decorator
def say_hello():
    """doc of say hello"""
    print('同学你好')

print(say_hello.__name__)
print(say_hello.__doc__)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值