通过字典的方式操纵类的属性:item系列方法
以点的方式操作属性,和attr有关。字典操纵属性,和item有关
和操作属性一样,如果不往字典里面写入是不会有相应的方法或属性的。添加一个属性:
class Test:
def __setitem__(self, key, value):
print('setitem')
self.__dict__[key] = value
t = Test()
print(t.__dict__)
t['name'] = 'Jax'
print(t.__dict__)
>>> {}
>>> setitem
>>> {'name': 'Jax'}
获取一个属性:
class Test:
def __getitem__(self, item):
print('getitem')
print(self.__dict__[item])
def __setitem__(self, key, value):
print('setitem')
self.__dict__[key] = value
t = Test()
t['name'] = 'Jax'
print(t['name'])
>>> setitem
>>> getitem
>>> Jax
>>> None
删除一个属性
class Test:
def __setitem__(self, key, value):
print('setitem')
self.__dict__[key] = value
def __delitem__(self, key):
print('delitem')
self.__dict__.pop(key)
t = Test()
t['name'] = 'Jax'
print(t.__dict__)
del t['name']
print(t.__dict__)
>>> setitem
>>> {'name': 'Jax'}
>>> delitem
>>> {}
__str__和__repr__方法:
1、两者的返回值都必须是字符串。
2、str函数和print函数触发__str__, repr函数或者交互式解释器触发__repr__()
3、__str__里面必须return一个值。打印一个实例对象(x)时,首先执行str(x)---->x.__str__() 如果再找不到就去找__repr__()
4、__repr__里面必须return一个值,在解释器里面有用 ,打印实例时触发,控制输出, 。两者共存时打印实例对象会显示__str__()
class Test:
def __str__(self):
return 'I am str'
def __repr__(self):
return 'I am repr'
t = Test()
print(t)
>>> I am str
format函数打印属性值
class Test:
def __init__(self, name, value):
self.name = name
self.value = value
t = Test('Jax', 15)
print('{0.name}{0.value}'.format(t))
>>> Jax15
__slot__变量:
是一个类变量,变量值可以是列表、可迭代对象。如果一个属性很少的类有很多实例,就用__slots__节省内存。
但是用__slots__以后就不能再给实例对象添加新的属性。加上__slots__的类无法在支持一些类的特性,例如多继承。也无法进行赋值因为打不开__dict__
由__slots__创建的类不再具有__dict__的属性
class Test:
__slots__ = ['name','gender']
t =Test()
t.name = 15
t.gender = 'Male'
print(t.__slots__)
__del__:
析构方法:只有当释放掉对象或内存时才会被触发,当python文件运行结束时也会释放掉空间,触发析构方法
class Test:
def __init__(self, name):
self.name = name
def __del__(self):
print('I an running')
t = Test('Jax')
del t.name # 删掉的是一个属性所以不会触发析构方法
print('--------------')
>>> --------------
>>> I an running
删除实例对象,触发析构方法
class Test:
def __init__(self, name):
self.name = name
def __del__(self):
print('I an running')
t = Test('Jax')
del t # 删掉的是对象所以触发析构方法
print('--------------')
>>> I an running
>>> --------------
__call__方法:
对象后面加括号,触发执行。是调用类的__call__方法,*args是初始化传递的参数,**kwargs是初始化传递的位置参数
class Test:
def __call__(self, *args, **kwargs):
print('running')
t = Test()
t()
>>> running
t()----->调用的是类的__call__方法,Test()---->调用的__call__方法,返回的只是一个类的地址而已
描述符:
在描述符协议中,它可以通过方法重写属性的访问。这些方法有 __get__(), __set__(), 和__delete__()。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。
只要和x有关的操作不会写入到t的字典中,用foo来描述x属性,x被foo代理了。
class Foo:
def __get__(self, instance, owner):
print('get method')
print(instance)
print(owner) # 来自哪个类
def __set__(self, instance, value):
print('set method')
print(instance) # 实例的对象
print(value) # 值
def __delete__(self, instance):
print('delete method')
print(instance)
class Test:
x = Foo() # 用foo来描述x属性,x被foo代理了
def __init__(self, x):
self.x = x # 触发了set函数,如果换成self.m就不会触发
t = Test(15)
print(Test.x) # 触发了get函数
>>> set method
>>> get method
优先级:1、类属性高于数据描述符,把原来的覆盖掉了
class Foo:
def __get__(self, instance, owner):
print('get method')
class Test:
x = Foo()
Test.x = 15
print(Test.x) # 类的属性优先级高
>>> 15
2、数据描述符高于实例属性,因为当实例对象找不到属性,会去类的属性里面找,类属性里面该属性是个描述符,所以描述符高于实例
class Foo:
def __get__(self, instance, owner):
print('get method')
class Test:
x = Foo()
t = Test()
print(t.x)
>>> get method
4、非数据描述符 因为找不到描述符所以只好赋值,就是调用其他属性(叫做非数据描述符)
class Foo:
def __get__(self, instance, owner):
print('get method')
def __set__(self, instance, value):
print('set method')
class Test:
x = Foo() # 用foo来描述x属性,x被foo代理了
t = Test()
t.m = 15
print(t.m)
>>> 15
5、找不到属性就只能触发__getattr__()方法