1、__new__
用于创建对象,但一般无需重新定义该方法。
2、__init__
初始化创建好的对象,初始化的前提是:“给实例对象赋值”。
如果不定义__init__方法,系统会提供一个默认的__init__方法。
如果定义了带参的__init__方法,系统不创建默认的__init__方法。 __init__只能返回None,一般不写return
3、__del__
# 析构方法,垃圾回收机制 class Person: def __del__(self): #重新定义__del__()方法 print('销毁对象:{0}'.format(self)) p1=Person() p2=Person() del p2 #主动调用del函数,销毁p2对象 print('程序结束') #程序执行完毕后,会再次调用del,销毁p1对象
4、__call__
可调用对象:凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象,判断对象是否为可调用对象可以用函数 callable。例函数,内置函数、类都属于可调用对象。
在类中重新定义 __call__ 方法,那么实例对象也将成为一个可调用对象,例:
class SalaryAcount: def __call__(self,salary): year = salary*12 return dict(month=salary,year=year) s=SalaryAcount() print(s(10000))
5、__str__
直接打印实例化对象时会调用该方法,该方法返回的必须是字符串
6、__enter__与__exit__
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
__exit__(self, exc_type, exc_val, exc_tb)
exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
7、__setitem__ 、__getitem__、__delitem__
class Tag: def __init__(self): self.change={'python':'This is python'} def __getitem__(self, item): print('这个方法被调用') return self.change[item] a=Tag() print(a['python'])
8、__getattr__与__getattribute__
重载__getattr__方法对类及其实例未定义的属性有效。如果访问的属性存在,就不会调用__getattr__方法。这个属性的存在,包括类属性和实例属性
class ClassA: x = 'a' def __init__(self): self.y = 'b' def __getattr__(self, item): return '__getattr__' a = ClassA() print(a.x) # 输出结果 a print(a.y) # 输出结果 b print(a.z) # 输出结果 __getattr__
通过类名直接调用类属性不会调用__getattribute__
通过实例化对象调用实例属性或者类属性都会首先调用__getattribute__,无论类存在与否
内置getattr和hasattr也会触发这个魔法方法__getattribute__
class ClassA: x = 'a' def __init__(self): self.y = 'b' def __getattribute__(self, item): return '__getattribute__' print(ClassA.x) a = ClassA() print(a.x) # 输出结果 __getattribute__ print(a.y) # 输出结果 __getattribute__ print(a.z) # 输出结果 __getattribute__
在__getattribute__代码块中,再次执行属性的获取操作时,会再次触发__getattribute__方法的调用,代码将会陷入无限递归
class ClassA: x = 'a' def __getattribute__(self, item): print('__getattribute__') return self.item # 再次出现属性的获取操作,会再次触发__getattribute__的调用,相当于return self.__getattribute__(item) a = ClassA() a.x
为了避免无限递归,应该把获取属性的方法 __getattribute__指向一个更高的超类,例如object。
class ClassA: x = 'a' # 类属性 def __getattribute__(self, item): print('__getattribute__') return super().__getattribute__(item) # 或者使用return object.__getattribute__(self, item) a = ClassA() print(a.x) # 输出__getattribute__
当同时定义__getattribute__和__getattr__时,__getattr__方法不会再被调用,除非显示调用__getattr__方法或引发AttributeError异常。
class A(): x = 1 def __getattribute__(self, item): print('正在调用属性{}'.format(item)) if item != 'x': raise AttributeError return super().__getattribute__(item) def __getattr__(self, item): print('正在调用属性{}'.format(item)) return 'aa' a = A() print(a.x)
9、__setattr__
试图给属性赋值时自动调用该方法
class Person(): def __init__(self,name,age): self.name = name self.age = age def __setattr__(self, key, value): self.__dict__[key] = value #此处不能写成self.key = value,会导致无线循环 p = Person('kuangfeng',20) p.gender = 'man' print(p.__dict__)
会执行三次print函数,是因为在__init__方法中,对象A初始化时给属性name和age赋值时,触发了__setattr__方法。 使用该方法是同样需要十分小心避免无限循环陷阱
如果定义__setattr__方法的同时定义了__getattribute__方法,那么在修改__dict__字典中的键值对时, 由于调用了self.__dict__属性,同样会触发__getattribute__方法,使用时应格外小心
class Person(): def __init__(self,name,age): self.name = name self.age = age def __setattr__(self, key, value): print('执行__setattr__') self.__dict__[key] = value def __getattribute__(self, item): print('执行__getattribute__') print('item:{}'.format(item)) #此处的item是__dict__ return super().__getattribute__(item) p = Person('kuangfeng',20) p.gender = 'man' print(p.__dict__)
10、__delattr__
定义当一个属性被删除时的行为
11、__get__、__set__、__del__描述器
一个类,如果只定义了 __get__() 方法,而没有定义 __set__(), __delete__() 方法,则认为是非数据描述符;反之,则成为数据描述符
__getattribute__(), 无条件调用
数据描述符:由第一条触发调用 (若人为的重载了该 __getattribute__() 方法,可能会调职无法调用描述符
实例对象的字典(若与描述符对象同名,会被覆盖哦)
类的字典
非数据描述符
父类的字典
__getattr__() 方法
对象属性的访问顺序:实例属性------------>类属性---------->父类属性------------->__getattr__
class Desc(): def __get__(self, instance, owner): print('Desc __get__') print('self:{}'.format(self)) print('instance:{}'.format(instance)) print('owner:{}'.format(owner)) print('=' * 50) def __set__(self, instance, value): print('Desc __set__') print('self:{}'.format(self)) print('instance:{}'.format(instance)) print('value:{}'.format(value)) print('=' * 50) class TestDesc(Desc): x = Desc() t = TestDesc() t.x 以下为输出内容: Desc __get__ self:<__main__.Desc object at 0x000001A8044C8430> #self为Desc的实例对象,其实就是TestDesc的类属性x instance:<__main__.TestDesc object at 0x000001A8044D6580> #instance为TestDesc的实例对象,其实就是t owner:<class '__main__.TestDesc'> #owner为TestDesc类 ==================================================
原因: t为TestDesc的实例,当访问t.x时,首先访问TestDesc的__getattrbute__()方法,发现没有之后去类TestDesc中访问,访问到了类属性x 其次,判断类属性x为一个描述符,此时,它就会做一些变动了,将TestDesc.x转化为TestDesc.__dict__['x'].__get__(t,TestDesc)来访问 然后进入类Desc中的__get__()方法,进行相应的操作
class Desc(object): def __init__(self, name): self.name = name def __get__(self, instance, owner): print("__get__...") print('name = ', self.name) print('=' * 40, "\n") class TestDesc(object): x = Desc('x') def __init__(self): self.y = Desc('y') # 把描述符(Desc类)的对象变成实例属性 t = TestDesc() t.x t.y #以下为输出结果: __get__... name = x ======================================== <__main__.Desc object at 0x03FB0088> #没有打印t.y的信息,但至少证明了y是Desc类的实例对象
原因: 访问t.y时,首先会调用TestDesc(即Owner)的__getattribute__()方法,就是TestDesc.__getattribute__() 先来访问实例属性,找到y,又发现属性y为一个描述符,于是将t.y转化成TestDesc.__dict__['y'].__get__(t, TestDesc) 但是,实际上TestDesc并没有y这个属性,y是属于实例对象的,所以,只能忽略了。