python给类的所有方法加上装饰器

mac2024-01-26  34

概述

今天遇到一个需求,需要对类的所有方法进行监听其返回值,本来想的是用装饰器,但是方法太多了一个一个写装饰器会爆炸。 所以这里发现一个有用的东西 参考:https://www.cnblogs.com/nkwy2012/p/6264031.html

__getattribute__

所有类进行方法或者属性获取的时候,实际上都是如下:

class A(): def name(): return "a" x=A() x.name()#其实是x.\_\_getattribute\_\_(‘name’)()

这里实际上就有一个很好的方法对所有的属性都进行监听了。

序号目的所编写代码Python 实际调用①获取一个计算属性(无条件的)x.my_propertyx.__getattribute__(‘my_property’)②获取一个计算属性(后备)x.my_propertyx.__getattr__(‘my_property’)③设置某属性x.my_property = valuex.__setattr__(‘my_property’,value)④删除某属性del x.my_propertyx.__delattr__(‘my_property’)⑤列出所有属性和方法dir(x)x.__dir__()

这里需要注意的是,如果该函数没有找到,就会进入__get__attr__里面去找,如果找到了或者没找到都会直接返回。

如果某个类定义了__getattribute__() 方法,在 每次引用属性或方法名称时 Python 都调用它(特殊方法名称除外,因为那样将会导致讨厌的无限循环)。如果某个类定义了 __getattr__() 方法,Python 将只在正常的位置查询属性时才会调用它。如果实例 x 定义了属性color, x.color 将 不会 调用x.__getattr__(‘color’);而只会返回x.color 已定义好的值。无论何时给属性赋值,都会调用 __setattr__() 方法。无论何时删除一个属性,都将调用 __delattr__() 方法。如果定义了 __getattr__() 或 __getattribute__() 方法, __dir__() 方法将非常有用。通常,调用 dir(x) 将只显示正常的属性和方法。如果__getattr()__方法动态处理color 属性, dir(x) 将不会将 color 列为可用属性。可通过覆盖 __dir__() 方法允许将 color 列为可用属性,对于想使用你的类但却不想深入其内部的人来说,该方法非常有益。

一般情况下使用__getattribute__()即可 就目前自己测试来看,如果__getattribute__()找不到,那么getattr基本也找不到。 基本的调用流程:先调用__getattribute__(),如果找不到会调用父类的相关方法,如果都找不到,父类会调用__getattr__进行找,找到了就找到了返回了值,如果没找到那就直接报错。示例如下:

import time from typing import Iterable class A(): def name(self): print("a") class B(A): def __getattr__(self, item): # print(self.__dir__()) print("getattr:", item) return "None111111111111111111" # x=10 @property def name1(self): return 213 def name(self): return "sfag" def __getattribute__(self, item): print("use getattribute") x = super().__getattribute__(item)#重点:如果父类调用找不到会直接在父类那里面调用__getattr__,如果还没找到就会直接返回了,这一行下面的代码不会执行。这里有点扯,我觉得是python的bug。。。 print(isinstance(x,classmethod)) print(type(x)) print(str(type(x))=="<class 'method'>")#判断是否为类的方法属性或函数,具体分析请看下面 print(item + ":" + str(x)) return x def __dir__(self) -> Iterable[str]: print("dir")#本来以为上面两个方法会调用,但是实际上没有调用 return super().__dir__() b = B() print(0) print(hasattr(b,'nnnn')) print("1") print(b.x) print(2) print(b.name())

有部分很重要的代码:

def __getattribute__(self, item): print("use getattribute") x = super().__getattribute__(item)#重点:如果父类调用找不到会直接在父类那里面调用__getattr__,如果还没找到就会直接返回了,这一行下面的代码不会执行。这里有点扯,我觉得是python的bug。。。 print(isinstance(x,classmethod)) print(type(x)) print(str(type(x))=="<class 'method'>")#判断是否为类的方法属性或函数,如果为类或实例的方法则为method,如果为类或实例的变量则为对应的变量属性,如果为静态方法则为function。抽象类那一块没测试过,需要使用的自己使用 print(item + ":" + str(x)) return x

所以如果要对类的所有方法加上装饰器,只需要重写该类的__getattribute__方法即可。这里我建议重写的时候还是对输入参数item进行一下判断,以免误伤把一些不想做装饰的都进行了装饰。 这里我给一个简单的例子,

class ChiseAipFace(AipFace):#AipFace为百度的人脸识别http封装的类。 def __getattribute__(self, item): """建议对item进行一下判断,不要全局增加""" ret = super().__getattribute__(item) if type( ret) == "<class 'method'>": # 类里面的成员分为三种,method(类方法和实例方法),function(实例方法),int,str...(变量成员),具体需要的时候还是通过type进行判断或者直接通过item来判断 def res(*args, **kwargs): retu = None t = 0 for i in range(10): if i > 0: t = time.time() retu = ret(*args, **kwargs) if retu['error_code'] == 18:#主要解决瞬时并发超限的问题,通过随机值将超限的并发随机在之后的一段时间里面进行接口访问。 time.sleep(random.random() * i * 5) else: if t: logger.warning("接口访问延时18:" + str(time.time() - t) + ",name:" + item) return retu logger.error("接口失败18:" + item) return retu return res else: return ret

一些其他有意义的东西

本部分来自链接:https://www.cnblogs.com/nkwy2012/p/6264031.html 真正神奇的东西

如果知道自己在干什么,你几乎可以完全控制类是如何比较的、属性如何定义,以及类的子类是何种类型。

序号目的所编写代码Python 实际调用类构造器x = MyClass()x.__new__()*类析构器del xx.__del__()只定义特定集合的某些属性x.__slots__()自定义散列值hash(x)x.__hash__()获取某个属性的值x.colortype(x).__dict__[‘color’].__get__(x, type(x))设置某个属性的值x.color = ‘PapayaWhip’type(x).__dict__[‘color’].__set__(x, ‘PapayaWhip’)删除某个属性del x.colortype(x).__dict__[‘color’].__del__(x)控制某个对象是否是该对象的实例 your class isinstance(x, MyClass)MyClass.__instancecheck__(x)控制某个类是否是该类的子类issubclass(C, MyClass)MyClass.__subclasscheck__©控制某个类是否是该抽象基类的子类issubclass(C, MyABC)MyABC.__subclasshook__©

python中以双下划线的是一些系统定义得名称,让python以更优雅得语法实行一些操作,本质上还是一些函数和变量,与其他函数和变量无二。 比如x.__add__(y) 等价于 x+y 有一些很常见,有一些可能比较偏,在这里罗列一下,做个笔记,备忘。

x.__contains__(y) 等价于 y in x, 在list,str, dict,set等容器中有这个函数 __base__, __bases__, __mro__, 关于类继承和函数查找路径的。 class.__subclasses__(), 返回子类列表 x.__call__(…) == x(…) x.__cmp__(y) == cmp(x,y) 2. x.__getattribute__(‘name’) == x.name == getattr(x, ‘name’), 比__getattr__更早调用 x.__hash__() == hash(x) x.__sizeof__(), x在内存中的字节数, x为class得话, 就应该是x.__basicsize__ x.__delattr__(‘name’) == del x.name

__dictoffset__ attribute tells you the offset to where you find the pointer to the __dict__ object in any instance object that has one. It is in bytes.

__flags__, 返回一串数字,用来判断该类型能否被序列化(if it’s a heap type), __flags__ & 512 S.__format__, 有些类有用 x.__getitem__(y) == x[y], 相应还有__setitem__, 某些不可修改类型如set,str没有__setitem__ x.__getslice__(i, j) == x[i:j], 有个疑问,x=‘123456789’, x[::2],是咋实现得

__subclasscheck__(), check if a class is subclass

__instancecheck__(), check if an object is an instance

__itemsize__, These fields allow calculating the size in bytes of instances of the type. 0是可变长度, 非0则是固定长度 x.__mod__(y) == x%y,

x.__rmod__(y) == y%x x.__module__ , x所属模块 x.__mul__(y) == x*y,

x.__rmul__(y) == y*x

__reduce__, __reduce_ex__ , for pickle

__slots__ 使用之后类变成静态一样,没有了__dict__, 实例也不可新添加属性

__getattr__ 在一般的查找属性查找不到之后会调用此函数

__setattr__ 取代一般的赋值操作,如果有此函数会调用此函数, 如想调用正常赋值途径用 object.__setattr__(self, name, value)

__delattr__ 同__setattr__, 在del obj.name有意义时会调用

最新回复(0)