2023年8月15日星期二

py修饰器

py修饰器

装饰器

参考视频

关于对象

函数也可以作为一个对象object,只不过是callable可以调用的,如
func(arg1)
在py语法中任何对象都能调用,只不过会warning,如
1(arg1)

def double(arg):
    return arg * 2

def triple(arg):
    return arg * 3

def calc_number(func,arg:int):
    return func(arg)

print(calc_number(double,1))
print(calc_number(triple,1))

2
3
def get_multi_func(n):
    def wrapper(arg):
        return n * arg
    return wrapper

double = get_multi_func(2)
print(double(3))
6

函数装饰器

decorator 本身是一个callable,
当对一个变量名做decorator操作时,等价于
这个变量名=decorator调用变量名。
本质上输入一定是函数,输出可能是函数或其他

'''
输入输出都是函数
'''
def dec(func):
    print('with decorator')
    return func

@dec
def double(arg):
    return 2 * arg

print(double(3))


# 等价于
def double(arg):
    return 2 * arg

double = dec(double)

print(double(3))
with decorator
6
with decorator
6

补充 星号表达式*

转载自
Python星号表达式(*)用法详解

星号在python中的用法主要可分为三类:一是作为函数的可变参数标志以及在函数参数的语境下对可迭代对象进行解包并进行参数传递(参数解包),二是作为赋值语句中的可变变量标志,三是在非函数参数的其他特定的语境中直接对可迭代对象进行解包操作。这三种用法是在不同的python版本中不断的添加进去的,其中后两种用法只在3.x版本中可以使用,具体的讲,用法一是在2.x和3.x都可以使用的,第二种用法是在3.0版本添加进去的,第三种用法是在3.5版本中添加进去的,所以在使用星号的不同用法时,还需要注意python的版本,以免出错。下面对每种用法进行详细的说明。

  1. 作为函数的可变参数标志以及参数解包
  2. 赋值语句中作为可变变量标志
  3. 在除函数参数语境外的其他语境下对可迭代对象进行解包

详见原文
————————————————
版权声明:本文为CSDN博主「S_o_l_o_n」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/S_o_l_o_n/article/details/102823490

import time

def timeit(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        ret = func(*args,**kwargs)
        print("consume time ",time.time() - start,"sec")
        return ret
    
    return wrapper
    
@timeit
def double(arg):
    return arg * 2

print(double(2))
consume time  0.0 sec
4

带参数的decorator,如

@timeit(10)
def double(x):
    return 2 * x

等价于

double = timeit(10)(double) 

先计算timeit(10)并返回一个函数,之后这个函数再调用double

import time
# 例如执行代码多次的时间
def timeit(iteration:int):
    def inner(func):
        def wrapper(*args,**kwargs):
            start = time.time()
            for _ in range(iteration):
                ret = func(*args,**kwargs)
            print('consume',time.time()-start)
            return ret
        
        return wrapper

    return inner

@timeit(10000)
def double(x):
    return 2 * x

print(double(3))

# 等价于
inner = timeit(10000)
double = inner(double)
consume 0.0010006427764892578
6

类装饰器

参考读物:
python 类的使用(3)之类中常用的三个装饰器@classmethod、@staticmethod、@property
python 类的使用(5)之类装饰器(类的装饰器和类作为装饰器)
Python中下划线的5种含义
下划线的5种用法

几个常见的类中常用的装饰器

类的装饰器

类作为装饰器

在需要作为修饰器的类中定义一个魔术方法__call__,这样在作为decorator时就会调用class_.__call__(func)

from typing import Any


class Worker:
    _num_of_worker = 0

    def __init__(self,func) -> None:
        print("decorate")
        Worker._num_of_worker += 1
        self.number = Worker._num_of_worker
        self.func = func
        pass

    def __call__(self, *args: Any, **kwds: Any) -> Any:
        print("伞兵",self.number,"号",end=' ')
        ret = self.func(*args,**kwds)
        return ret
    
@Worker
def CoalWorker(name):
    print(name,"开始挖煤")

@Worker
def IronWorker(name):
    print(name,"开始挖铁")

if __name__ == "__main__":
    print("__main__")
    CoalWorker("qjr")
    print(type(CoalWorker))
    print(id(CoalWorker))

    IronWorker("fjm")
    print(type(IronWorker))
    print(id(IronWorker))

    print("现在有",Worker._num_of_worker,"个工人")

    CoalWorker.func("wmr")
    print(CoalWorker.number)
decorate
decorate
__main__
伞兵 1 号 qjr 开始挖煤
<class '__main__.Worker'>
2764647110352
伞兵 2 号 fjm 开始挖铁
<class '__main__.Worker'>
2764640984720
现在有 2 个工人
wmr 开始挖煤
1

观测现象,在执行main代码之前,首先对用Worker类修饰的两个函数CoalWorkerIronWorker进行decorate,对于两个函数,分别创建了新的Worker类,因此Worker._num_of_worker从0增长为2.
其次发现,CoalWorkerIronWorker变量名的type不再是函数而是Worker类的实例化。
使用CoalWorker.成员可以直接调用成员变量和方法

类的修饰器

为类加装饰器的效果可能是修改类属性,修改类方法等。

# 给被修饰的类增加一个属性
def dec(class_):                # 传入一个类作为参数
    print("decorate!")
    class_._num_of_worker = 5   # 设置一个类的属性
    return class_               # 返回这个类

@dec
class worker:
    pass

print("main")
print(worker._num_of_worker)
decorate!
main
5
# 用带参数的装饰器,给被修饰的类增加或修改一个函数
"""
转载自 https://blog.csdn.net/weixin_42124234/article/details/90451158
"""
def dec(class_):
    class wrapper():
        def __init__(self) -> None:
            print("init")
            print(type(class_))
            print(type(class_()))
            self.wrapper = class_()         # 注意括号,保留一个original_class类对象
            pass

        def printf(self):
            print("原类执行前的打印语句")
            self.wrapper.printf()           # 原始类original_class的一个对象,调用original_class类的实例方法printf()
            print("原类执行后的打印语句")
            pass
    
    return wrapper

@dec
class origin_class:
    def __init__(self) -> None:
        self.text = "origin text"
    def printf(self):
        print(self.text)

if __name__ == '__main__':
    ori = origin_class()
    ori.printf()
    print(type(ori))
        

init
<class 'type'>
<class '__main__.origin_class'>
原类执行前的打印语句
origin text
原类执行后的打印语句
<class '__main__.dec.<locals>.wrapper'>
# 重写类
def dec(class_):
    class wrapper:
        id_ = 1
        pass
    return wrapper

@dec
class worker:
    id_ = 2
    pass

worker.id_
1
# 类修饰类(感觉不如继承)
'''
转载自 https://blog.csdn.net/keepaware/article/details/112909406
'''
class animal:
    def __init__(self, func):
        self.func = func
  
    def __call__(self, *args, **kwargs):
        print('working here')
        res = self.func(*args, **kwargs)
        
        return res

@animal        
class dogs:
    def test2(*args):
        print('look here')

wonwon = dogs()   # 首先实例化
# 结果:working here
wonwon.test2()    # 再执行dogs类中的test2函数
# 结果:look here
# 由于得到了dogs中类的test2函数的运行结果,表明wonwon确实是dogs类的一个实例,
# 表明之前的装饰是成功的。不过这里的装饰只是在创建类实例时调用了一次,其实作用不是很大。
working here
look here

0 评论:

发表评论