Python中装饰器的用法总结:

Python中装饰器的用法总结:

'''
Author: Hollis23
Date: 2021-10-19 15:01:29
LastEditTime: 2021-10-20 09:27:56
LastEditors: Hollis23
Description: v0.0
FilePath: /mmdetection/tests/test_decorator/1.py

'''

import functools
import time
from typing import AsyncGenerator

# ------------------------------------------------------
#                    1.装饰器语法糖
# ------------------------------------------------------
'''
    @符号就是装饰器语法糖
    它放在一个函数开始定义的地方,就像一顶帽子一样戴在这个函数的头上,和这个
    函数绑定在一起。在我们调用这个函数的时候,第一件事并不是执行这个函数,而是
    将这个函数作为参数传入他头顶这顶帽子,这顶帽子我们称为装饰函数或装饰器
'''


# -------------------------------------------------------
#                    2.装饰器入门用法
# -------------------------------------------------------
def logger(func):
    def wrapper(*args, **kw):
        print('Prepare to calculate: {} func!'.format(func.__name__))

        # truly process
        func(*args, **kw)

        print('claculate Done!')
    return wrapper


@logger
def add(x, y):
    print('{} + {} = {}'.format(x, y, x+y))


# 时间计时器
def timer(func):
    def wrapper(*args, **kw):
        t1 = time.time()

        func(*args, **kw)

        t2 = time.time()
        # 计算时长
        cost_time = t2 - t1
        print('take {} seconds'.format(t2 - t1))
    return wrapper


@timer
def want_sleep(sleep_time):
    time.sleep(sleep_time)


# if __name__ == '__main__':
    # add(200, 50)
    # want_sleep(10)

# -------------------------------------------------------
#                    3.带参数的装饰器函数
# -------------------------------------------------------


def say_hello(country):  # 传装饰器带的参数
    def wrapper(func):  # 传函数
        def deco(*args, **kw):  # 传函数带的参数
            if country == 'china':
                print('你好!')
            elif country == 'american':
                print('Hello!')
            else:
                return

            # truely process func
            func(*args, **kw)
        return deco
    return wrapper


@say_hello('american')
def american():
    print('I coming from America!')


@say_hello('china')
def china():
    print('I coming from China!')


# if __name__ == '__main__':
#     china()
#     american()

# -------------------------------------------------------
#                    4.不带参数的类装饰器
# -------------------------------------------------------

# 基于类的装饰器的实现,必须实现__call__和__init__两个内置函数,其中
# __init__: 接收被装饰函数
# __call__: 实现装饰逻辑

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

    def __call__(self, *args, **kwargs):
        print('[INFO]:the function {func}() is running...'
              .format(func=self.func.__name__))
        return self.func(*args, **kwargs)


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


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


# -------------------------------------------------------
#                    5.带参数的类装饰器
# -------------------------------------------------------
'''
    上面不带参数的例子只能打印INFO级别的日志,正常情况下,我们还要打印
    DEBUG,WARNING等级别的日志。这就需要给类装饰器传入参数,给这个函数
    指定级别了。

    带参数和不带参数的装饰器有很大的不同:
    __init__: 不再接收被装饰函数,而是接收传入参数
    __call__: 接收被装饰函数,实现装饰逻辑
'''


class logger(object):
    def __init__(self, level='INFO'):
        self.level = level

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print('[{level}]: the function {func}() is running...'
                  .format(level=self.level, func=func.__name__))

            func(*args, **kwargs)
        return wrapper


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


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


# -------------------------------------------------------
#                    6.使用偏函数与类实现装饰器
# -------------------------------------------------------
'''
    绝大多数装饰器都是基于函数和闭包实现,但这并不是制造装饰器的唯一方法
    Python对某个对象是否能通过装饰器形式使用(当做装饰器使用)只有一个要求:decorator必须
    是一个‘可被调用(callable)的对象’。
    这个callable对象,我们最熟悉的就是函数,类也可以是callable对象,只要实现了__call__函数
    ,还有比较少人使用的偏函数也是callable对象。
'''

# 实现了__call__的类


class DelayFunc:
    def __init__(self, duration, func):
        self.duration = duration
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f'Waig for {self.duration} seconds...')
        time.sleep(self.duration)
        return self.func(*args, **kwargs)

    def eager_call(self, *args, **kwargs):
        print('Call without delay')
        return self.func(*args, **kwargs)

# delay是一个偏函数,这里delay就可以作为一个装饰器


def delay(duration):
    return functools.partial(DelayFunc, duration)


@delay(duration=2)
def add(a, b):
    return a + b


# if __name__ == '__main__':
#     add(3, 5)

# -------------------------------------------------------
#                    7.如何写能装饰类的装饰器?
# -------------------------------------------------------
# 用python写单例模式的时候,常用的有三种写法。其中一种是用装饰器来实现的
# 如,装饰器版的单例模式
instances = {}


def singleton(cls):
    def get_instance(*args, **kwargs):
        cls_name = cls.__name__
        print('============1============')
        if not cls_name in instances:
            print('++++++++2+++++++++++')
            instance = cls(*args, **kwargs)
            instances[cls_name] = instance
        return instances[cls_name]
    return get_instance


@singleton
class User:
    _instance = None

    def __init__(self, name):
        print('===========3==========')
        self.name = name

# 装饰器用在类上,并不是很常见,但只要熟悉装饰器的实现过程,就不难实现对类的装饰。这里
# 的装饰器就只是实现对类实例的生成的控制而已


# if __name__ == '__main__':
#     u1 = User('hollis')
#     u1.age = 20
#     print(u1)
#     print(instances)
#     u2 = User('hollis2')
#     print(u2.age)


# -------------------------------------------------------
#                    8.warp装饰器有啥用?
# -------------------------------------------------------
# 在functools中提供一个warps装饰器,有啥用?
# 它的作用就是将被修饰的函数(wrapped)的一些属性值赋值给修饰器函数(wrapper),最终让属性的显示
# 更符合我们的直觉

# -------------------------------------------------------
#                    9.内置装饰器property
# -------------------------------------------------------
# property通常存在于类中,可以将一个函数定义成一个属性,属性的值就是该函数return的内容
# (1)常规创建类并赋值的方法:有风险,并不能对属性的值做合法性限制
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

# (2)私有属性写法


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()

# (3)使用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('年龄超出正常范围')
        self._age = value

    @age.deleter
    def age(self):
        del self._age


# if __name__ == '__main__':
#     XiaoMing = Student('小明')
#     XiaoMing.age = 25
#     print(XiaoMing.age)
#     del XiaoMing.age
#     print(XiaoMing.name)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值