由浅入深理解装饰器

装饰器是一种函数的函数,因为装饰器传入的参数就是一个函数,然后通过实现各种功能来对这个函数的功能进行增强。

装饰器的本质:函数的嵌套调用。

  • 以下由浅入深演示各种装饰器的调用过程:

无参函数 的装饰器

# -*- encoding: utf-8 -*-
import time

def wrapper(target_func):
    def inner():
        start = time.time()
        target_func()
        end = time.time()
        run_time = end - start
        return "RUN TIME FOR FUNCTION 【%s】 IS %s S." % (target_func.__name__, run_time)
    return inner


# 语法糖调用方式1,等价于普通调用方式2
@wrapper
def demo1():
    time.sleep(2)
print(demo1())

# 普通调用方式2
def demo2():
    time.sleep(2)
w = wrapper(demo2)
print(w())

含参函数 的装饰器

装饰器内部 需要获取到 被装饰(原)函数 的参数。

def wrapper1(target_func):
    def inner(*args, **kwargs): # 目标函数的参数传递到这里args 和 kwargs
        target_func()
        num1 = args[0]
        num2 = kwargs["num2"]
        result = num1 + num2
        return "THE RESULT FOR FUNCTION 【%s】 IS %s." % (target_func.__name__, result)
    return inner

@wrapper1
def demo1(*args, **kwargs):
    print("JUST DO IT")

print(demo1(10, num2=20))

有返回值的含参函数 的装饰器

装饰器 需要返回 被装饰(原)函数 的返回值。

def wrapper2(target_func):
    def inner(*args, **kwargs): # 目标函数的参数传递到这里args 和 kwargs
        return_user = target_func(*args, **kwargs) 
        # 目标函数的返回值用变量`return_user` 接收
        if return_user['is_super'] == True: # 判断用户是否为超级用户
            return "THE USER IS SUPER USER !"
        return "NORMAL USER."
    return inner

@wrapper2
def demo2(id, username, is_super):
    user = {
        "id": id,
        "username": username,
        "is_super": is_super
    }
    return user

print(demo2(1214, 'xaunRui', True))
print(demo2(52221, 'Jack', False))

无参函数 的含参装饰器

装饰器本身 需要参数。

def wrapper3(*args, **kwargs):  # 此处也可以直接定义参数`log_level`
    log_level = args[0]
    def wrapper_inner(target_func):
        def inner(*args, **kwargs): # 目标函数的参数传递到这里args 和 kwargs
            return_value = target_func(*args, **kwargs) 
            # 目标函数的返回值用变量 `return_value` 接收
            if return_value == "some error" and log_level == "ERROR":
                return "ERROR!
            return "IT'S OK OR DON'T CARE."
        return inner
    return wrapper_inner

# @wrapper3("ERROR")
@wrapper3("WARRING")
def demo3():
    return "some error"

print(demo3())

类装饰器

基于类装饰器的实现,必须实现 __call____init__两个内置函数。

__init__ :接收被装饰函数, __call__ :实现装饰逻辑。

不含参的类装饰器

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

    def __call__(self, *args, **kwargs):
        print("[INFO]:log for {func}().".format(func=self.func.__name__))
        return self.func(*args, **kwargs)

@logger
def say(something):
    print("say {}!".format(something))

say("hello")

含参的类装饰器

class logger(object):
    def __init__(self, level='INFO'):  # 默认为`INFO`
        self.level = level

    def __call__(self, func): # 接受函数
        def wrapper(*args, **kwargs):
            print("[{level}]: log for {func}()."\
                .format(level=self.level, func=func.__name__))
            func(*args, **kwargs)
        return wrapper  #返回函数

@logger(level='WARNING')
def say(something):
    print("say {}!".format(something))

say("hello")

functools.wraps装饰器

  • 示例:

    def wrapper(func):
        def inner_function():
            pass
        return inner_function
    
    # 方式1:
    @wrapper
    def wrapped():
        pass
    print(wrapped.__name__)
    
    # 方式2:
    def wrapped():
        pass
    print(wrapper(wrapped).__name__)
    # inner_function
    # 返回的并不是func,原因如下:
    # 方式1执行func和方式2decorator(func)是等价的,所以上面 func.__name__ 等价于#decorator(func).__name__ 的,所以函数名是 inner_function
    
  • 为了避免这种情况,使用 functools .wraps 装饰器,它的作用就是将被修饰的函数(wrapped) 的一些属性值赋值给修饰器函数(wrapper) ,最终让属性的显示更符合直觉,如下:

    from functools import wraps
    
    def wrapper(func):
        @wraps(func)  # 此处!
        def inner_function():
            pass
        return inner_function
    
    @wrapper
    def wrapped():
        pass
    
    print(wrapped.__name__)
    # wrapped
    
  • 实际使用偏函数,源码如下:

    def wraps(wrapped,
              assigned = WRAPPER_ASSIGNMENTS,
              updated = WRAPPER_UPDATES):
        return partial(update_wrapper, wrapped=wrapped,
                       assigned=assigned, updated=updated)
    

property装饰器

  • 示例,学生类实例,通常的属性操作方法:

    class Student(object):
        def __init__(self, name, age=None):
            self.name = name
            self.age = age
    
    # 实例化
    xiaoming = Student("小明")
    
    # 添加属性
    xiaoming.age=25
    
    # 查询属性
    xiaoming.age
    
    # 删除属性
    del xiaoming.age
    
  • 如上操作对象属性,会直接暴露属性,并且不能对属性的值做合法性限制,手动实现setter:

    class Student(object):
        def __init__(self, name):
            self.name = name
            self.name = None
    
        def set_age(self, age):
            if not isinstance(age, int):
                raise ValueError('输入不合法:年龄必须为数值!')
            if not 0 < age < 100:
                raise ValueError('输入不合法:年龄范围必须0-100')
            self._age=age
    
        def get_age(self):
            return self._age
    
        def del_age(self):
            self._age = None
    
    
    xiaoming = Student("小明")
    
    # 添加属性
    xiaoming.set_age(25)
    
    # 查询属性
    xiaoming.get_age()
    
    # 删除属性
    xiaoming.del_age()
    
  • 如上的方法,满足了要求,但不太符合对象.属性名的风格,使用property:

    class Student(object):
        def __init__(self, name):
            self.name = name
            self.name = None
    
        @property
        def age(self):
            return self._age
    
        @age.setter
        def age(self, value):
            if not isinstance(value, int):
                raise ValueError('输入不合法:年龄必须为数值!')
            if not 0 < value < 100:
                raise ValueError('输入不合法:年龄范围必须0-100')
            self._age=value
    
        @age.deleter
        def age(self):
            del self._age
    
    xiaoming = Student("小明")
    
    # 设置属性
    xiaoming.age = 25
    
    # 查询属性
    xiaoming.age
    
    # 删除属性
    del xiaoming.age
    

    @property装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函数return的内容。**同时,会将这个函数变成另外一个装饰器。**如:@age.setter@age.deleter

    @age.setter使XiaoMing.age = 25的方式可以直接赋值。

    @age.deleter 使del XiaoMing.age的方式可以删除属性。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值