python(26)- 面向对象补充Ⅱ
一 isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)判断obj是否是类 cls 的对象
class Foo(object): pass obj = Foo() print(isinstance(obj, Foo)) #检查obj是否是类Foo的对象 --->True
isinstance(x,list)判断x是否是列表list
x=[] print(isinstance(x,list)) --->True
issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object): pass class Bar(Foo): pass print(issubclass(Bar, Foo)) #检查Bar类是否是Foo的派生类 --->True
二 反射
1 反射的概念
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领
域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
2.1 hasattr() 判断属性
hasattr(object,name)判断object中有没有一个name字符串对应的方法或属性
class People: country='China' def __init__(self,name): self.name=name p=People('egon') print(p.__dict__) --->{'name': 'egon'} print(hasattr(p,'name')) #等同于print('name' in p.__dict__),检查对象p是否有“name”属性 --->True print(hasattr(p,'name1213')) #等同于print('name1213' in p.__dict__),检查对象p是否有“name1213”属性 --->False print(hasattr(p,'country')) #p.country,相当于查找p下是否有country --->True print(People.__dict__) --->{'__module__': '__main__', 'country': 'China', '__init__': <function People.__init__ at 0x0000000002203A60>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None} print(hasattr(People,'country')) #People.country,相当于查找People下是否有country --->True print(hasattr(People,'__init__')) #People.__init__,相当于查找People下是否有__init__ --->True
2.2 getattr() 获取属性
getattr(object, name, default=None) 获得对象object的‘name’的内存地址,name对应的方法或属性,结果返回object.name的值
当object.name不存在时,不会报错,自动返回第三个参数
class People: country='China' def __init__(self,name): self.name=name def walk(self): print('%s is walking' %self.name) p=People('egon') res=getattr(p,'country') #相当于res=p.country print(res) --->China f=getattr(p,'walk') #相当于f=p.walk print(f) ---><bound method People.walk of <__main__.People object at 0x00000000027CDC50>> f1=getattr(People,'walk') #相当于f1=People.walk print(f1) ---><function People.walk at 0x00000000027CF048> f() #对象的绑定方法运行 --->egon is walking f1(p) #类的函数运行 --->egon is walking print(p.xxxxxxx) #输入不存在的数据属性或方法,报错 --->AttributeError: 'People' object has no attribute 'xxxxxxx' print(getattr(p,'xxxxxxxx')) --->AttributeError: 'People' object has no attribute 'xxxxxxx' print(getattr(p,'xxxxxxxx','这个属性确实不存在')) #属性不存在时,getattr自动返回第三个参数 --->这个属性确实不存在
一般hasattr与getattr配合使用
class People: country='China' def __init__(self,name): self.name=name #def walk(self): #print('%s is walking' %self.name) p=People('egon') if hasattr(p, 'walf'): #避免查找p下的绑定方法walk报错 func = getattr(p,'walk') func()
2.3 setattr() 设置属性或方法
setattr(object, name, value) 用于新增加或者修改对象object属性(等同与object.name = value)
class People: country='China' def __init__(self,name): self.name=name p=People('egon') print(p.__dict__) --->{'name': 'egon'} p.sex='male' #新增属性 print(p.sex) --->male print(p.__dict__) --->{'name': 'egon', 'sex': 'male'}
用setattr来实现
class People: country='China' def __init__(self,name): self.name=name p=People('egon') print(p.__dict__) --->{'name': 'egon'} setattr(p,'age',18) #新增p的属性,p.age=18 print(p.__dict__) --->{'name': 'egon', 'age': 18} print(p.age) --->18 #用getattr来实现 print(getattr(p,'age')) --->18
2.4 delattr() 删除属性或方法
delattr(x, y) #删除对象属性(等同于del x.y)
class People: country='China' def __init__(self,name): self.name=name def walk(self): print('%s is walking' %self.name) p=People('egon') print(p.__dict__) --->{'name': 'egon'} del p.name print(p.__dict__) --->{} #用delattar print(p.__dict__) --->{'name': 'egon'} delattr(p,'name') print(p.__dict__) --->{}
以上反射的四种方法应用
四个方法的使用演示 class BlackMedium: feature='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_house(self): print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name) def rent_house(self): print('%s 黑中介租房子啦,傻逼才租呢' %self.name) b1=BlackMedium('万成置地','回龙观天露园') #检测是否含有某属性 print(hasattr(b1,'name')) print(hasattr(b1,'sell_house')) #获取属性 n=getattr(b1,'name') print(n) func=getattr(b1,'rent_house') func() # getattr(b1,'aaaaaaaa') #报错 print(getattr(b1,'aaaaaaaa','不存在啊')) #设置属性 setattr(b1,'sb',True) setattr(b1,'show_name',lambda self:self.name+'sb') print(b1.__dict__) print(b1.show_name(b1)) #删除属性 delattr(b1,'addr') delattr(b1,'show_name') delattr(b1,'show_name111')#不存在,则报错 print(b1.__dict__)
类也是对象,使用反射
class Foo(object): staticField = "old boy" def __init__(self): self.name = 'wupeiqi' def func(self): return 'func' @staticmethod def bar(): return 'bar' print getattr(Foo, 'staticField') #获取类的'staticField'属性 --->old boy print getattr(Foo, 'func') #获取类的'func'属性 ---><function Foo.func at 0x0000018156FBB950> print getattr(Foo, 'bar') #获取类的'bar'属性 ---><function Foo.bar at 0x00000192D2AFB9D8>
class Foo: x=1 def __init__(self,name): self.name=name def walk(self): print('walking......') print(Foo.__dict__) --->{'__module__': '__main__', 'x': 1, '__init__': <function Foo.__init__ at 0x0000000001E7F048>, 'walk': <function Foo.walk at 0x0000000001E7F2F0>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None} print('x' in Foo.__dict__) --->True print(hasattr(Foo,'x')) #等同于print('x' in Foo.__dict__) --->True print(Foo.__dict__['x'] ) --->1 print(getattr(Foo,'x')) #等同于print(Foo.x) --->1 print(Foo.x) --->1
反射当前模块成员
#反射当前模块的属性 import sys x=1111 class Foo: pass def s1(): print('s1') def s2(): print('s2') print(__name__) --->__main__ this_module = sys.modules[__name__] print(this_module) #this_module = sys.modules[__name__]以上是被调用的模块内容,输出的结果是被调用模块的文件路径 ---> <module '__main__' from 'E:/python教学视频/Python fullstack s4 基础篇-day31/day31/test.py'> print(hasattr(this_module, 's1')) --->True print(getattr(this_module, 's2')) #等同于print(this_module.s2) ---><function s2 at 0x00000000027EF048> print(this_module.s2) ---><function s2 at 0x0000000001E73A60> print(this_module.s1) ---><function s1 at 0x0000000001D28048>
反射的应用
#普通情况下 def add(): print('add') def change(): print('change') def search(): print('search') def delete(): print('delete') func_dic={ 'add':add, 'change':change, 'search':search, 'delete':delete } while True: cmd=input('>>:').strip() if not cmd:continue if cmd in func_dic: #hasattr() func=func_dic.get(cmd) #func=getattr() func()
#使用反射 import sys def add(): print('add') def change(): print('change') def search(): print('search') def delete(): print('delete') this_module=sys.modules[__name__] while True: cmd=input('>>:').strip() if not cmd:continue if hasattr(this_module,cmd): func=getattr(this_module,cmd) func() #以上减少了代码func_dic字典的定义,直接可以使用函数add,change,search,delete的功能,减少了代码量,更加简单清晰
导入其他模块,利用反射查找该模块是否存在某个方法
导入test这个模块,在test1.py中查找test是否存在某些方法
1 #反射当前模块的属性 2 import sys 3 4 x=1111 5 class Foo: 6 pass 7 def s1(): 8 print('s1') 9 10 def s2(): 11 print('s2') 12 13 print(__name__) 14 --->__main__ 15 16 this_module = sys.modules[__name__] 17 print(this_module) #this_module = sys.modules[__name__]以上是被调用的模块内容,输出的结果是被调用模块的文件路径 18 ---> <module '__main__' from 'E:/python教学视频/Python fullstack s4 基础篇-day31/day31/test.py'> 19 20 print(hasattr(this_module, 's1')) 21 --->True 22 23 print(getattr(this_module, 's2')) #等同于print(this_module.s2) 24 ---><function s2 at 0x00000000027EF048> 25 26 print(this_module.s2) 27 ---><function s2 at 0x0000000001E73A60> 28 29 print(this_module.s1) 30 ---><function s1 at 0x0000000001D28048>
1 import test #调用test模块,test模块中运行的程序也会被运行 2 --->test 3 <module 'test' from 'E:\\python教学视频\\Python fullstack s4 基础篇-day31\\day31\\test.py'> 4 True 5 <function s2 at 0x000000000222F048> 6 <function s2 at 0x000000000222F048> 7 <function s1 at 0x0000000002223B70> 8 9 print(test) #打印test模块的文件路径 10 ---><module 'test' from 'E:\\python教学视频\\Python fullstack s4 基础篇-day31\\day31\\test.py'> 11 12 print(test.x) #查看test模块下的数据属性x 13 --->1111 14 15 print(test.s1) #查看test模块下的函数s1 16 ---><function s1 at 0x0000000001EA3B70> 17 18 print(test.s2) #查看test模块下的函数s2 19 ---><function s2 at 0x000000000283F048> 20 21 test.s1() #调用test模块下的函数s1 22 --->s1
3 反射的好处
一:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反
射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻
辑写好(只定义接口),然后后期再去实现接口的功能。
1 class FtpClient: 2 'ftp客户端,但是还么有实现具体的功能' 3 def __init__(self,addr): 4 print('正在连接服务器[%s]' %addr) 5 self.addr=addr
from module import FtpClient f1=FtpClient('192.168.1.1') if hasattr(f1,'get'): func_get=getattr(f1,'get') func_get() else: print('---->不存在此方法') print('处理其他的逻辑')
二:动态导入模块(基于反射当前模块成员)
通过字符串导入模块
#方法1 m=input("请输入你要导入的模块:") #用户输入要导入的模块名,以time模块为例 m1=__import__(m) #将用户输入的字符串变成模块的名字并导入模块 print(m1) ---><module 'time' (built-in)> print(m1.time()) #当前时间 --->1493023753.0238242
python官方推荐方法二
#方法二 #推荐使用方法 import importlib #导入importlib模块 t=importlib.import_module('time') print(t) ---><module 'time' (built-in)> print(t.time()) #当前时间(距离1970年1月1日的时间) --->1493023753.0238242
三 内置函数__setattr__,__delattr__,__getattr__
__setattr__当对类中的某个属性进行添加、修改、赋值属性会触发该函数
#__setattr__ class Foo: def __init__(self,x): self.name=x def __setattr__(self, key, value): if not isinstance(value,str): raise TypeError('must be str') print('----setattr---key:%s,value:%s' %(key,value)) print(type(key)) print(type(value)) print(self.__dict__) self.__dict__[key]=value print(self.__dict__) f1=Foo('egon') #Foo('egon') 触发__init__下的self.name=egon,self.name被赋值,触发__setattr__的运行 --->----setattr---key:name,value:egon <class 'str'> <class 'str'> {} {'name': 'egon'} #f1.age=18 #报错,因为__setattr__下设置value值必须是str f1.age="18" --->----setattr---key:age,value:18 <class 'str'> <class 'str'> {'name': 'egon'} {'name': 'egon', 'age': '18'} print(f1.name) --->egon print(f1.age) --->18
__delattr__删除类内部属性会触发该函数
class Foo: def __init__(self,x): self.name=x def __delattr__(self, item): print('delattr:%s' %item) print(type(item)) self.__dict__.pop(item) f1=Foo('egon') f1.age=18 #新增age属性 print(f1.__dict__) --->{'name': 'egon', 'age': '18'} del f1.age #删除age属性,自动触发__delattr__的运行 --->delattr:age <class 'str'> print(f1.__dict__) --->{'name': 'egon'}
__getattr__当对象调用的属性不存在时,才会触发,返回None
class Foo: def __init__(self,x): self.name=x def __getattr__(self, item): print('getattr-->%s %s' %(item,type(item))) f=Foo('egon') print(f.name) #name属性存在,所以不触发__getattr__ --->egon print(f.age) #age属性不存在,触发__getattr__的运行 --->getattr-->age <class 'str'> None
三种用法演示
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 #因为被不停的赋值,所以一直在触发__setattr__运行,造成递归 self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print(' from delattr') # del self.item #不停触发__delattr__运行,无限递归 self.__dict__.pop(item) #__setattr__添加/修改属性会触发它的执行 f1=Foo(10) #相当于赋值self.y=10,触发__setattr__运行 ---> from setattr print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 --->{'y': 10} f1.z=3 #新增z属性,触发__setattr__运行 --->from setattr print(f1.__dict__) --->{'y': 10, 'z': 3} f1.__dict__['a']=3 #我们可以直接修改属性字典,来完成添加/修改属性的操作。防止触发__setattr__的运行 #__delattr__删除属性的时候会触发 print(f1.__dict__) --->{'y': 10, 'z': 3, 'a': 3} del f1.a --->from delattr print(f1.__dict__) --->{'y': 10, 'z': 3} #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx --->from getattr:你找的属性不存在
四 二次加工标准类型(包装):定制我们自己的数据类型
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这
就用到了继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)。
#列表list方法的使用 l=list([1,2,3]) l.append(4) l.append('5') print(l) --->[1,2,3,4,"5"]
#使用类继承的方法来实现 class List(list): #继承列表list的方法 pass l1=List([1,2,3]) print(l1) --->[1, 2, 3] l1.append(4) print(l1) --->[1, 2, 3, 4] l1.append('5') print(l1) --->[1, 2, 3, 4, '5']
基于继承的原理,来定制自己的数据类型(继承标注类型)
class List(list): def append(self, p_object): ' 派生自己的append:加上类型检查' print(p_object) if not isinstance(p_object,int): raise TypeError('must be int') # self.append(p_object) #不停的触发append函数运行,无限递归 super().append(p_object) def insert(self, index, p_object): if not isinstance(p_object,int): raise TypeError('must be int') super().insert(index,p_object) l=List([1,2,3]) print(l) --->[1, 2, 3] l.append(4) --->4 print(l) --->[1, 2, 3, 4] # l.append('5') #报错,TypeError: must be int l.insert(0,-1) # l.insert(0,'-1123123213') #报错,TypeError: must be int print(l) --->[-1, 1, 2, 3, 4]
class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid def append(self, p_object): ' 派生自己的append:加上类型检查' print('--->',p_object) if not isinstance(p_object,int): raise TypeError('must be int') #self.append(p_object) #不停的触发append函数运行,无限递归 super().append(p_object) @property def mid(self): '新增自己的属性' index=len(self)//2 return self[index] l=List([1,2,3,4]) print(l) l.append(5) print(l) # l.append('1111111') #报错,必须为int类型 print(l.mid) #其余的方法都继承list的 l.insert(0,-123) print(l) l.clear() print(l)
当继承不能实现函数功能时,如open函数
#open函数的使用 f=open('a.txt','w') print(f) ---><_io.TextIOWrapper name='a.txt' mode='w' encoding='cp936'> f.write('1111111')
基于授权的方式实现定制自己的数据类型
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即
是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法
class Open: def __init__(self,filepath,m='r',encode='utf-8'): self.x=open(filepath,mode=m,encoding=encode) self.filepath=filepath self.mode=m self.encoding=encode def write(self,line): print('f自己的write',line) t=time.strftime('%Y-%m-%d %X') self.x.write('%s %s' %(t,line)) def __getattr__(self, item): print(item,type(item)) return getattr(self.x,item) f=Open('b.txt','w') print(f) ---><__main__.Open object at 0x0000000001E7DB70> f.write('111111\n') --->f自己的write 111111 f=Open('b.txt','r+') print(f.write) ---><bound method Open.write of <__main__.Open object at 0x00000000021CDBE0>> print(f.read) #未以授权的方式实现定制自己的数据类型的,触发__getattr__的使用 --->read <class 'str'> <built-in method read of _io.TextIOWrapper object at 0x0000000001CD9A68>
五 __setitem__,__getitem,__delitem__
#把对象操作属性模拟成字典的格式 class Foo: def __init__(self,name): self.name=name def __setattr__(self, key, value): print('setattr===>') 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): self.__dict__.pop(key) print('delitem===>') def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) #__setitem__ f=Foo('xuyaping') #触发__setattr__的运行 --->setattr===> f.name='xuyaping123' #触发__setattr__的运行 --->setattr===> f['name']='xuyaping12345' #修改属性,触发__setitem__的运行 --->setitem===> f.name='xuyaping' #触发__setattr__的运行 --->setattr===> f['age']=18 #新增属性,触发__setitem__的运行 --->setitem===> #__getitem__ print(f.name) --->xuyaping12345 print(f['name']) #触发__getitem__的运行 --->getitem name xuyaping12345 #__delitem__ print(f.__dict__) --->{'name': 'xuyaping12345', 'age': 18} del f['age'] #删除属性,触发__delitem__的运行 --->delitem===> print(f.__dict__) --->{'name': 'xuyaping12345'} del f.name --->del obj.key时,我执行
六 __slots__
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。
更多的是用来作为一个内存优化工具。
class People: __slots__=["x","y","z"] p=People() print(People.__dict__) #__slots__=["x","y","z"]全部保存到类的名称空间内 --->{'__module__': '__main__', '__slots__': ['x', 'y', 'z'], 'x': <member 'x' of 'People' objects>, 'y': <member 'y' of 'People' objects>, 'z': <member 'z' of 'People' objects>, '__doc__': None} p.x=1 p.y=2 p.z=3 # p.a=10 #会报错,因为__slots__=["x","y","z"]锁定了只能定义数据属性x,y,z print(p.x,p.y,p.z) --->1 2 3 # print(p.__dict__) #报错,因为__slots__=["x","y","z"]将数据属性x,y,z全部保存到类中,所以对象p中就没有名称空间,节省空间 p1=People() p1.x=10 p1.y=20 p1.z=30 print(p1.x,p1.y,p1.z) --->10 20 30 # print(p1.__dict__) #报错,AttributeError: 'People' object has no attribute '__dict__'
七 __next__和__iter__实现迭代器协议
1、什么是迭代器协议
1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)
2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
2、python中强大的for循环机制
for循环的本质:循环所有对象,全都是使用迭代器协议。
(字符串,列表,元组,字典,集合,文件对象)这些都不是可迭代对象,只不过在for循环式,调用了他们内部的__iter__方法,把他们变成了可迭代对象
然后for循环调用可迭代对象的__next__方法去取值,而且for循环会捕捉StopIteration异常,以终止迭代。
迭代器的定义:
1.只要对象本身有__iter__方法,那对象就是可迭代的;
2.调用iter()或者__iter__方法可以将列表、元组等变成迭代器;
3.调用next()或者__next__方法可以将返回迭代器中的每一个元素;如果对象具有__iter__方法,说明该对象是可迭代对象。
定义类时,通过在类中增加__iter__和__next__方法,实例化的对象就是可迭代的
from collections import Iterable,Iterator class Foo: def __init__(self,start): self.start=start def __iter__(self): #__iter__将传入self的值变成迭代器 return self def __next__(self): #__next__按序返回迭代器中的每一个元素 if self.start > 5: #__next__第七次触发__next__时,即self.start=6时停止迭代 raise StopIteration n=self.start #加上n=self.start,可以执行self.start=0的操作 self.start+=1 return n f=Foo(0) print(f.__iter__()) #打印返回值f ---><__main__.Foo object at 0x00000000027ED630> print(f.__next__()) #打印返回值n --->0 print(isinstance(f,Iterable)) --->True print(isinstance(f,Iterator)) --->True print(next(f)) #自动触发__next__运行 --->1 print(next(f)) --->2 print(next(f)) --->3 print(next(f)) --->4 print(next(f)) --->5 # print(next(f)) #运行到self.start > 5 报错
使用next(f)会报错,使用for循环
#使用for循环 class Foo: def __init__(self,start): self.start=start def __iter__(self): return self def __next__(self): if self.start > 5: raise StopIteration n=self.start self.start+=1 return n f=Foo(0) for i in f: print("====>",i) #运行到self.start > 5 时停止 --->====> 0 ====> 1 ====> 2 ====> 3 ====> 4 ====> 5
使用__iter__和__next__实现range()方法:
class my_range: def __init__(self, start, end): self.start = start self.end = end def __iter__(self): return self def __next__(self): if self.start > self.end: raise StopIteration n = self.start self.start += 1 return n for i in my_range(0,3): print(i) --->0 1 2
八 __doc__ 查看类的描述信息
class Foo: '我是描述信息' pass print(Foo.__doc__) --->我是描述信息
__doc__无法继承给子类
class Foo: '我是描述信息' pass class Bar(Foo): pass print(Bar.__doc__) #该属性无法继承给子类
九 __module__和__class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
class C: def __init__(self): self.name = ‘xuyaping'
from lib.aa import C obj = C() print(obj.__module__) # 输出a,即:输出模块 print(obj.__class__) # 输出a.C,即:输出类
class Bar(Foo): pass b=Bar() print(b.__module__) --->__main__ print(Foo.__module__) --->__main__ print(b.__class__) ---><class '__main__.Bar'> print(Foo.__class__) ---><class 'type'>
十 __del__析构方法
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数
的调用是由解释器在进行垃圾回收时自动触发执行的。
import time class Open: def __init__(self,filepath,mode="r",encode="utf8"): self.f=open(filepath,mode=mode,encoding=encode) def write(self): pass def __getattr__(self, item): return getattr(self.f,item) def __del__(self): print("---->del") self.f.close() f=Open("a.txt","w") del f #del删除后立刻运行__del__的运行,关闭文件,然后进行程序后面的操作。 # del注释掉后,没有这部操作的话,程序所有内容执行完毕才能触发__del__的运行,每隔一段时间清除下内存,f 才被清除 time.sleep(2) print("====>") --->---->del ====>
import time class Open: def __init__(self,filepath,mode="r",encode="utf8"): self.f=open(filepath,mode=mode,encoding=encode) def write(self): pass def __getattr__(self, item): return getattr(self.f,item) def __del__(self): print("---->del") self.f.close() f=Open("a.txt","w") #del f #del删除后立刻运行__del__的运行,关闭文件,然后进行程序后面的操作。 # del注释掉后,没有这部操作的话,程序所有内容执行完毕才能触发__del__的运行,每隔一段时间清除下内存,f 才被清除 time.sleep(2) print("====>") --->====> ---->del
十一 __enter__和__exit__上下文管理协议
我们知道在操作文件对象的时候可以这么写
with open('a.txt') as f: '代码块'
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。
上下文管理协议
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') # return self def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') with Open('a.txt') as f: print('=====>执行代码块') # print(f,f.name)
__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行。
exc_type如果抛出异常,这里获取异常的类型
exc_val如果抛出异常,这里显示异常内容
exc_tb如果抛出异常,这里显示所在位置
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) #异常类型 print(exc_val) #异常值 print(exc_tb) #追溯信息 with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('***着火啦,救火啊***') print('0'*100) #不会执行
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) return True with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('***着火啦,救火啊***') print('0'*100) #会执行
用途或者说好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
# 没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都会执行 class Foo: def __enter__(self): print('=======================》enter') return 123#返回值给下面with的文件句柄f def __exit__(self, exc_type, exc_val, exc_tb):#下面with里面的代码执行完毕,则执行这个 print('exit') print('exc_type', exc_type) print('exc_val',exc_val) print('exc_tb',exc_tb) with Foo() as f: #with Foo()是会触发def __enter__(self)这个函数的运行,并将def __enter__(self)函数的返回值拿回来赋值给f print('with foo的自代码块',f) print("aaaaaaaaaa") print("bbbbbbbbb") #执行结果 =======================》enter with foo的自代码块 123 aaaaaaaaaa bbbbbbbbb exit exc_type None exc_val None exc_tb None
class Foo: def __enter__(self): print('=======================》enter') return 123 # 返回值给下面with的文件句柄f def __exit__(self, exc_type, exc_val, exc_tb):#下面with里面的代码执行完毕,则执行这个 print('exit') print('exc_type',exc_type) print('exc_val',exc_val) print('exc_tb',exc_tb) return True#加上这个的话,则运行完毕不会报错,并且可以执行下面的代码 with Foo() as f: #with Foo()是会触发def __enter__(self)这个函数的运行,并将def __enter__(self)函数的返回值拿回来赋值给f print('with foo的自代码块',f) print("aaaaaaaaaa") raise NameError('主动抛出异常')#加上这个的话,才会去触发上面的def __exit__(self, exc_type, exc_val, exc_tb)的运行,并且with里面的代码和后面的代码都不会运行,直接结束了 print('22222222222') print('1111111111111') #执行结果: =======================》enter with foo的自代码块 123 aaaaaaaaaa exit exc_type <class 'nameerror'=""> exc_val 主动抛出异常 exc_tb <traceback object="" at="" 0x000000000282bec8=""> 1111111111111
总结:
1.上下文管理协议:就是在文件操作经常用到with open() as f中的with语句
2.如果对象要兼容with语句,需要在对象的类中声明__enter__和__exit__方法
3.其中,在实例化对象前出现with时,对象的__enter__方法被调用,并将返回值赋值给as后的变量(文件句柄)
4.其中,with中的代码执行完毕或者代码抛异常时执行__exit__方法
5.__exit__中的三个参数分别代表异常类型、异常值和追述信息,当with代码块缩进的代码出现问题,抛出异常,则with代码块中后面的代码无法被执行
6.__exit__中的参数可以设置抛出异常内容,并返回True,相当于自行处理异常内容,不影响with代码中后面的代码执行
十二 __call__
使用__call__,对象加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__ --->__call__
使用__call__,对象变成可被调用的
class People: def __init__(self,name): self.name=name p=People("xuyuanyuan") print(callable(People)) #判断People是否是可调用对象 --->True print(callable(p)) #判断实例化对象是否是可调用对象 --->False People("tom") p() #报错
class People: def __init__(self,name): self.name=name def __call__(self, *args, **kwargs): #call方法,使对象可以加上括号运行 print("call") p=People("xuyuanyuan") print(callable(People)) #判断People是否是可调用对象 --->True print(callable(p)) #判断实例化对象是否是可调用对象 --->True People("tom") p() #call方法,使对象p可以加上括号触发_call_的运行 --->call
十三 元类
类是元类的对象,可以利用元类来控制类的行为
#type称为元类,是所有类的类,利用type模拟class关键字的创建过程 def run(self): pass class_name = 'Spam' class_base = (object,) class_dict = { 'x': 1, 'run':run } bar = type(class_name,class_base,class_dict) print(bar) print(type(bar)) print(bar.__dict__) #class封装了以上类的创建过程 class spam: pass print(spam) print(type(spam)) print(spam.__dict__) #利用type创建一个空类cls赋值给对象aaa aaa = type('cls',(),{}) print(aaa) print(type(aaa)) print(aaa.__dict__)
1、引子
class Foo: pass f1=Foo() #f1是通过Foo类实例化的对象
Python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)
以上可以看出f1是由Foo类产生的对象,而Foo本身也是对象。
type函数可以查看类型,也可以用来查看对象的类,二者是一样的
print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print(type(Foo)) # 输出:<type 'type'>
2、什么是元类?
元类是类的类,可以控制类的行为,是类的模板,正如类是创建对象的模板。
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是type类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象。
3、创建类的两种方式
方式一:
class Foo: def func(self): print('from func')
方式二:
def func(self): print('from func') x=1 Foo=type('Foo',(object,),{'func':func,'x':1})
4、一个类没有声明自己的元类,默认它的元类就是type,除了使用元类type, 用户也可以通过继承type来自定义元类(顺便也可以了解下元类是如何控制类的创建,工作流程是什么)