面向对象之反射
参考 :https://www.cnblogs.com/yuliangkaiyue/p/9516427.html
面向对象之内置方法
__setattr__ ,__getattr__,__delattr__
- class Foo:
- x = 1
- def __init__(self, y):
- self.y = y
- def __setattr__(self, key, value):
- print('----->setattr')
- # self.key =value #等同于赋值,不断触发setattr,造成递归循环
- self.__dict__[key] = value
- def __getattr__(self, item):
- print('------->getattr')
- print('%s不存在' % item)
- def __delattr__(self, item):
- print('-------delete')
- # del self.item # 同上理
- self.__dict__.pop(item)
- f = Foo(6)
- print(f.__dict__)
- f.z
- f.z =2
- print(f.__dict__)
- f.__dict__['a'] = 10 # 直接调用字典赋值并不会触发setattr方法
- del f.a
- print(f.__dict__)
- 执行结果:
- ----->setattr 因为初始化触发了__setattr__方法执行
- {'y': 6}
- ------->getattr 属性Z不存在,触发了getattr方法,所以找不到属性才触发
- z不存在
- ----->setattr
- {'y': 6, 'z': 2}
- -------delete
- {'y': 6, 'z': 2}
|
item系列
__setitem__,__getitem__,__delitem__ 在功能上和上边的有些类似,但是又稍有不同。item系列的特点是把类操作弄得像字典一样。
class
Foo:
-
def
__init__(self, name):
- self.name = name
-
-
def
__setitem__(self, key, value):
- print('from setitem')
- self.__dict__[key]=value
-
-
def
__getitem__(self, item):
- print('from getitem')
- print(self.__dict__.get(item))
-
-
def
__delitem__(self, key):
- print('from delitem del 类[key]')
- self.__dict__.pop(key)
-
-
def
__delattr__(self, item):
- print('from delattr del 类.属性')
- self.__dict__.pop(item)
-
-
- f1=Foo('李四')
- print(f1.__dict__)
- f1['age']=18
- f1['sex']='男'
- print(f1.__dict__)
- del f1.age
- del f1['sex']
- f1['home']='滨州'
- print(f1.__dict__)
-
- # {'name': '李四'}
- # from setitem
- # from setitem
- # {'name': '李四', 'age': 18, 'sex': '男'}
- # from delattr
- # from delitem
- # from setitem
- # {'name': '李四', 'home': '滨州'}
|
为类设置输出 __str__,__repr__,__format__
改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__
- format_dict={
-
'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
-
'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
-
'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
- }
- class
School:
-
def
__init__(self,name,addr,type):
- self.name=name
- self.addr=addr
- self.type=type
-
-
def
__repr__(self):
-
return
'School(%s,%s)' %(self.name,self.addr)
-
def
__str__(self):
-
return
'(%s,%s)' %(self.name,self.addr)
-
-
def
__format__(self, format_spec):
-
# if format_spec
-
if
not format_spec or format_spec not
in format_dict:
- format_spec='nat'
- fmt=format_dict[format_spec]
-
return fmt.format(obj=self)
-
- s1=School('oldboy1','北京','私立')
- print('from repr: ',repr(s1))
- print('from str: ',str(s1))
- print(s1)
-
- # from repr: School(oldboy1,北京)
- # from str: (oldboy1,北京)
- # (oldboy1,北京)
- '''
- str函数或者print函数--->obj.__str__()
- repr或者交互式解释器--->obj.__repr__()
- 如果__str__没有被定义,那么就会使用__repr__来代替输出
- 注意:这俩方法的返回值必须是字符串,否则抛出异常
- '''
- print(format(s1,'nat'))
- print(format(s1,'tna'))
- print(format(s1,'tan'))
- print(format(s1,'a'))
-
- # oldboy1-北京-私立
- # 私立:oldboy1:北京
- # 私立/北京/oldboy1
- # oldboy1-北京-私立
|
当然也可以在__str__()中调用format方法,以显示想要的效果
析构方法 __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__。简而言之,操作系统资源要通过代码手动调用管理的,用完就关闭它,用del方式可以防止忘记关闭操作系统资源而出错,也使代码简洁。
- class
Foo:
-
-
def
__del__(self):
- print('回收')
-
- f1=Foo()
- del f1
- print('------->')
-
- #输出结果
- # 回收
- # ------->
|
描述符(__get__, __set__, __delete__)
描述符
本质就是一个新式类,在这个类中至少实现了__get__, __set__, __delete__中的一个,这也被称为描述符协议。
__get__():调用一个属性时触发
__set__():为一个属性赋值时触发
__delete__():采用del删除一个属性时触发
作用
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
- class
Foo:
-
def
__get__(self, instance, owner):
- print('触发get')
-
def
__set__(self, instance, value):
- print('触发set')
-
def
__delete__(self, instance):
- print('触发delete')
-
-
- f1=Foo()
- f1.name='egon'
- f1.name
- del f1.name
|
但是,这样子并没有触发三个方法的执行
那如何才会触发执行呢?
- #描述符Str
- class
Str:
-
def
__get__(self, instance, owner):
- print('Str调用')
-
def
__set__(self, instance, value):
- print('Str设置...')
-
def
__delete__(self, instance):
- print('Str删除...')
-
- #描述符Int
- class
Int:
-
def
__get__(self, instance, owner):
- print('Int调用')
-
def
__set__(self, instance, value):
- print('Int设置...')
-
def
__delete__(self, instance):
- print('Int删除...')
-
- class
People:
- name=Str()
- age=Int()
-
def
__init__(self,name,age): #name被Str类代理,age被Int类代理,
- self.name=name
- self.age=age
-
- #何地?:定义成另外一个类的类属性
-
- #何时?:且看下列演示
-
- p1=People('alex',18)
-
- #描述符Str的使用
- p1.name
- p1.name='egon'
- del p1.name
-
- #描述符Int的使用
- p1.age
- p1.age=18
- del p1.age
-
- #我们来瞅瞅到底发生了什么
- print(p1.__dict__)
- print(People.__dict__)
-
- #补充
- print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
- print(type(p1).__dict__ == People.__dict__)
-
-
- 执行结果:
-
- Str设置...
- Int设置...
- Str调用
- Str设置...
- Str删除...
- Int调用
- Int设置...
- Int删除...
- {}
- {'__module__': '__main__', 'name': <__main__.Str object at 0x0000000A51974438>, 'age': <__main__.Int object at 0x0000000A51ACFC18>, '__init__': <function People.__init__ at 0x0000000A51AD3BF8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
- True
- True
|
注意事项
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性触发__getattr__()