一、反射
1、什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
2、Python面向对象中的反射
通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
3、四个可以实现自省的函数
(1)hasattr(object,name),判断object中有没有一个name字符串对应的方法或属性,检测是否含有某属性。
class BlackMedium: feture='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_hourse(self): print('【%s】正在卖房子,傻逼才买呢' %self.name) def rent_hourse(self): print('【%s】正在租房子,傻逼才租呢' %self.name) b1=BlackMedium('黑中介','北京') print(hasattr(b1,'name')) print(hasattr(b1,'sell_hourse')) """ True True """
(2)getattr(object, name, default=None),获取属性。
class BlackMedium: feture='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_hourse(self): print('【%s】正在卖房子,傻逼才买呢' %self.name) def rent_hourse(self): print('【%s】正在租房子,傻逼才租呢' %self.name) b1=BlackMedium('黑中介','北京') print(getattr(b1,'name')) print(getattr(b1,'rent_hourse')) fun=getattr(b1,'rent_hourse') fun() # print(getattr(b1,'aaaaa')) # 没有aaaaa这个属性则报错 print(getattr(b1,'aaaaa','没有这个属性')) # 没有aaaaa这个属性则输出“没有这个属性” """ 黑中介 <bound method BlackMedium.rent_hourse of <__main__.BlackMedium object at 0x101d3fba8>> 【黑中介】正在租房子,傻逼才租呢 没有这个属性 """
(3)setattr(x, y, v),设置属性。
class BlackMedium: feture='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_hourse(self): print('【%s】正在卖房子,傻逼才买呢' %self.name) def rent_hourse(self): print('【%s】正在租房子,傻逼才租呢' %self.name) b1=BlackMedium('黑中介','北京') setattr(b1,'sb',True) # b1.sb=True setattr(b1,'sb1',123) # b1.sb=True print(b1.__dict__) """ {'name': '黑中介', 'addr': '北京', 'sb': True, 'sb1': 123} """
(4)delattr(x, y),删除属性。
class BlackMedium: feture='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_hourse(self): print('【%s】正在卖房子,傻逼才买呢' %self.name) def rent_hourse(self): print('【%s】正在租房子,傻逼才租呢' %self.name) b1=BlackMedium('黑中介','北京') setattr(b1,'sb',True) # b1.sb=True setattr(b1,'sb1',123) # b1.sb=True delattr(b1,'sb') # del b1.sb delattr(b1,'sb1') # del b1.sb1 print(b1.__dict__) """ {'name': '黑中介', 'addr': '北京'} """
4、为什么要使用反射?
(1)“即插即用”:可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。
(2)动态导入模块(基于反射当前模块成员)
二、__setattr__,__delattr__,__getattr__
1、__getattr__含义是只要找的属性不存在就会触发__getattr__
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('你要找的属性不存在') f1=Foo(10) print(f1.y) f1.aaaaaa """ 10 你要找的属性不存在 """
2、__delattr__含义是执行删除操作的时候出发__delattr__
class Foo: x=1 def __init__(self,y): self.y=y def __delattr__(self, item): self.__dict__.pop(item) print('删除操作') f1=Foo(10) f1.__dict__['z']=3 print(f1.__dict__) del f1.y print(f1.__dict__) """ {'y': 10, 'z': 3} 删除操作 {'z': 3} """
3、__setattr__含义是添加/修改属性会触发它的执行
class Foo: x=1 def __init__(self,y): self.y=y def __setattr__(self, key, value): print('__setattr__执行') # self.key=value # 这样就会无限递归了 self.__dict__[key]=value # 直接修改底层字典 f1=Foo(10) print(f1.__dict__) f1.z=2 print(f1.__dict__) """ __setattr__执行 {'y': 10} __setattr__执行 {'y': 10, 'z': 2} """
4、__getattr__补充
class Foo: def __init__(self,name): self.name=name def __getattr__(self, item): print('你找的属性【%s】不存在' %item) f1=Foo('lionel') print(f1.name) print(f1.age) print(f1.sex) """ lionel 你找的属性【age】不存在 None 你找的属性【sex】不存在 None """
三、二次加工标准类型(包装)
1、包装
python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法。
class List(list): # 继承list所有的属性,也可以派生出自己新的,比如append和pop def append(self, p_object): '派生自己的append:加上类型检查' if type(p_object) is str: super().append(p_object) else: print('只能添加字符串类型') l1=list('hello world') print(l1,type(l1)) """ ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] <class 'list'> """ l2=List('hello world') print(l2,type(l2)) """ ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] <class '__main__.List'> """ l2.append('aaaaa') print(l2) """ ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'aaaaa'] """ l2.append(123123123) print(l2) """ 只能添加字符串类型 ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'aaaaa'] """
2、授权
授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能,其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。实现授权的关键点就是覆盖__getattr__方法。
import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): # self.filename=filename self.file=open(filename,mode,encoding=encoding) self.mode=mode self.encoding=encoding def write(self,line): t=time.strftime('%Y-%m-%d %X') self.file.write('%s %s' %(t,line)) def __getattr__(self, item): # print(item) return getattr(self.file,item) while True: info=input('>>: ') f1.write('%s\n' %info) # a.txt文件内容 """ 2018-04-20 18:06:15 cpu负载过高 2018-04-20 18:06:15 内存剩余不足 2018-04-20 18:06:15 硬盘剩余不足 2018-04-20 18:06:38 mysql集群宕机 2018-04-20 18:06:48 kvm虚拟机故障 2018-04-20 18:07:17 数据库被删除 """
四、isinstance(obj,cls)和issubclass(sub,super)
1、isinstance(obj,cls)检查是否obj是否是类cls的对象
class Foo: pass f1=Foo() print(isinstance(f1,Foo)) """ True """
2、issubclass(sub, super)检查sub类是否是super类的派生类
class Bar(Foo): pass print(issubclass(Bar,Foo)) """ True """
五、__getattribute__(self, item)
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('执行的是__getattr') # return self.__dict__[item] f1=Foo(10) print(f1.x) f1.xxxxxx """ 10 执行的是__getattr """
class Foo: def __init__(self,x): self.x=x # 不管找不找得到属性,都会触发__getattribute__ def __getattribute__(self, item): print('执行的是__getattribute__') raise AttributeError('抛出异常') f1=Foo(10) f1.x f1.xxxxxx
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('执行的是__getattr__') # return self.__dict__[item] # 不管找不找得到属性,都会触发__getattribute__ def __getattribute__(self, item): print('执行的是__getattribute__') raise AttributeError('抛出异常') f1=Foo(10) f1.x f1.xxxxxx """ 执行的是__getattribute__ 执行的是__getattr__ 执行的是__getattribute__ 执行的是__getattr__ """
总结
当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError,才会执行__getattr__。
六、__setitem__,__getitem,__delitem__
# 中括号形式调用就是*item方法,点的形式调用就是*attr方法 class Foo: def __getitem__(self, item): print('getitem',item) return self.__dict__[item] def __setitem__(self, key, value): print('setitem') self.__dict__[key] = value def __delitem__(self, key): print('delitem') self.__dict__.pop(key) f1=Foo() f1['name'] = 'lionel' f1['age'] = '18' print(f1.__dict__) """ setitem setitem {'name': 'lionel', 'age': '18'} """ del f1['name'] print(f1.__dict__) """ delitem {'age': '18'} """ print(f1['age']) """ getitem age 18 """
七、__str__与__repr__改变对象的字符串显示
1、改变对象的字符串显示
改变对象的字符串显示__str__,__repr__ 。 __str__在print时触发,__repr__在解释器中触发。
class Foo: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return '名字是%s 年龄是%s' %(self.name,self.age) f1=Foo('lionel',18) print(f1) # str(f1) ---> f1.__str__() """ 名字是lionel 年龄是18 """
class Foo: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return '这是str' def __repr__(self): return '名字是%s 年龄是%s' %(self.name,self.age) f1=Foo('lionel',20) print(f1) # str(f1) ---> f1.__str__() ,如果找不到str就会找repr repr(f1) ---> f1.__repr__() """ 这是str """ ''' str函数或者print函数--->obj.__str__() repr或者交互式解释器--->obj.__repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩方法的返回值必须是字符串,否则抛出异常 '''
八、自定义格式化方式__format__
format_dic={ 'ymd':'{0.year}{0.mon}{0.day}', 'y-m-d':'{0.year}-{0.mon}-{0.day}', 'y:m:d':'{0.year}:{0.mon}:{0.day}', 'default':'{0.year}_{0.mon}_{0.day}' } class Date: def __init__(self,year,mon,day): self.year=year self.mon=mon self.day=day def __format__(self, format_spec): print('我执行啦') if not format_spec or format_spec not in format_dic: format_spec='default' fm=format_dic[format_spec] return fm.format(self) d1=Date(2018,4,21) print(format(d1,'ymd')) # 2018421 print(format(d1,'y-m-d')) # 2018-4-21 print(format(d1,'y:m:d')) # 2018:4:21 print(format(d1)) # 2018_4_21 print(format(d1,'asd')) # 2018_4_21 """ 我执行啦 2018421 我执行啦 2018-4-21 我执行啦 2018:4:21 我执行啦 2018_4_21 我执行啦 2018_4_21 """
九、__next__和__iter__实现迭代器协议
class Range: def __init__(self,n,stop,step): self.n=n self.stop=stop self.step=step def __next__(self): if self.n >= self.stop: raise StopIteration x=self.n self.n+=self.step return x def __iter__(self): return self for i in Range(1,7,3): print(i) """ /usr/local/bin/python3 /Users/wangzhiwei/python/day7/13_迭代器协议.py 1 4 """
class Fib: def __init__(self): self._a=1 self._b=1 def __iter__(self): return self def __next__(self): if self._a > 100: raise StopIteration('终止了') self._a,self._b = self._b,self._a + self._b return self._a f1=Fib() for i in f1: print(i) """ 1 2 3 5 8 13 21 34 55 89 144 """
十、描述符(__get__,__set__,__delete__)
1、描述符是什么?
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__()、__set__()、__delete__()中的一个,这也被称为描述符协议。
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
2、描述符是干什么的?
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)。
class Foo: def __get__(self, instance, owner): print('===>get方法') def __set__(self, instance, value): print('===>set方法') def __delete__(self, instance): print('===>delete方法') class Bar: x=Foo() b1=Bar() b1.x """ ===>get方法 """ # __get__():调用一个属性时,触发 # __set__():为一个属性赋值时,触发 # __delete__():采用del删除属性时,触发
十一、上下文管理协议(__enter__和__exit__)
1、什么是上下文管理协议
上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。
class Open: def __init__(self,name): self.name=name def __enter__(self): print('执行enter') return self def __exit__(self, exc_type, exc_val, exc_tb): print('执行exit') with Open('a.txt') as f: print('=====>') print(f) print(f.name) print('00000000000') """ 执行enter =====> <__main__.Open object at 0x10353f9b0> a.txt 执行exit 00000000000 """ # with obj as f: # '代码块' # 1、with obj --->触发obj.__enter__(),拿到返回值 # 2、as f ---->f=返回值 # 3、with obj as f 等同于 f = obj.__enter__() # 4、执行代码块 # 一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None # 二:有异常的情况下,从异常出现的位置直接触发__exit__ # a:如果__exit__的返回值为True,代表吞掉了异常 # b:如果__exit__的返回值不为True,代表吐出了异常 # c:__exit__的运行完毕就代表了整个with语句的执行完毕
2、使用场景
- 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
- 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
十二、类的装饰器
def deco(obj): print('=======') return obj @deco def test(): print('test函数运行') test() """ ======= test函数运行 """ def deco(obj): print('=======') obj.x=1 obj.y=2 obj.z=3 return obj @deco class Foo(): pass f1=Foo() print(Foo.__dict__) print(f1.x) """ ======= {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3} 1 """
def Typed(**kwargs): def deco(obj): for key,val in kwargs.items(): setattr(obj,key,val) return obj print('===>',kwargs) return deco @Typed(x=1,y=2,z=3) class Foo(): pass print(Foo.__dict__) @Typed(name='lionel') class Bar: pass b1=Bar() print(b1.name) """ ===> {'x': 1, 'y': 2, 'z': 3} {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3} ===> {'name': 'lionel'} lionel """