概述
今天遇到一个需求,需要对类的所有方法进行监听其返回值,本来想的是用装饰器,但是方法太多了一个一个写装饰器会爆炸。
所以这里发现一个有用的东西
参考: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_property | x.__getattribute__(‘my_property’) |
② | 获取一个计算属性(后备) | x.my_property | x.__getattr__(‘my_property’) |
③ | 设置某属性 | x.my_property = value | x.__setattr__(‘my_property’,value) |
④ | 删除某属性 | del x.my_property | x.__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 x | x.__del__() |
只定义特定集合的某些属性 | x.__slots__() | ||
自定义散列值 | hash(x) | x.__hash__() | |
获取某个属性的值 | x.color | type(x).__dict__[‘color’].__get__(x, type(x)) | |
设置某个属性的值 | x.color = ‘PapayaWhip’ | type(x).__dict__[‘color’].__set__(x, ‘PapayaWhip’) | |
删除某个属性 | del x.color | type(x).__dict__[‘color’].__del__(x) | |
控制某个对象是否是该对象的实例 your class i | sinstance(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有意义时会调用