python--装饰器

装饰器的作用:

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。

使用闭包来调用一个函数:

def funcA(func):
    def funcB():
        print("Before call function")
        func()
        print("After call function")
    return funcB

def funcD():
    print("This is funcD")

if __name__ == '__main__':
    t=funcA(funcD)
    t()

# Before call function
# This is funcD
# After call function

使用装饰器代替:

def funcA(func):
    def funcB():
        print("Before call function")
        func()
        print("After call function")
    return funcB
@funcA
def funcD():
    print("This is funcD")

if __name__ == '__main__':
   funcD()

# Before call function
# This is funcD
# After call function

使用装饰器后再次调用funcA,会发现:

def funcA(func):
    def funcB():
        print("Before call function")
        func()
        print("After call function")
    return funcB
@funcA
def funcD():
    print("This is funcD")

if __name__ == '__main__':
    t=funcA(funcD)
    t()
# 
# Before call function
# Before call function
# This is funcD
# After call function
# After call function

就相当于加了两层修饰器:

def funcA(func):
    def funcB():
        print("Before call function")
        func()
        print("After call function")
    return funcB
@funcA
@funcA
def funcD():
    print("This is funcD")

if __name__ == '__main__':
   funcD()
#
# Before call function
# Before call function
# This is funcD
# After call function
# After call function

使用装饰器后,打印一下函数名称,发现在函数体外打印并不是funcD,这是因为funcB重写了我们函数的名字和注释文档:

def funcA(func):
    def funcB():
        print("Before call function")
        print("inside,func_name:",func.__name__)
        func()
        print("After call function")
    return funcB

@funcA
def funcD():
    print("This is funcD")

if __name__ == '__main__':
    funcD()
    print("outside,func_name:",funcD.__name__)
    

# Before call function
# inside,func_name: funcD
# This is funcD
# After call function
# outside,func_name: funcB

解决方法:使用functools.wraps

from functools import wraps
def funcA(func):
    @wraps(func)
    def funcB():
        print("Before call function")
        print("inside,func_name:",func.__name__)
        func()
        print("After call function")
    return funcB

@funcA
def funcD():
    print("This is funcD")

if __name__ == '__main__':
    funcD()
    print("outside,func_name:",funcD.__name__)


# Before call function
# inside,func_name: funcD
# This is funcD
# After call function
# outside,func_name: funcD

函数中有参数:


from functools import wraps

def funcA(func):
    @wraps(func)
    def funcB(s):
        print(func.__name__," was called")
        return func(s)
    return funcB

@funcA
def funcD(s1):
    print(s1)
if __name__ == '__main__':
    funcD("this is func")

# funcD  was called
# this is func

但是如果另一个函数有多个参数怎么办,幸好python提供了不定参数个数的解决办法:

from functools import wraps

def funcA(func):
        @wraps(func)
        def funcB(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            return func(*args, **kwargs)

        return funcB
@funcA
def funcD(s1,s2):
    print(s1,s2)


@funcA
def funcE(s1):
    print(s1)

if __name__ == '__main__':
    funcD('hello','world')
    funcE("hello,python")



# funcD was called
# hello world
# funcE was called
# hello,python

带参数的装饰器

from functools import wraps
def funcA(level):
    def wrapper(func):
        def inner_wrapper(*args,**kwargs):
            print("level:{},func_name:{}".format(level,func.__name__))
            return func(*args,**kwargs)
        return inner_wrapper
    return wrapper

@funcA(level='debug')
def funcB(s1):
    print(s1)

@funcA(level='error')
def funcD(s1,s2):
    print(s1,s2)

if __name__ == '__main__':
    funcB("this is funcB")
    funcD('hello','world')
#     
# level:debug,func_name:funcB
# this is funcB
# level:error,func_name:funcD
# hello world

基于类实现的装饰器

装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()方法,那么这个对象就是callable的。

class logging(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print ("[DEBUG]: enter function {func}()".format(
            func=self.func.__name__))
        return self.func(*args, **kwargs)
@logging
def say(something):
    print ("say {}!".format(something))

if __name__ == '__main__':
    say('hello')
    

# [DEBUG]: enter function say()
# say hello!

使用场景

它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。有了装饰器,可以抽离出大量与函数功能本身无关的雷同代码并继续重用

日志:

from functools import wraps

def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile,并写入内容
            with open(logfile, 'a') as opened_file:
                # 现在将日志打到指定的logfile
                opened_file.write(log_string + '\n')
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator

@logit(logfile='func.log')
def func1():
    print("this is func1")

@logit(logfile='func.log')
def func2():
    print("this is func2")


if __name__ == '__main__':
   func1()
   func2()
 
# func1 was called
# this is func1
# func2 was called
# this is func2

捕获异常:

from functools import wraps
import sys


def decration_func(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except Exception as err:
            print(err)
            sys.exit(1)

    return wrapper


@decration_func
def testuse():
    a, b = 1, 0
    print(a / b)


if __name__ == '__main__':
    testuse()

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值