Python装饰器

把学习的东西都记一下

装饰器是python的重要部分,可以在不修改原函数代码的情况下,扩展原函数的功能,实现代码复用,切面编程。

python装饰器的实现基础

  1. 一切皆对象
    #其实是生成了一个函数对象,func1是指向这个对象的引用
    def func1():
        print('this is func1')
    
    #又有一个引用func2指向这个函数对象
    func2 = func1
    
    print(id(func2)) #-->48715856
    print(id(func1)) #-->48715856
    
    del func1
    
    func1()  #Tip: Name 'func1' can be undefined
    func2() #-->this is func1
  2. 在函数中定义函数
    def fun_out():
        def fun_inner():
            print('this is fun_inner')
    
        fun_inner()
        print('this is fun_out')
    
    fun_out()
    #-->this is fun_inner
    #-->this is fun_out
    #fun_inner在fun_out外部是无法访问的
    fun_inner()    #Tip: Unresolved reference 'fun_inner'
    fun_out.fun_inner()    #Tip: Cannot find reference 'fun_inner' in 'function' 
    fun_out().fun_inner()    #Tip: Cannot find reference 'fun_inner' in 'None'
  3. 将函数作为返回值
    def fun_out():
        def fun_inner():
            print('this is fun_inner')
        print('this is fun_out')
        return fun_inner
    
    fun = fun_out()
    fun()
    
    #-->this is fun_out
    #-->this is fun_inner
  4. 将函数作为参数传递
    def fun_out(func_param):
        func_param()
        print('this is fun_out')
    
    def fun_log():
        print('this is fun_log')
    
    fun_out(fun_log)
    #-->
    this is fun_log
    this is fun_out
    

    正是因为python语言具有以上特性,才有了装饰器。

第一个Python装饰器

#这是我的原有函数,现在我要扩展它:
#在函数执行前后打印日志
def my_foo():
    print('do something in foo')

#log_decorator就是一个装饰器
#它返回一个函数对象!!!-这很重要
def log_decorator(func):
    def wrap_func():
        print('will exec', func.__name__)
        func()
        print('done exec', func.__name__)
    return wrap_func

#本质上来说,my_foo已经不再是原来的my_foo了,因为它指向了一个新的函数对象
#这个新的函数对象是log_decorator()返回的,它是wrap_func
my_foo = log_decorator(my_foo)

#但是我们并不管这些,因为同样是调用my_foo()
#它现在却可以打印:
# will exec my_foo
# do something in foo
# done exec my_foo
my_foo()
  • 内置装饰器wraps

 在上面的代码中,如果我们在my_foo()后面加上

print(my_foo.__name__)

就会发现打印出来的是wrap_func,这明显不行,我希望装饰器仅仅是扩展函数功能,而不要改变函数信息,python提供了内置装饰器wraps来解决这个问题:

def log_decorator(func):
    @wraps(func) #就这一行代码,放在wrap_func上面即可
    def wrap_func():
        print('will exec', func.__name__)
        func()
        print('done exec', func.__name__)
    return wrap_func
  • 语法糖@

在上面的代码中,我们需要先执行一下  my_foo = log_decorator(my_foo)  才能得到一个装饰后的my_foo,难道每次执行my_foo()之前都要这样搞一下,这明显不行,python提供了一个语法糖@来解决这个问题

@log_decorator
def my_foo():
    print('do something in foo')

这样,我们的第一个python装饰器就可以写成下面这种方式了:

def log_decorator(func):
    @wraps(func)
    def wrap_func():
        print('will exec', func.__name__)
        func()
        print('done exec', func.__name__)
    return wrap_func

@log_decorator
def my_foo():
    print('do something in foo')

my_foo()
print(my_foo.__name__)

完成!跟内置装饰器staticmethod、classmethod都是一样的用法。

带参数的装饰器

首先说明一点:无参和有参的区别是@装饰器后面带不带(),而不是()里面有没有参数

在上面的例子中,我们是直接把日志print出来了,如果我们想写入到指定的文件中呢?

可以这样:

def log_decorator(func, logfile=''):
    @wraps(func)
    def wrap_func():
        print('will exec', func.__name__)
        if logfile:
            with open(logfile, 'a') as f:
                f.write('will exec '+ func.__name__)
        func()
        print('done exec', func.__name__)
        if logfile:
            with open(logfile, 'a') as f:
                f.write('done exec '+ func.__name__)
    return wrap_func

def my_foo():
    print('do something in foo')

my_foo = log_decorator(my_foo, logfile='d.log')

my_foo()

这样确实是可以,但是这样我们就用不了语法糖@了,因为@只会给装饰器传递一个参数--原函数名,所以为了使用@,我们就需要再把装饰器做一层包装

#这里log_decorator其实并不是真正的装饰器
#它只是一层包装,它返回装饰器real_decorator
def log_decorator(logfile=''):
    #real_decorator才是真正的装饰器
    def real_decorator(func):
        @wraps(func)
        def wrap_func():
            print('will exec', func.__name__)
            if logfile:
                with open(logfile, 'a') as f:
                    f.write('will exec '+ func.__name__)
            func()
            print('done exec', func.__name__)
            if logfile:
                with open(logfile, 'a') as f:
                    f.write('done exec '+ func.__name__)
        return wrap_func

    return real_decorator

#log_decorator(logfile='a.log')返回的是real_decorator
#所以就相当于是 @real_decorator,real_decorator又定义在log_decorator的内部
#它自然可以使用log_decorator的参数,这样就实现了带参数的装饰器
@log_decorator(logfile='a.log')
def my_foo():
    print('do something in foo')

my_foo()

多个装饰器的执行顺序

代码:

def decorator_a(func):
    print('Get in decorator_a')
    def inner_a(*args, **kwargs):
        print('Get in inner_a')
        return func(*args, **kwargs)
    return inner_a

def decorator_b(func):
    print('Get in decorator_b')
    def inner_b(*args, **kwargs):
        print('Get in inner_b')
        return func(*args, **kwargs)
    return inner_b

@decorator_b
@decorator_a
def f(x):
    print('Get in f')
    return x * 2

f(1)
输出:
Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f

这里来分析一下:

@decorator_b
@decorator_a
def f(x):
    print('Get in f')
    return x * 2

f(1)

其实就是:

def f(x):
    print('Get in f')
    return x * 2

f = decorator_b(decorator_a(f))

f(1)

很明显:

decorator_a(f)最先执行,所以最先输出Get in decorator_a

然后执行decorator_b(...),输出Get in decorator_b

所以包装的时候是从里到外

执行的时候呢,f(1) ==> inner_b(inner_a(*args, **kwargs))

从日志可以看出,执行的时候是从外到里

装饰器类

在上面的例子中,装饰器都是一个函数,其实也可以把一个类作为装饰器,其实原理都是一样的,无非就是要区分对象的创建(__init__)和对象的调用(__call__),这里就只上代码了:

  • 无参装饰器类
class log_decorator(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('will exec', self.func.__name__)
        self.func(*args, **kwargs)
        print('done exec', self.func.__name__)

@log_decorator
def my_foo():
    print('do something in foo')

my_foo()
print(my_foo.__name__) #AttributeError: 'log_decorator' object has no attribute '__name__'

这种方式,my_foo其实就是log_decorator的一个实例,而且无法通过wraps还原信息。而且print(my_foo.__name__)是会抛出异常的,因为log_decorator并没有__name__属性。

  • 有参装饰器类
class log_decorator(object):
    def __init__(self, param1='', param2=''):
        self.param1 = param1
        self.param2 = param2
        
    def __call__(self, func):
        @wraps(func)
        def wrap_func(*args, **kwargs):
            print('will exec', func.__name__)
            func(*args, **kwargs)
            print('done exec', func.__name__)
        return wrap_func

@log_decorator()
def my_foo():
    print('do something in foo')

my_foo()
print(my_foo.__name__)
输出:
will exec my_foo
do something in foo
done exec my_foo
my_foo

my_foo还是它自己。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值