面向对象2 面向对象之反射 面向对象之内置方法

参考 :https://www.cnblogs.com/yuliangkaiyue/p/9516427.html

面向对象之内置方法

__setattr__ ,__getattr__,__delattr__

  1. class Foo:
  2. x = 1
  3. def __init__(self, y):
  4. self.y = y
  5. def __setattr__(self, key, value):
  6. print('----->setattr')
  7. # self.key =value #等同于赋值,不断触发setattr,造成递归循环
  8. self.__dict__[key] = value
  9. def __getattr__(self, item):
  10. print('------->getattr')
  11. print('%s不存在' % item)
  12. def __delattr__(self, item):
  13. print('-------delete')
  14. # del self.item # 同上理
  15. self.__dict__.pop(item)
  16. f = Foo(6)
  17. print(f.__dict__)
  18. f.z
  19. f.z =2
  20. print(f.__dict__)
  21. f.__dict__['a'] = 10 # 直接调用字典赋值并不会触发setattr方法
  22. del f.a
  23. print(f.__dict__)
  24. 执行结果:
  25. ----->setattr 因为初始化触发了__setattr__方法执行
  26. {'y': 6}
  27. ------->getattr 属性Z不存在,触发了getattr方法,所以找不到属性才触发
  28. z不存在
  29. ----->setattr
  30. {'y': 6, 'z': 2}
  31. -------delete
  32. {'y': 6, 'z': 2}

item系列

  __setitem__,__getitem__,__delitem__ 在功能上和上边的有些类似,但是又稍有不同。item系列的特点是把类操作弄得像字典一样。


  1. class Foo:
  2. def __init__(self, name):
  3. self.name = name
  4. def __setitem__(self, key, value):
  5. print('from setitem')
  6. self.__dict__[key]=value
  7. def __getitem__(self, item):
  8. print('from getitem')
  9. print(self.__dict__.get(item))
  10. def __delitem__(self, key):
  11. print('from delitem del [key]')
  12. self.__dict__.pop(key)
  13. def __delattr__(self, item):
  14. print('from delattr del .属性')
  15. self.__dict__.pop(item)
  16. f1=Foo('李四')
  17. print(f1.__dict__)
  18. f1['age']=18
  19. f1['sex']=''
  20. print(f1.__dict__)
  21. del f1.age
  22. del f1['sex']
  23. f1['home']='滨州'
  24. print(f1.__dict__)
  25. # {'name': '李四'}
  26. # from setitem
  27. # from setitem
  28. # {'name': '李四', 'age': 18, 'sex': ''}
  29. # from delattr
  30. # from delitem
  31. # from setitem
  32. # {'name': '李四', 'home': '滨州'}

为类设置输出 __str__,__repr__,__format__

  改变对象的字符串显示__str__,__repr__

  自定制格式化字符串__format__

  1. format_dict={
  2. 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
  3. 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
  4. 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
  5. }
  6. class School:
  7. def __init__(self,name,addr,type):
  8. self.name=name
  9. self.addr=addr
  10. self.type=type
  11. def __repr__(self):
  12. return 'School(%s,%s)' %(self.name,self.addr)
  13. def __str__(self):
  14. return '(%s,%s)' %(self.name,self.addr)
  15. def __format__(self, format_spec):
  16. # if format_spec
  17. if not format_spec or format_spec not in format_dict:
  18. format_spec='nat'
  19. fmt=format_dict[format_spec]
  20. return fmt.format(obj=self)
  21. s1=School('oldboy1','北京','私立')
  22. print('from repr: ',repr(s1))
  23. print('from str: ',str(s1))
  24. print(s1)
  25. # from repr: School(oldboy1,北京)
  26. # from str: (oldboy1,北京)
  27. # (oldboy1,北京)
  28. '''
  29. str函数或者print函数--->obj.__str__()
  30. repr或者交互式解释器--->obj.__repr__()
  31. 如果__str__没有被定义,那么就会使用__repr__来代替输出
  32. 注意:这俩方法的返回值必须是字符串,否则抛出异常
  33. '''
  34. print(format(s1,'nat'))
  35. print(format(s1,'tna'))
  36. print(format(s1,'tan'))
  37. print(format(s1,'a'))
  38. # oldboy1-北京-私立
  39. # 私立:oldboy1:北京
  40. # 私立/北京/oldboy1
  41. # oldboy1-北京-私立

 

  当然也可以在__str__()中调用format方法,以显示想要的效果 

 析构方法  __del__

  析构方法,当对象在内存中被释放时,自动触发执行。

  注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__。简而言之,操作系统资源要通过代码手动调用管理的,用完就关闭它,用del方式可以防止忘记关闭操作系统资源而出错,也使代码简洁。

  1. class Foo:
  2. def __del__(self):
  3. print('回收')
  4. f1=Foo()
  5. del f1
  6. print('------->')
  7. #输出结果
  8. # 回收
  9. # ------->

 

描述符(__get__, __set__, __delete__)

描述符

本质就是一个新式类,在这个类中至少实现了__get__, __set__, __delete__中的一个,这也被称为描述符协议。

  __get__():调用一个属性时触发

  __set__():为一个属性赋值时触发

  __delete__():采用del删除一个属性时触发

作用

描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

  1. class Foo:
  2. def __get__(self, instance, owner):
  3. print('触发get')
  4. def __set__(self, instance, value):
  5. print('触发set')
  6. def __delete__(self, instance):
  7. print('触发delete')
  8. f1=Foo()
  9. f1.name='egon'
  10. f1.name
  11. del f1.name

 

  但是,这样子并没有触发三个方法的执行

  那如何才会触发执行呢?

  1. #描述符Str
  2. class Str:
  3. def __get__(self, instance, owner):
  4. print('Str调用')
  5. def __set__(self, instance, value):
  6. print('Str设置...')
  7. def __delete__(self, instance):
  8. print('Str删除...')
  9. #描述符Int
  10. class Int:
  11. def __get__(self, instance, owner):
  12. print('Int调用')
  13. def __set__(self, instance, value):
  14. print('Int设置...')
  15. def __delete__(self, instance):
  16. print('Int删除...')
  17. class People:
  18. name=Str()
  19. age=Int()
  20. def __init__(self,name,age): #nameStr类代理,ageInt类代理,
  21. self.name=name
  22. self.age=age
  23. #何地?:定义成另外一个类的类属性
  24. #何时?:且看下列演示
  25. p1=People('alex',18)
  26. #描述符Str的使用
  27. p1.name
  28. p1.name='egon'
  29. del p1.name
  30. #描述符Int的使用
  31. p1.age
  32. p1.age=18
  33. del p1.age
  34. #我们来瞅瞅到底发生了什么
  35. print(p1.__dict__)
  36. print(People.__dict__)
  37. #补充
  38. print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
  39. print(type(p1).__dict__ == People.__dict__)
  40. 执行结果:
  41. Str设置...
  42. Int设置...
  43. Str调用
  44. Str设置...
  45. Str删除...
  46. Int调用
  47. Int设置...
  48. Int删除...
  49. {}
  50. {'__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}
  51. True
  52. True

 

注意事项

  一 描述符本身应该定义成新式类,被代理的类也应该是新式类
  二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
  三 要严格遵循该优先级,优先级由高到底分别是
           1.类属性
           2.数据描述符
           3.实例属性
           4.非数据描述符
           5.找不到的属性触发__getattr__()