Python 装饰器 函数

Python装饰器学习(九步入门):http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html
浅谈Python装饰器:https://blog.csdn.net/mdl13412/article/details/22608283
Python装饰器与面向切面编程:http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html

一些更加实用的 Python 装饰器示例https://sikasjc.github.io/2018/09/29/somedecorator/

Python装饰器高级版—Python类内定义装饰器并传递self参数:https://blog.51cto.com/yishi/2354752

Python 装饰器学习(九步入门)

这是在 Python学习小组上介绍的内容,现学现卖、多练习是好的学习方式。

第一步:最简单的函数,准备附加额外功能

示例代码:

# -*- coding:gbk -*-
'''示例1: 最简单的函数,表示调用了两次'''


def deco(func):
    print("before myfunc() called.")
    func()
    print("after myfunc() called.")
    return func


def myfunc():
    print("     myfunc() called.")


print('*****************************')
dec_func = deco(myfunc)

print('*****************************')
dec_func()


"""
结果:
*****************************
before myfunc() called.
     myfunc() called.
after myfunc() called.
*****************************
     myfunc() called.
"""

第二步:使用装饰函数在函数执行前和执行后分别附加额外功能

示例代码:

# -*- coding:gbk -*-
'''示例2: 替换函数(装饰)
装饰函数的参数是被装饰的函数对象,返回原函数对象
装饰的实质语句: myfunc = deco(myfunc)'''


def deco(func):
    print("before myfunc() called.")
    func()
    print("after myfunc() called.")
    return func


def myfunc():
    print("    myfunc() called.")


dec_func = deco(myfunc)
print('****************************')
dec_func()
dec_func()

"""
结果:
before myfunc() called.
    myfunc() called.
after myfunc() called.
****************************
    myfunc() called.
    myfunc() called.
"""

第三步:使用语法糖@来装饰函数

本例中 deco 没有使用内嵌函数,可以看到第一次执行可以进入到装饰函数,但是第二次不会进入装饰函数

# -*- coding:gbk -*-
'''示例3: 使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
但发现新函数只在第一次被调用,且原函数多调用了一次'''


def deco(func):
    print("before myfunc() called.")
    func()
    print("after myfunc() called.")
    return func


@deco
def myfunc():
    print("    myfunc() called.")


# 第一次调用后,返回的是 deco 里面的 func 对象,
# 所以第二次调用myfunc() 只输出 myfunc() called.
myfunc()  # 第一次调用
print('************************')
myfunc()  # 第二次调用

"""
结果:
before myfunc() called.
    myfunc() called.
after myfunc() called.
    myfunc() called.
************************
    myfunc() called.
"""

第四步:使用 内嵌包装函数 来确保 每次 新函数 都被调用( 被装饰的函数没有参数 )

装饰函数 deco 返回内嵌包装函数对象 _deco。使用内嵌包装函数来确保每次新函数都被调用

# -*- coding:gbk -*-
'''示例4: 使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''


def deco(func):
    def _deco():
        print("before myfunc() called.")
        func()
        print("after myfunc() called.")
        # 不需要返回func,实际上应返回原函数的返回值
    return _deco


@deco
def myfunc():
    print("    myfunc() called.")
    return 'ok'


myfunc()
print('*************************')
myfunc()

"""
执行结果:
before myfunc() called.
    myfunc() called.
after myfunc() called.
*************************
before myfunc() called.
    myfunc() called.
after myfunc() called.
"""

示例代码:无参数的函数( 被装饰的函数没有参数 )

# decorator.py

from time import ctime, sleep


def time_fun(func):
    def wrapped_func():
        print(f"{func.__name__} called at {ctime()}")
        return func()
    return wrapped_func


@time_fun
def foo():
    pass


foo()
sleep(2)
foo()

第五步:对 带参数的函数 进行装饰( 被装饰的函数带参数  )

示例代码:

# -*- coding:gbk -*-
'''示例5: 对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''


def deco(func):
    def _deco(a, b):
        print("before myfunc() called.")
        ret = func(a, b)
        print("after myfunc() called. result: %s" % ret)
        return ret

    return _deco


@deco
def myfunc(a, b):
    print("    myfunc(%s,%s) called." % (a, b))
    return a + b


myfunc(1, 2)
print('*************************')
myfunc(3, 4)

"""
执行结果:
before myfunc() called.
    myfunc(1,2) called.
after myfunc() called. result: 3
*************************
before myfunc() called.
    myfunc(3,4) called.
after myfunc() called. result: 7
"""

示例代码:被装饰的函数带参数 

# decorator2.py

from time import ctime, sleep


def time_fun(func):
    def wrapped_func(a, b):
        print(f"{func.__name__} called at {ctime()}")
        print(a, b)
        return func(a, b)

    return wrapped_func


@time_fun
def foo(a, b):
    print(a + b)


foo(3, 5)
sleep(2)
foo(2, 4)

第六步:对 参数数量不确定 的函数进行装饰

示例代码:

# -*- coding:gbk -*-
'''示例6: 对参数数量不确定的函数进行装饰,
参数用(*args, **kwargs),自动适应变参和命名参数'''


def deco(func):
    def _deco(*args, **kwargs):
        print("before %s called." % func.__name__)
        ret = func(*args, **kwargs)
        print("after %s called. result: %s" % (func.__name__, ret))
        return ret

    return _deco


@deco
def myfunc_1(a, b):
    print("    myfunc_1(%s,%s) called." % (a, b))
    return a + b


@deco
def myfunc_2(a, b, c):
    print("    myfunc_2(%s,%s,%s) called." % (a, b, c))
    return a + b + c


print('*' * 30)
myfunc_1(1, 2)
print('*' * 30)
myfunc_1(3, 4)
print('*' * 30)
myfunc_2(1, 2, 3)
print('*' * 30)
myfunc_2(3, 4, 5)

"""
执行结果:
******************************
before myfunc_1 called.
    myfunc_1(1,2) called.
after myfunc_1 called. result: 3
******************************
before myfunc_1 called.
    myfunc_1(3,4) called.
after myfunc_1 called. result: 7
******************************
before myfunc_2 called.
    myfunc_2(1,2,3) called.
after myfunc_2 called. result: 6
******************************
before myfunc_2 called.
    myfunc_2(3,4,5) called.
after myfunc_2 called. result: 12
"""

第七步:让 装饰器 带 参数

示例代码:

# -*- coding:gbk -*-
'''示例7: 在示例4的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''


def deco(arg):
    def _deco(func):
        def __deco():
            print("before %s called [%s]." % (func.__name__, arg))
            func()
            print("after %s called [%s]." % (func.__name__, arg))

        return __deco

    return _deco


@deco("mymodule")
def myfunc():
    print("    myfunc_1() called.")


@deco("module2")
def myfunc2():
    print("    myfunc_2() called.")


print('************************************')
myfunc()
print('************************************')
myfunc2()

"""
执行结果:
************************************
before myfunc called [mymodule].
    myfunc_1() called.
after myfunc called [mymodule].
************************************
before myfunc2 called [module2].
    myfunc_2() called.
after myfunc2 called [module2].
"""

装饰器带参数,在原有装饰器的基础上,设置外部变量

from time import ctime, sleep


def time_fun_arg(pre="hello"):
    def time_fun(func):
        def wrapped_func():
            print("%s called at %s %s"%(func.__name__, ctime(), pre))
            return func()
        return wrapped_func
    return time_fun


@time_fun_arg("12345")
def foo():
    pass


@time_fun_arg("abcde")
def too():
    pass


foo()
sleep(2)
foo()


too()
sleep(2)
too()

第八步:让 装饰器 带 类 参数

示例代码:

# -*- coding:gbk -*-
'''示例8: 装饰器带类参数'''


class Locker:
    def __init__(self):
        print("locker.__init__() should be not called.")

    @staticmethod
    def acquire():
        print("locker.acquire() called.(这是静态方法)")

    @staticmethod
    def release():
        print("locker.release() called.(不需要对象实例)")


def deco(cls):
    '''cls 必须实现acquire和release静态方法'''

    def _deco(func):
        def __deco():
            print("before %s called [%s]." % (func.__name__, cls))
            cls.acquire()
            try:
                return func()
            finally:
                cls.release()

        return __deco

    return _deco


@deco(Locker)
def myfunc():
    print("    myfunc() called.")


print('*********************************************')
myfunc()
print('*********************************************')
myfunc()

"""
执行结果:
*********************************************
before myfunc called [<class '__main__.Locker'>].
locker.acquire() called.(这是静态方法)
    myfunc() called.
locker.release() called.(不需要对象实例)
*********************************************
before myfunc called [<class '__main__.Locker'>].
locker.acquire() called.(这是静态方法)
    myfunc() called.
locker.release() called.(不需要对象实例)
"""

装饰器 和 闭包 混用:

# coding=utf-8
from time import time


def logged(when):
    def log(f, *args, **kargs):
        print("fun:%s args:%r kargs:%r" % (f, args, kargs))
        # %r字符串的同时,显示原有对象类型

    def pre_logged(f):
        def wrapper(*args, **kargs):
            log(f, *args, **kargs)
            return f(*args, **kargs)
        return wrapper

    def post_logged(f):
        def wrapper(*args, **kargs):
            now = time()
            try:
                return f(*args, **kargs)
            finally:
                log(f, *args, **kargs)
                print("time delta: %s" % (time() - now))
        return wrapper

    try:
        return {"pre": pre_logged, "post": post_logged}[when]
    except BaseException as e:
        print('must be "pre" or "post"')
        raise e


@logged("post")
def fun(name):
    print("Hello, ", name)


fun("world!")

第九步:装饰器带类参数,并分拆公共类到其他py文件中,

同时 演示了对一个函数应用多个装饰器

mylocker.py

# -*- coding:gbk -*-


class MyLocker:
    def __init__(self):
        print("mylocker.__init__() called.")

    @staticmethod
    def acquire():
        print("mylocker.acquire() called.")

    @staticmethod
    def unlock():
        print("mylocker.unlock() called.")


class LockerEx(MyLocker):
    @staticmethod
    def acquire():
        print("lockerex.acquire() called.")

    @staticmethod
    def unlock():
        print("lockerex.unlock() called.")


def lock_helper(cls):
    """cls 必须实现acquire和release静态方法"""

    def _deco(func):
        def __deco(*args, **kwargs):
            print("before %s called." % func.__name__)
            cls.acquire()
            try:
                return func(*args, **kwargs)
            finally:
                cls.unlock()

        return __deco

    return _deco

测试代码:

# -*- coding:gbk -*-

"""
示例 9: 装饰器带类参数,并分拆公共类到其他py文件中
同时演示了对一个函数应用多个装饰器
"""


class Example:
    @lock_helper(MyLocker)
    def func_1(self):
        print("\nfunc_1() called.")

    @lock_helper(MyLocker)
    @lock_helper(LockerEx)
    def func_2(self, a, b):
        print("\nfunc_2() called.")
        return a + b


if __name__ == "__main__":
    a = Example()
    a.func_1()
    print(a.func_1())
    print(a.func_2(1, 2))
    print(a.func_2(3, 4))


"""
执行结果:
before func_1 called.
mylocker.acquire() called.
    func_1() called.
mylocker.unlock() called.
before func_1 called.
mylocker.acquire() called.
    func_1() called.
mylocker.unlock() called.
None
before __deco called.
mylocker.acquire() called.
before func_2 called.
lockerex.acquire() called.
    func_2() called.
lockerex.unlock() called.
mylocker.unlock() called.
3
before __deco called.
mylocker.acquire() called.
before func_2 called.
lockerex.acquire() called.
    func_2() called.
lockerex.unlock() called.
mylocker.unlock() called.
7
"""

下面是参考资料,当初有不少地方没看明白,真正练习后才明白些:

1. Python装饰器学习:http://blog.csdn.net/thy38/article/details/4471421

2. Python装饰器与面向切面编程:http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html

3. Python装饰器的理解:http://apps.hi.baidu.com/share/detail/17572338

Python 装饰器的 4 种类型

  • 函数  装饰  函数
  • 函数  装饰 
  •   装饰  函数
  •   装饰 

@wraps(func) 要使用这个必须导入functools,这个作用是消除(被装饰后的函数名等属性的改变)副作用

参考:https://blog.csdn.net/freeking101/article/details/109662542

from functools import wraps


def decorate(src_func):
    @wraps(src_func)
    def wrapper():
        print("hello")
        src_func()
        print("world")

    return wrapper


@decorate
def func():
    print("2019-12-31")


print(func.__name__, func.__doc__)

# 加上 @wraps(src_func)   输出为func None
# 不加  输出为 wrapper None

一:函数 装饰 函数

def wrapFun(func):
    def inner(a, b):
        print('function name:', func.__name__)
        r = func(a, b)
        return r
    return inner

@wrapFun
def myadd(a, b):
    return a + b

print(myadd(2, 3))

装饰不带参数的函数

# -*- coding: utf-8 -*-

def clothes(func):
    def wear():
        print('Buy clothes!{}'.format(func.__name__))
        return func()
    return wear


@clothes
def body():
    print('The body feels cold!')

body()
    
#备注:@是语法糖
# 不用语法糖的情况下,使用下面语句也能实现装饰作用:把body再加工,再传给body
# body = clothes(body)

示例 :

def deco(func):
    def _deco(*args, **kwargs):
        print('call deco')
        func(*args, **kwargs)
    return _deco


@deco
def test():
    print('call test')

# 等同于
# def test():
#     print('call test')
# test = deco(func)

装饰带一个参数的函数

# -*- coding: utf-8 -*-

def clothes(func):
    def wear(anything):  # 实际是定义一个anything参数,对应body函数参数
        print('Buy clothes!{}'.format(func.__name__))
        return func(anything)  # 执行func函数,并传入调用传入的anything参数
        # wear = func(anything)    # 在这一步,实际上可以看出来,为啥wear必须带参数,因为它就是func(anything)

    return wear
    # 所以clothes的结果是
    # clothes = wear = func(anything)
    # 不用语法糖的情况下就是
    # body = clothes(body)('hands')
    # 进一步证明:print(body.__name__)  显示的是wear函数


@clothes
def body(part):
    print('The body feels could!{}'.format(part))


body('hands')

装饰带不定长参数的函数

通常装饰器不只装饰一个函数,每个函数参数的个数也不相同,这个时候使用不定长参数*args,**kwargs

def clothes(func):
    def wear(*args, **kwargs):
        print('Buy clothes!{}'.format(func.__name__))
        return func(*args, **kwargs)

    return wear


@clothes
def body(part):
    print('The body feels could!{}'.format(part))


@clothes
def head(head_wear, num=2):
    print('The head need buy {} {}!'.format(num, head_wear))


body('hands')
head('headdress')

装饰器带参数

# 把装饰器再包装,实现了seasons传递装饰器参数。

def seasons(season_type):
    def clothes(func):
        def wear(*args, **kwargs):
            if season_type == 1:
                s = 'spring'
            elif season_type == 2:
                s = 'summer'
            elif season_type == 3:
                s = 'autumn'
            elif season_type == 4:
                s = 'winter'
            else:
                print('The args is error!')
                return func(*args, **kwargs)
            print('The season is {}!{}'.format(s, func.__name__))
            return func(*args, **kwargs)

        return wear

    return clothes


@seasons(2)
def children():
    print('i am children')

示例:

def deco(*args, **kwargs):
    def _deco(func):
        print(args, kwargs)
        def __deco(*args, **kwargs):
            print('call deco')
            func(*args, **kwargs)
        return __deco
    return _deco


@deco('hello', x='ni hao')
def test():
    print('call test')


# 等同于
# def test():
#     print('call test')
# test = deco('hello', x='ni hao')(test)

二:函数 装饰 类

示例:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值