面向对象学习目录
一、isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
1 class Foo(object): 2 pass 3 4 obj = Foo() 5 isinstance(obj, Foo)
issubclass(sub, super)检查sub类是否是 super 类的派生类
1 class Foo(object): 2 pass 3 4 class Bar(Foo): 5 pass 6 7 issubclass(Bar, Foo)
二、反射
1 什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数——下列方法适用于类和对象(一切皆对象,类本身也是一个对象):
hasattr(object,name)
判断object中有没有一个name字符串对应的方法或属性
getattr(object, name, default=None)
def getattr(object, name, default=None): # known special case of getattr """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. """ pass
setattr(x, y, v)
def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ pass
delattr(x, y)
def delattr(x, y): # real signature unknown; restored from __doc__ """ Deletes the named attribute from the given object. delattr(x, 'y') is equivalent to ``del x.y'' """ pass
四个方法的使用演示
1 class BlackMedium: 2 feature='Ugly' 3 def __init__(self,name,addr): 4 self.name=name 5 self.addr=addr 6 7 def sell_house(self): 8 print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name) 9 10 def rent_house(self): 11 print('%s 黑中介租房子啦,傻逼才租呢' %self.name) 12 13 b1=BlackMedium('万成置地','回龙观天露园') 14 15 #检测是否含有某属性 16 print(hasattr(b1,'name')) 17 print(hasattr(b1,'sell_house'))
1 #获取属性 2 n=getattr(b1,'name') 3 print(n) 4 func=getattr(b1,'rent_house') 5 func() 6 7 # getattr(b1,'aaaaaaaa') #报错 8 print(getattr(b1,'aaaaaaaa','不存在啊'))
1 #设置属性 2 setattr(b1,'sb',True) 3 setattr(b1,'show_name',lambda self:self.name+'sb') 4 print(b1.__dict__) 5 print(b1.show_name(b1))
1 #删除属性 2 delattr(b1,'addr') 3 delattr(b1,'show_name') 4 delattr(b1,'show_name111') #不存在,则报错 5 6 print(b1.__dict__)
类也是对象
1 class Foo(object): 2 staticField = "old boy" 3 4 def __init__(self): 5 self.name = 'wupeiqi' 6 7 def func(self): 8 return 'func' 9 10 @staticmethod 11 def bar(): 12 return 'bar' 13 14 print getattr(Foo, 'staticField') 15 print getattr(Foo, 'func') 16 print getattr(Foo, 'bar')
反射查找当前模块成员
1 import sys 2 3 def s1(): 4 print 's1' 5 6 def s2(): 7 print 's2' 8 9 this_module = sys.modules[__name__] # 利用sys模块的modules方法将当前模块形成一个对象 10 11 hasattr(this_module, 's1') 12 getattr(this_module, 's2')
导入其他模块,利用反射查找该模块是否存在某个方法(即传入方法名的字符串查找)
1 """ 2 程序目录内容: 3 module_test.py 4 index.py 5 6 当前文件: 7 index.py 8 """ 9 10 import module_test as obj # 这里将导入的模块赋值给obj(作为一个对象) 11 12 #obj.test() 13 14 print(hasattr(obj,'test')) 15 16 getattr(obj,'test')()
3 为什么用反射——反射的好处
好处一:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
egon还没有实现全部功能
1 class FtpClient: 2 'ftp客户端,但是还么有实现具体的功能' 3 def __init__(self,addr): 4 print('正在连接服务器[%s]' %addr) 5 self.addr=addr
不影响lili的代码编写
1 #from module import FtpClient 2 f1=FtpClient('192.168.1.1') 3 if hasattr(f1,'get'): 4 func_get=getattr(f1,'get') 5 func_get() 6 else: 7 print('---->不存在此方法') 8 print('处理其他的逻辑')
好处二:动态导入模块(基于反射当前模块成员)
有两种方法,但有区别:
①__import__方法(解释器自己内部的)
这里用__import__导入的import_lib目录下的metaclass,确实会把要导入的模块metaclass执行一遍,但是返回的是import_lib这个目录的名称空间(如果赋值m1 = __import__('import_lib.metaclass'),则m1为import_lib目录的名称空间)
因为__import__只会返回同级文件的内存地址(若为同级文件夹则返回名称空间)
②importlib模块下的importlib.import_module方法
如果用importlib.import_module方法导入,会把要导入的模块metaclass执行一遍,并且返回的是想导入的模块metaclass的内存地址
三、__setattr__,__delattr__,__getattr__
都是给类的实例用的方法:
__setattr__是添加/修改实例的属性会触发它的执行(不去定义修改的话,默认就是在对象的__dict__里添加/修改属性的操作)
__delattr__是删除实例的属性会触发它的执行(不去定义修改的话,默认就是在对象的__dict__里删除属性)
__getattr__是调用实例的属性且属性不存在时才会触发执行(不去定义修改的话,默认就是对象无法调用到属性就报错)
三者的用法演示
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 self.__dict__.pop(item) #__setattr__添加/修改属性会触发它的执行 f1=Foo(10) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z=3 print(f1.__dict__) #__delattr__删除属性的时候会触发 f1.__dict__['a']=3 #我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx
四、二次加工标准类型(包装)
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
二次加工标准类型(基于继承实现)
1 class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid 2 def append(self, p_object): 3 ' 派生自己的append:加上类型检查' 4 if not isinstance(p_object,int): 5 raise TypeError('must be int') 6 super().append(p_object) # 检查通过后这里直接用的父类list的append方法添加 7 8 @property 9 def mid(self): 10 '新增自己的属性' 11 index=len(self)//2 12 return self[index] 13 14 15 l=List([1,2,3,4]) 16 print(l) 17 l.append(5) 18 print(l) 19 # l.append('1111111') #报错,必须为int类型 20 21 print(l.mid) 22 23 #其余的方法都继承list的 24 l.insert(0,-123) 25 print(l) 26 l.clear() 27 print(l)
练习(clear加权限限制)
1 class List(list): 2 def __init__(self,item,tag=False): 3 super().__init__(item) 4 self.tag=tag 5 def append(self, p_object): 6 if not isinstance(p_object,str): 7 raise TypeError 8 super().append(p_object) 9 def clear(self): 10 if not self.tag: 11 raise PermissionError 12 super().clear() 13 14 l=List([1,2,3],False) 15 print(l) 16 print(l.tag) 17 18 l.append('saf') 19 print(l) 20 21 # l.clear() #异常 22 23 l.tag=True 24 l.clear()
授权与包装的关系:
授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能,其它的则保持原样。
授权的过程,即是所有更新的功能都是由新类的某部分来包装定制处理,但已存在的不打算更新的功能就授权给对象的默认属性保留使用。
实现授权的关键点就是重设__getattr__方法
授权示范一(1.重设write方法 2.其他未重设的方法都随句柄自带内建方法)
import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): self.file=open(filename,mode,encoding=encoding) def write(self,line): # 重设内建方法 t=time.strftime('%Y-%m-%d %T') # 加入一个写入时间功能 self.file.write('%s %s' %(t,line)) # 这里没继承关系,就直接调用生成的句柄本身的内建方法write def __getattr__(self, item): # 其他未重设的属性/方法就授权用句柄自带内建方法 return getattr(self.file,item) # 这里如果item调用的是方法,则返回的是内存地址 f1=FileHandle('b.txt','w+') f1.write('你好啊') f1.seek(0) print(f1.read()) f1.close()
授权示范二
1 #我们来加上b模式支持 2 import time 3 class FileHandle: 4 def __init__(self,filename,mode='r',encoding='utf-8'): 5 if 'b' in mode: 6 self.file=open(filename,mode) 7 else: 8 self.file=open(filename,mode,encoding=encoding) 9 self.filename=filename 10 self.mode=mode 11 self.encoding=encoding 12 13 def write(self,line): 14 if 'b' in self.mode: 15 if not isinstance(line,bytes): 16 raise TypeError('must be bytes') 17 self.file.write(line) 18 19 def __getattr__(self, item): 20 return getattr(self.file,item) 21 22 def __str__(self): 23 if 'b' in self.mode: 24 res="<_io.BufferedReader name='%s'>" %self.filename 25 else: 26 res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding) 27 return res 28 29 f1=FileHandle('b.txt','wb') 30 # f1.write('你好啊啊啊啊啊') 31 # 自定制的write,不用在进行encode转成二进制去写了,简单,大气 32 f1.write('你好啊'.encode('utf-8')) 33 print(f1) 34 f1.close()
练习题(授权)
【练习一】
1 class List: 2 def __init__(self,seq): 3 self.seq=seq 4 5 def append(self, p_object): 6 ' 派生自己的append加上类型检查,重设原有的append' 7 if not isinstance(p_object,int): 8 raise TypeError('must be int') 9 self.seq.append(p_object) 10 11 @property 12 def mid(self): 13 '新增自己的方法' 14 index=len(self.seq)//2 15 return self.seq[index] 16 17 def __getattr__(self, item): # 授权list的其他方法给List 18 return getattr(self.seq,item) 19 20 def __str__(self): 21 return str(self.seq) 22 23 l=List([1,2,3]) 24 print(l) 25 l.append(4) 26 print(l) 27 # l.append('3333333') 28 # 报错,必须为int类型 29 30 print(l.mid) 31 32 #基于授权,获得insert方法 33 l.insert(0,-123) 34 print(l)
【练习二】
1 class List: 2 def __init__(self,seq,permission=False): 3 self.seq=seq 4 self.permission=permission 5 def clear(self): 6 if not self.permission: 7 raise PermissionError('not allow the operation') 8 self.seq.clear() 9 def __getattr__(self, item): 10 return getattr(self.seq,item) 11 12 def __str__(self): 13 return str(self.seq) 14 15 l=List([1,2,3]) 16 # l.clear() #此时没有权限,抛出异常 17 18 l.permission=True 19 print(l) 20 l.clear() 21 print(l) 22 23 #基于授权,获得insert方法 24 l.insert(0,-123) 25 print(l)
五、__getattribute__
调用类的实例的属性时,无论是否找的到该属性,都会触发__getattribute__
若__getattribute__与__getattr__同时存在,当找不到属性时,也只会执行__getattrbute__,除非__getattribute__在执行过程中指定抛出异常AttributeError(必须指定为AttributeError,其他异常会直接中止程序运行),那么该异常就会被屏蔽,且继续运行__getattr__
❉注:实际上,系统默认的__getattribute__方法就是在找到实例的属性时,返回这个属性的值,找不到则raise出AttributeError异常,如果我们写了__getattr__方法,则会屏蔽掉该异常,继续运行__getattr__
回顾__getattr__
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattr__(self, item): 6 print('执行的是我') 7 # return self.__dict__[item] 8 9 f1=Foo(10) 10 print(f1.x) 11 f1.xxxxxx #不存在的属性访问,触发__getattr__
getattribute
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattribute__(self, item): 6 print('不管是否存在,我都会执行') 7 8 f1=Foo(10) 9 f1.x 10 f1.xxxxxx
二者同时出现
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattr__(self, item): 6 print('执行的是我') 7 # return self.__dict__[item] 8 9 def __getattribute__(self, item): 10 print('不管是否存在,我都会执行') 11 raise AttributeError('哈哈') 12 13 f1=Foo(10) 14 f1.x 15 f1.xxxxxx 16 17 #当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError