python_面向对象的进阶
一、 isinstance(obj,cls)
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
1 #判断obj是不是Foo类的对象 2 class Foo(object): 3 pass 4 5 obj = Foo() 6 7 isinstance(obj, Foo)
issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object): pass class Bar(Foo): pass issubclass(Bar, Foo)
二、反射
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
一切皆对象,类也是对象。
1.反射的四种方法:hasattr(obj,var)、setattr(obj,key,value)、getattr(obj,key),delattr(obj,key)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #判断f中有没有‘name’这个属性 2 print(hasattr(f,'name')) #f.name
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #添加值,其实和在外面添加新的值是一样,但是这个是可以做判断,什么可以添加什么不能添加 2 setattr(f,'age',18) #f.age = 18 3 print(f.age)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #获取值,也是一样可以做判断,什么获取什么不获取 2 print(getattr(f,'name')) #f.name
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #删除属性,还可以做判断删除。 2 print(f.__dict__) 3 delattr(f,'name') 4 print(f.__dict__)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Foo: 2 x = 1 3 def __init__(self,name): 4 self.name = name 5 f = Foo('yang') 6 # 检测属性 7 print(hasattr(f,'name')) #f.name 8 print(hasattr(f,'x')) #f.x 9 print(hasattr(Foo,'x')) 10 print(Foo.__dict__) 11 print(hasattr(Foo,'name')) #Flase #对象可以在自己里面找,还可以在类里面找。但是类智能在自己的里面找,不能在对象里面找。 12 13 #获取属性 14 print(getattr(f,'name')) #f.name 15 print(getattr(f,'x')) #f.x 16 print(getattr(Foo,'x')) #Foo.x 17 # print(getattr(Foo,'name')) 18 19 #设置属性 20 setattr(f,'age',18) #f.age = 18 21 print(f.age) 22 print(f.__dict__) 23 setattr(Foo,'y',110) #Foo.y = 000 24 print(Foo.y) 25 print(Foo.__dict__) 26 27 28 #删除属性 29 print(f.__dict__) 30 delattr(f,'name') 31 print(f.__dict__) 32 print(Foo.__dict__) 33 delattr(Foo,'x') 34 print(Foo.__dict__)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import sys 2 #反射当前模块的属性 3 x = 1 4 5 class foo: 6 k = 1 7 8 def s1(): 9 print('from s1') 10 11 #使用sys.modules[__name__]进行反射 12 this_module = sys.modules[__name__] 13 print(this_module) 14 print(hasattr(this_module,'x'))#查看模块里面的有没有这个属性 15 print(hasattr(this_module,'s1')) 16 s = getattr(this_module,'s1')#获取这个模块里面的属性 17 s() 18 x1 = getattr(this_module,'x') 19 print(x1) 20 p = setattr(this_module,'y',123132) 21 print(p)
2.反射机制的好处
好处1:实现可插拔机制
两个程序员在同时写自己的功能,A的功能还没写完,那么也不耽误B去使用A的功能
1 class FtpClient: 2 'ftp客户端,但是还么有实现具体的功能' 3 def __init__(self,addr): 4 print('正在连接服务器[%s]' %addr) 5 self.addr=addr 6 def test(self): 7 print('from test')
import ftpclient #自己定义的模块进行调用, f1=ftpclient.FtpClient('192.168.1.1') #ftpclient.FtpClient:调用文件下面的某个功能,进行使用 #调用的文件里面的功能是一个class类,在上面已经进行的实例化,所以下面就是在使用FtpClient类的功能 f1.test() if hasattr(f1,'get'): func_get=getattr(f1,'get') func_get() else: print('---->不存在此方法') print('处理其他的逻辑')
好处2:动态导入模块(基于反射当前模块成员)
m = input('请输入你要导入的模块:') m1 = __import__(m) print(m1) print(m1.time()) ------------------------------------------------------------------------ #推荐使用的方法,通过字符串导入模块 import importlib t = importlib.import_module('time') print(t.time())
----------------------------------------------------------------------------------------
#在pycharm中飘红不是代表报错了,只是python的环境变量中没有这个没有这个模块,
from aa.t import * 没有下划线,是可以用*来代替所有的
test()
test1()
from aa.t import test,_test1 #如果带下划线的属性,是不能用*来代替了,NameError: name '_test1' is not defined
test()
_test1()
3、反射的好处
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import sys def add(): print('add') def change(): print('change') def search(): print('search') def delete(): print('delete') ''' 反射的用途: 1.通过sys导入自己的模块, 2.在自己的模块里面写很多的功能,可以直接使用attr的方式进行跟用户交互的方式。 ''' this_module = sys.modules[__name__] # func_dic = { # 'add':add, # 'change':change, # 'search':search, # 'delete':delete, # } while True: cmd = input('>>>:').strip() if not cmd:continue if hasattr(this_module,cmd): #用hasattr()在不在里面 func = getattr(this_module,cmd) #获取里面的值 func() # if cmd in func_dic: #hasattr # func= func_dic.get(cmd) #func = getattr # func()
三、__setattr__、__delattr__、__getattr__的使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # __setattr__: 通过object中的__setattr__函数来设置属性,查看__init__下面的属性,key 和value,且是逐个打印 2 # __delattr__:用来删除对象的属性。 3 # __getattr__:从对象中读取某个属性时,首先需要从self.__dict__中搜索该属性,再从__getattr__中查找。属性不存在的情况下才会触发 4 class Foo: 5 def __init__(self,name,age): 6 self.name = name 7 self.age = age 8 9 def __setattr__(self, key, value): 10 ''' 11 查看__init__下面的属性,key 和value 12 13 ''' 14 if not isinstance(value,str): 15 raise TypeError('只能是str类型') 16 print('......setattr....key:%s,value:%s' %(key,value)) 17 print(type(key)) 18 print(type(value)) 19 self.__dict__[key] = value #给__dict__里面设置字典 20 21 def __delattr__(self, item): 22 ''' 23 1.delattr功能可以想到的是: 24 在函数里面可以做一些处理,判断可以删上面不能删上面, 25 如果是不定义这个__delattr__函数,就是想删上面都可以 26 ''' 27 if not isinstance(item,str): 28 raise TypeError('不能删除') 29 print('delattr:%s'%item) 30 print(type(item)) 31 self.__dict__.pop(item) 32 33 f = Foo('yang',20) 34 f.age = '19' #__setattr__产生的 35 print(f.__dict__) 36 del f.age #__delattr__产生的 37 f.sss = 'sdfa' 38 print(f.sss) 39 print(f.__dict__) 40 41 #------------------------------------------------------ 42 #这是__getattr__函数方法 43 class Foo: 44 def __init__(self,name): 45 self.name = name 46 47 #属性不存在的情况下才会触发 48 def __getattr__(self, item): 49 print('getattr--->%s %s'%(item,type(item))) 50 51 52 f1 = Foo('yang') 53 print(f1.aaaaa) #产生__getattr__
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #基于继承的原理,来定制自己的数据类型(继承标准类型) 2 #str、int、dict、set、tuple、list都是属于类的数据。所以都是可以被继承的类型 3 class List(list): 4 def append(self, p_object):#给当前写一个添加的方法, 5 if not isinstance(p_object,int): #判断动态添加的属性是否是int类型的, 6 raise TypeError('must be int') #强制报错,提示只能输入ing类型的。 7 super().append(p_object) #不能写成self.append()这样会递归报错,所以要写成super().append() 8 def insert(self, index, p_object): 9 super().insert(index,p_object) 10 def __getattr__(self, item): 11 return getattr(super(),item) 12 l = List([1,2,3]) 13 # l.append(55) 14 # l.append(4) 15 # print(l) 16 # l.insert(0,'5555') 17 print(l) 18 l.insert(2,666) 19 print(l) 20 l.clear() 21 22 23 #提示x的值只能是int,y的值只能是int,最后的结构只能是int 24 def test(x:int,y:int)->int: 25 return x+y 26 print(test.__annotations__) #检测你的类型。 27 # print(test(1,'444')) 28 29 30 31 #定义一个open 的数据类型 32 class Open: 33 def __init__(self,fliepath,m='r',encode='utf-8'): 34 self.x = open(fliepath,mode=m,encoding=encode) 35 self.fliepath = fliepath 36 self.mode = m 37 self.encode = encode 38 def write(self,line): #定义自己写的方法 39 self.x.write(line) 40 #如果要重新定义open类型的方法,不可能每个都自己重新写,那么久用到下面的这种方法。 41 #————gatattr__方法是,属性不存在的情况下才会触发, 42 def __getattr__(self, item): 43 res = getattr(self.x,item) #res = 获取(open,里面的方法),这样open里面的方法就都能用用了 44 return res #需要使用这个方法 ,所以需要返回。 45 f = Open('b.txt','w+') 46 f.write('111111111\n') 47 f.write('111111111\n') 48 f.write('111111111\n') 49 f.seek(0) 50 print(f.read()) 51 f.seek(0) 52 print('------->\n',f.read())
四、__call__、__slots__、__del__方法
__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
''' __call__是什么:是python的一个内置函数,对象+()或类()+()可以执行__call__函数 ''' class Foo: def __init__(self,name,age): self.name = name self.age = age def __call__(self, *args, **kwargs): print('from call') def test(self): print('sssss') f = Foo('yang',20) # print(f.name) # print(callable(Foo)) #是否可调用 # print(callable(f)) #是否可调用 f() Foo('yang',20)()
__slots__
''' __slots__,将你的定义的属性不会放到内存中,这样节省内存。 不能与__init__一起使用,有报错(AttributeError: 'Foo' object has no attribute 'name') 而且实例化中也没有__dict__这个属性, 1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者是可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的。而每个实例是独立的) 3.为何使用__slots__:字典会占用大量的内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定代销的数组来构建,而不是为每个实例顶一个 字典,这跟元祖或列表很类似,在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能在给 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,不如多继承。大多数情况下, 你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象。 关于__slots__的一个长剑误区是它可以作为一个封装工具来防止用户给实例增加新的属性,尽管使用__slots__可以达到这样的目的,但是这个并不是他的初衷, 更多的是用来作为一个内存优化工具 ps.在__slots__内定义的,都归__slots__管,这个节省内存 ''' class Foo: # def __init__(self,name,age): # self.name = name # self.age = age __slots__ = ('x', 'y', 'z') f = Foo() f.x = 1 f.y = 2 f.z = 3 f.k = 9 #报错,不存在的属性, # print(f.__dict__) #通过对象去找自己的名称空间,是没有__dict__ print(Foo.__dict__)
__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Foo: 2 3 def __del__(self): 4 print('执行我啦') 5 6 f1=Foo() 7 del f1 8 print('------->') 9 10 #输出结果 11 执行我啦 12 ------->
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Open: 2 def __init__(self,filepath,mode='r',encode='utf-8'): 3 self.f=open(filepath,mode=mode,encoding=encode) 4 def __getattr__(self, item): 5 return getattr(self.f,item) 6 def __del__(self): 7 print('----->del') 8 self.f.close() 9 10 f = Open('b.txt','w') 11 f1 = f #将f 赋值给f1,相当于我删除了f,就会执行end的打印,不会执行__del__函数,因为还没又删除f1 12 del f 13 14 print('----end------')
五、__setitem__、__getitem__、__delitem__
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #把对象操作模拟成字典格式 2 ''' 3 4 __setitem__:把对象操作模拟成字典的格式,内置函数有固定的参数,self, key, value对应的是f['age'] = 19,添加值 5 f['age'] = 19 #通过f['age'] = f.'age' 添加值 6 __getitem__:查询值/获取值,把对象操作模拟成字典的格式,内置函数有固定的参数,self, item对应的是self.__dict__[item] 7 f['name'] #查看值f['name']= f.name 8 __delitem__:删除值,吧对象操作模拟成字典的格式,内置函数有固定的参数,self, key对应的是self.__dict__.pop(key) 9 del f['age'] #del f['age'] = del f.age 删除值 10 11 ps.上述,都是使用对象['key']的方式去做查询、添加、删除操作的 12 下面的就是上述解析的案例: 13 14 ''' 15 class Foo: 16 def __init__(self,name): 17 self.name = name 18 def __setitem__(self, key, value): 19 self.__dict__[key] = value 20 def __getitem__(self, item): 21 return self.__dict__[item] 22 def __delitem__(self, key): 23 self.__dict__.pop(key) 24 # def __str__(self): 25 # return self[self.__dict__] 26 27 f = Foo('yang') 28 f['age'] = 19 #通过f['age'] = f.'age' 设置值 29 print(f.age) 30 # print(f['sss']) 31 print(f.__dict__) 32 del f['age'] #del f['age'] = del f.age 设置删除值 33 print(f.__dict__) 34 print(f['name']) #查看值f['name']= f.name
六、__doc__、__module__、__class__
__doc__
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Foo: 2 '我是描述信息' 3 pass 4 5 class Bar(Foo): 6 pass 7 print(Bar.__doc__) #该属性无法继承给子类
__modeule__表示当前操作的对象在那个模块
__class__表示当前操作的对象的类是什么
#继承父类,不能继承父类的内部注释,__doc__ class Foo: 'ssss' pass class Bar(Foo): pass f = Foo() b = Bar() print(b.__doc__) #当前类中的注释 print(f.__class__) #输入当前f的类 print(f.__module__) #输出模块 # print(f.__doc__) #继承父类,不能继承父类的内部注释,
七、__next__和__iter__实现迭代器协议
1 ''' 2 实现迭代器协议,使用__iter__和__next__内置函数, 3 1.使用__iter__和__next__实现迭代器该有的功能,将类传换成一个迭代器, 4 PS.如下代码实现 5 ''' 6 from collections import Iterable,Iterator 7 class Foo: 8 def __init__(self,strat,end): 9 self.strat = strat 10 self.end = end 11 def __iter__(self): 12 return self 13 def __next__(self): 14 if self.strat >self.end: 15 raise StopIteration 16 n = self.strat 17 self.strat += 1 18 return n 19 20 f = Foo(0,19) 21 print(isinstance(f,Iterable)) 22 # print(next(f.strat)) 23 # print(next(f.strat)) 24 # print(next(f.strat)) 25 for i in f: 26 print('---->',i)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class squares: 2 def __init__(self, start, stop): 3 self.flag = start - 1 4 self.value = self.flag 5 self.stop = stop 6 7 def __iter__(self): 8 self.value = self.flag 9 return self 10 11 def __next__(self): 12 if self.value == self.stop: 13 raise StopIteration 14 self.value += 1 15 return self.value 16 17 18 a = squares(1, 5) 19 b = squares(1, 5) 20 s = 0 21 while s <= 41: 22 for i in a: 23 s = s + i 24 print(s)
八、 __enter__和__exit__
''' 上下文管理协议:使用__enter__、__exit__内置函数 with open('b.txt','w') as f: '代码块' 上述叫做上下文管理协议,即with语句,为了让一个对象兼容,必须要在类当中添加__enter__、__exit__内置函数来实现with语句 ''' class Foo: def __init__(self,fliepath,mode='w',encode='utf-8'): self.x = open(fliepath,mode=mode,encoding=encode) def __getattr__(self, item): ''' 在使用__enter__的时候,__enter__的return 的返回值应该是self,不能加.文件句柄,如果加了的话。 那么在执行__getattr__的话就会跳过,直接执行__getattr__里面的代码。 __getattr__接受的是对象本身。 ''' return getattr(self.x,item) #必须要加上return 对你的代码进行一个返回给获取代码的对象,即f.f.write()执行 def __enter__(self): #执行with需要加入__enter__函数 print('enter') return self #返回的是对象本身,即f def __exit__(self, exc_type, exc_val, exc_tb): ''' exc_type:报错的类型, exc_val:报错的内容 exc_tb:找到报错的对象的内存地址。 ''' # print('exc_type',exc_type) # print('exc_val',exc_val) # print('exc_tb',exc_tb) print('with 执行完了') # return True #加上这个代码,当你报错,wtih外面的代码会继续执行 #有了return True 即不会产生报错,会将报错制止。 #返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行 # pass # f = Foo('b.txt','w') # # time.sleep(2) # f.x.write('sdfsafadfsas') # del f f = Foo('b.txt','r') # f.write('我是谁。。。') print(f.read()) # with Foo('b.txt','w') as obj: #即.obj = Foo('b.txt','w')实例化 # print('with foo的子代码块',obj) #obj是通过Foo实例化出来的 # # obj.write('2222222222222222') obj.x.write() # print(obj.read()) # # print('***************************') # # raise NameError('出错.....') # print(1111111111111111111111111111111111111111) ''' 上面的实例: 先执行enter函数,再执行with下的代码,最后执行exit函数退出。执行with外面的代码。 当exit函数中的(exc_type, exc_val, exc_tb)报错就会终止with的子代码的运行,加上一个return True就可以制止报错, '''
用途或者说好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
九、metaclass
python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)
class Foo: def __init__(self,name): self.name = name f = Foo('yang') print(type(Foo)) # type成为元类,是所有类的类,利用type模拟class关键字的创建类的过程 def run(self): print('这是你的名字:%s'%self.name) class_name = 'Foo' besae = (object,) class_dict = { 'x':1, 'run':run, } ''' 自己定义一个类,所需要定义三个变量,class_name,besae,class_dict class_name:定义类名 besae:定义所继承的父类 class_dict:定义自己的名称空间 ''' s = type(class_name,besae,class_dict) print(type(s)) print(s.__bases__) #继承的类 print(s.__name__) #对象的类名 print(s.__module__) # print(s.__dict__)
自定义元类
class Foo(metaclass=type): x = 1 def run(self): print('running') # type('Foo',(object),{'x':1,'run':run}) #定义一个强制子类添加__doc__ class Mymeta(type): 'dsfsdf' def __init__(self,class_name,besae,class_dict): print(self) print(class_name) print(besae) print(class_dict) for key in class_dict: if not callable(class_dict[key]):continue if not class_dict[key].__doc__: raise TypeError('自己写注释') class Foo(metaclass=Mymeta): 'asdfdf' x = 1 def run(self): print('running') print(Foo.__doc__)