反射
反射:主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)
python面向对象中的反射:通过字符串的形式操作对象相关的属性
#四个可实现自省的函数
#下列方法适用于类与对象:
class BlackMedium:
feature = 'Ugly'
def __init__(self,name,addr):
self.name = name
self.addr = addr
def sell_hosue(self):
print('【%s】正在卖房子' % self.name)
def rent_house(self):
print('【%s】正在租房子' % self.name)
b1 = BlackMedium('北京中介','北京')
print(b1.__dict__) #{'name': '北京中介', 'addr': '北京'}
#b1.name -->>b1.__dict__['name']
#-->>hasattr(object,name)
#判断object中有没有一个name字符串对应的方法和属性
print(hasattr(b1,'name')) #True
print(hasattr(b1,'sell_hosue')) #True
#-->>getattr(object,name,default=None)
#获得一个name字符串对应的方法和属性,没有则报错
print(getattr(b1,'name')) #北京中介
print(getattr(b1,'rent_house')) #<bound method BlackMedium.rent_house of <__main__.BlackMedium object at 0x0000016000194438>>
func = getattr(b1,'rent_house') #得到一个函数内存地址
func() #北京中介】正在租房子
#print(getattr(b1,'dadafaf')) #没有则报错
print(getattr(b1,'dadafaf','没有这个属性')) #给Default取值,默认参数:没有这个属性
#-->>setattr(x,y,v)
#设置属性和方法
setattr(b1,'sb',True) #相当于b1.sb = True
print(b1.__dict__) #增加:{'name': '北京中介', 'addr': '北京', 'sb': True}
setattr(b1,'name','上海中介')
print(b1.__dict__) #修改:{'name': '上海中介', 'addr': '北京', 'sb': True}
setattr(b1,'func',lambda self:self.name + 'heheda')
print(b1.__dict__) #{'name': '上海中介', 'addr': '北京', 'sb': True, 'func': <function <lambda> at 0x000002C0029A1E18>}
print(b1.func) #<function <lambda> at 0x000001B0403C1E18>
print(b1.func(b1)) #上海中介heheda
#-->>delattr(x,y)
#删除
del b1.sb
print(b1.__dict__) #{'name': '上海中介', 'addr': '北京'}
delattr(b1,'addr')
print(b1.__dict__) #{'name': '上海中介'}
#--->>> 实现反射的四个函数也适用于类
class BlackMedium:
feature = 'Ugly'
def __init__(self,name,addr):
self.name = name
self.addr = addr
def sell_hosue(self):
print('【%s】正在卖房子' % self.name)
def rent_house(self):
print('【%s】正在租房子' % self.name)
#b1 = BlackMedium('北京中介','北京')
print(hasattr(BlackMedium,'feature'))
print(getattr(BlackMedium,'sell_house'))
#扩展:模块之间的使用
#模块间的使用-->>导入其他模块,利用反射查找该模块是否存在某个方法
import test as t
if hasattr(t,'cal'):
func = getattr(t,'cal')
func()
else:
print('找不到哦')
#模块间的使用-->>访问自身
x = 111
y = 222
#怎么访问到自身的内容?
import sys
obj = sys.modules[__name__] #得到当前文件的模块对象
print('-->>',hasattr(obj,'x')) #-->> True
使用反射的好处:
① 可以事先定义好接口,接口只有在被完成后才会真正执行,这达到了即插即用
② 可以事先把主要的逻辑写好(只定义接口),然后后面再去实现接口的功能
class FtpClient:
'ftp客户端,但是还么有实现具体的功能'
def __init__(self,addr):
print('正在连接服务器[%s]' %addr)
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('处理其他的逻辑')
动态导入模块(基于反射当前模块成员)
'''
import_lib是文件夹,里面有metaclass.py文件
import_lib与test.py处于同一个目录下
目前的文件为test.py
问题:在test.py调用metaclass.py文件
'''
import importlib
#__import__("import_lib.metaclass") ->python解释器内部用的
importlib.import_module("import_lib.metaclass") #与上面效果一样
内置的attr系列属性
①__getattr__(self,item)
class Foo:
x = 1
def __init__(self,y):
self.y = y
def __getattr__(self,item):
print('执行__getattr__')
f1 = Foo(10)
print(f1.y) #10
print(getattr(f1,'y')) #1 #len(str)-->>str.__len__()
f1.sb #属性不存在时,执行__getattr__方法
①__setattr__(self,key,value)
class Foo:
x = 1
def __init__(self,y):
self.y = y
def __setattr__(self,key,value):
#self.key = value 只要是设置属性都会无限触发__setattr__方法,一直递归下去
print('执行__setattr__')
self.__dict__[key] = value
f1 = Foo(10) #执行__setattr__ 设置时会触发
print(f1.__dict__) #{'y': 10}
f1.z = 2 #执行__setattr__ 设置时会触发
print(f1.__dict__) #{'y': 10, 'z': 2}
①__delattr__(self,item)
class Foo:
x = 1
def __init__(self,y):
self.y = y
def __delattr__(self,item):
print('执行__delattr__')
f1 = Foo(10)
del f1.y #执行__delattr__ 删除时触发属性
del f1.x #执行__delattr__ 删除时触发属性
作用:可以对默认的getattr,setattr,delattr进行重写,完成我们需要的操作
class Foo:
def __init__(self,name):
self.name = name
def __getattr__(self,item):
print('你找的属性【%s】不存在' % item)
def __setattr__(self,k,v): #设置属性都会触发这个方法
print('执行__setattr__',k,v)
if type(v) is str:
print('开始设置')
#self.k = v #会一直触发__setattr__,递归下去
self.__dict__[k] = v
else:
print('必须是字符串类型')
def __delattr__(self,item):
#del self.item #删除操作会一直触发__delattr__方法,递归下去
self.__dict__.pop(item)
print('执行__delattr__',item)
f1 = Foo('laowang') #实例化的过程就是设置属性,执行setattr方法:将self.name = name k就是name v就是传入的值
#f1.age = 18 #当setattr方法默认的时候,会将属性传入属性字典中,若重写此方法,则执行重写后的setattr方法,不会将属性自动传入属性字典
f1.gender = 'male' #self.__dict__未增加时,触发setattr方法会发现字典时是空的
#print(f1.__dict__) #{}
#print(f1.name) #laowang
#print(f1.age) #你找的属性【age】不存在,不存在时,触发__getattr__方法
print(f1.__dict__) #{'name': 'laowang', 'gender': 'male'}
del f1.name #删除会触发delattr方法:#执行__delattr__ name
print(f1.__dict__) #{'gender': 'male'}
包装
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就需要用到继承/派生。
class List(list):
def append(self,item):
if type(item) is str:
#self.append(item) #会一直递归下去
#list.append(self,item)
super().append(item)
else:
print('只能添加字符串类型')
@property
def show_middle(self):
mid_index = int(len(self)/2)
return self[mid_index]
l1 = List('helloworld')
print(l1,type(l1)) #['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'] <class '__main__.List'>
l2 = List('hellogirls')
print(l2,type(l2)) #['h', 'e', 'l', 'l', 'o', 'g', 'i', 'r', 'l', 's'] <class '__main__.List'>
l1.append(123) #只能添加字符串类型
print(l1) #['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
l2.append('123')
print(l2) #['h', 'e', 'l', 'l', 'o', 'g', 'i', 'r', 'l', 's', '123']
print(l1.show_middle) #w
#####clear加权限限制
class List(list):
def __init__(self,item,tag=False):
super().__init__(item)
self.tag = tag
def append(self,p_object):
if not isinstance(p_object,str):
raise TypeError
super().append(p_object)
def clear(self):
if not self.tag:
raise PermissionError
super().clear()
l = List([1,2,3],False)
print(l) #[1, 2, 3]
print(l.tag) #False
l.append('hehe')
print(l) #[1, 2, 3, 'hehe']
#l.clear() #没有权限,报异常
l.tag = True
l.clear()
授权
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能,其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性
#实现授权的关键点就是覆盖__getattr__方法
import time
class Open:
def __init__(self,filename,mode='r',encoding='utf-8'):
self.filename = filename
self.mode = mode
self.encoding = encoding
self.file = open(filename,mode,encoding=encoding)
def write(self,line):
t = time.strftime('%Y-%m-%d %X')
self.file.write('%s %s' % (t,line))
#只用__getattr__方法就可以调用到文件内部的方法
def __getattr__(self,item):
print(item,type(item))
#self.file.read
return getattr(self.file,item)
f1 = Open('a.txt','r+')
print(f1.file) #<_io.TextIOWrapper name='a.txt' mode='r+' encoding='utf-8'>
f1.read #read不存在,触发的是__getattr__: read <class 'str'>
print(f1.read) #read <class 'str'> #<built-in method read of _io.TextIOWrapper object at 0x000001B071DC7B40>
print(f1.write) #<bound method Open.write of <__main__.Open object at 0x000002A17D524358>>
f1.write('呵呵哒\n')
f1.seek(0) #seek <class 'str'>
print(f1.read()) #read <class 'str'> #2018-08-10 20:39:18 呵呵哒
sys_f = open('b.txt','w+')
print('-->>',getattr(sys_f,'read')) #<built-in method read of _io.TextIOWrapper object at 0x000001B79BF763A8>
##########增加b模式筛选
import time
class Open:
def __init__(self,filename,mode='r',encoding='utf-8'):
if 'b' in mode:
self.file = open(filename,mode)
else:
self.file = open(filename,mode,encoding=encoding)
self.filename = filename
self.mode = mode
self.encoding = encoding
def write(self,line):
if 'b' in self.mode:
if not isinstance(line,bytes):
raise TypeError('必须是字节格式')
self.file.write(line)
#只用__getattr__方法就可以调用到文件内部的方法
def __getattr__(self,item):
#print(item,type(item))
#self.file.read
return getattr(self.file,item)
def __str__(self):
if 'b' in self.mode:
res="<_io.BufferedReader name='%s'>" % self.filename
else:
res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" % (self.filename,self.mode,self.encoding)
return res
f1 = Open('a.txt','wb')
#f1.write('你好啊') #不是字节,报错
f1.write('你好啊'.encode('utf-8'))
print(f1) #<_io.BufferedReader name='a.txt'>
f1.close()
#-->>练习1:
class List:
def __init__(self,seq):
self.seq = seq
def append(self,p_object):
'派生自己的append加上类型检查,覆盖原有的append'
if not isinstance(p_object,int):
raise TypeError('必须是整形')
self.seq.append(p_object)
@property
def mid(self):
'新增取中间的元素'
index = int(len(self.seq)/2)
return self.seq[index]
def __getattr__(self,item):
print('被触发了')
return getattr(self.seq,item)
def __str__(self):
return str(self.seq)
l = List([1,2,3])
print(l) #[1, 2, 3]
print(type(l)) #<class '__main__.List'>
l.append(4)
print(l) #[1, 2, 3, 4]
#l.append('3') #TypeError: 必须是整形
print(l.mid) #3
#基于授权,获得insert方法
l.insert(0,8)
print(l) #被触发了 #[8, 1, 2, 3, 4]
#-->>练习2
class List:
def __init__(self,seq,permission=False):
self.seq = seq
self.permission = permission
def clear(self):
if not self.permission:
raise PermissionError('不允许哦')
self.seq.clear()
def __getattr__(self,item):
print('我又被触发了')
return getattr(self.seq,item)
def __str__(self):
return str(self.seq)
l = List([1,2,3])
#l.clear() #PermissionError: 不允许哦 ->>没有权限,报异常
l.permission = True
print(l) #[1, 2, 3]
l.clear()
print(l) #[]
#基于授权,获得insert方法
l.insert(0,8)
print(l) #我又被触发了 #[8]
isinstance(obj,cls)和issubclass(sub,super)
#-->>isinstance(obj,cls)-->>判断obj是否是cls的实例化对象
class Foo:
pass
f1 = Foo()
print(isinstance(f1,Foo)) #True
#-->> issubclass(sub,super)-->>判断sub类是否是super类的子类
class Bar(Foo):
pass
b1 = Bar()
print(isinstance(b1,Bar)) #True
print(isinstance(b1,Foo)) #与基类也符合-->>True
print(issubclass(Bar,Foo)) #True
print(type(b1)) #<class '__main__.Bar'>
__getattribute__
#__getattr__的使用
class Foo:
def __init__(self,x):
self.x = x
def __getattr__(self,item):
print('我被触发了')
#return self.__dict__[item]
f1 = Foo(10)
print(f1.x) #存在的属性,所以不触发__getattr__:#10
f1.xxxx #不存在的属性访问,触发__getattr__
#__getattribute__的使用
class Foo:
def __init__(self,x):
self.x = x
def __getattr__(self,item):
print('我被触发了')
#return self.__dict__[item]
def __getattribute__(self,item): #默认首先执行此方法,不管属性存不存在
print('执行的是getattribute')
f1 = Foo(10)
f1.x #触发__getattribute__
f1.xxxx ##触发__getattribute__
#两个方法一起使用
class Foo:
def __init__(self,x):
self.x=x
def __getattr__(self, item):
print('执行的是getattr')
# return self.__dict__[item]
def __getattribute__(self,item): #默认首先执行此方法,不管属性存不存在,都执行它,不会执行getattr
print('执行的是getattribute')
raise AttributeError('抛出异常- -') #此方法在默认的getattribute中也存在,属性存在抛出异常,转而执行getattr
f1 = Foo(10)
#f1.x
f1.xxxxx #打印:执行的是getattribute 执行的是getattr
item系列
class Foo:
def __getitem__(self,item):
print('getitem')
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()
print(f1.__dict__)
#f1.name = 'laowang' #.形式触发的是attr系列的方法
#print(f1.__dict__) #{'name': 'laowang'}
f1['name'] = 'laowang' ##触发__setitem__方法:#setitem
f1['age'] = 18 #触发__setitem__方法:#setitem
print(f1.__dict__) #{'name': 'laowang', 'age': 18}
#del f1.name #.形式触发的是attr系列的方法
#print(f1.__dict__)
print(f1['name']) #触发__getitem__方法:#getitem laowang
del f1['name'] ##触发__delitem__方法:#delitem
print(f1.__dict__) #{'age': 18}
改变字符串显示 __str__ __repr__
#######__str__
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('wang',18)
print(f1) #实际上:str(f1) -->> f1.__str__() 打印,相当于默认的str方法处理得到的,因此可以对str方法重写
#重写str方法前打印:<__main__.Foo object at 0x00000240695372B0> 重写后打印:名字wang 年龄是18
#相当于
x = str(f1)
print(x) #名字wang 年龄是18
#相当于
y = f1.__str__()
print(y) #名字wang 年龄是18
#######__repr__ #repr(f1)相当于f1.__repr__()
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self): #若__str__存在,print()则执行__str__,若不存在,则选择执行__repr__方法
return '这是str' #只能return字符串类型的值
def __repr__(self):
return '名字%s 年龄是%s' % (self.name,self.age)
f1 = Foo('zhao',25)
#repr(f1) -->>f1.__repr__() #交互式解释器会执行repr方法
#print()执行的是__str__,如果找不到str则会去调用repr方法
print(f1) #print()执行的是__str__,如果找不到str则会去调用repr方法
#整个流程是这样的 #str(f1) -->>f1.__str__() -->>f1.__repr__()
自定制格式化字符串__format__
#--->>>正常格式化字符串
class Date:
def __init__(self,year,mon,day):
self.year = year
self.mon = mon
self.day = day
d1 = Date(2018,7,28)
x = '{0.year}{0.mon}{0.day}'.format(d1)
y = '{0.year}:{0.mon}:{0.day}'.format(d1)
z = '{0.year}-{0.mon}-{0.day}'.format(d1)
#打印不同格式的日期显示方式
print(x) #2018728
print(y) #2018:7:28
print(z) #2018-7-28
#多种自定义格式
format_dic = {
'ymd':'{0.year}{0.mon}{0.day}',
'm-d-y':'{0.mon}-{0.day}-{0.year}',
'y:m:d':'{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('执行了')
#print('-->>','format_spec')
if not format_spec or format_spec not in format_dic:#添加:若格式为空或不存在的时候,可以增加一个默认格式
format_spec = 'ymd'
fm = format_dic[format_spec]
return fm.format(self)
d1 = Date(2018,7,28)
format(d1) #执行d1.__format__()
print(format(d1)) #2018728
print(format(d1,'y:m:d')) #2018:7:28
print(format(d1,'m-d-y')) #7-28-2018
print(format(d1,'adada')) #2018728 默认格式
slots属性
'''
1.__slots__是什么:是一个类变量,变量值可以是列表,元组,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
4.当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
5.使用__slots__时,一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名
6.关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性,更多的是用来作为一个内存优化工具
'''
class Foo:
#__slots__ = 'name' #{'name':None}
__slots__ = ['name','age'] #{'name':None,'age':None}
f1 = Foo()
#f1.name = 'laowang'
#print(f1.name)
#f1.age = 18 相当于setattr-->>f1.__dict__['age'] = 18
#print(f1.__dict__)
print(Foo.__slots__) #['name', 'age']
print(f1.__slots__) #['name', 'age'] 实际就是调用了类的属性
#说明了由这个类产生的实例对象不再有__dict__属性字典
#问题:我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名
f1.name = 'laowang'
f1.age = 18
print(f1.name) #laowang
print(f1.age) #18
#f1.gender = 'male' #报错,类Foo的__slots__里面没有这个属性名,不能新增或者访问不存在的属性
f2 = Foo()
print(f2.__slots__) #['name', 'age']
f2.name = 'zhao'
f2.age = 25
print(f2.name) #zhao
print(f2.age) #25
__doc__ :文档属性
class Foo:
'我是帅哥'
pass
print(Foo.__doc__) #我是帅哥
#注意:文档属性无法被继承
class Foo:
'我是帅哥'
pass
class Bar(Foo):
pass
print(Foo.__dict__) #'__doc__': '我是帅哥'
print(Bar.__dict__) #'__doc__': None
print(Bar.__doc__) #该属性无法被继承:原因在于每一个类都有文档属性,就算不写也有默认的文档属性None
__module__ 和 __class__
#__module__ 表示当前操作的对象所处的模块
#__class__表示当前操作的对象的类是什么
class C:
def __init__(self):
self.name = 'sb'
c1 = C()
print(c1.name) #sb
print(c1.__module__) #__main__
print(c1.__class__) #<class '__main__.C'>
#当然也可以在其他文件调用类,然后查看类所属模块以及类名
__del__ 析构方法
析构方法:当对象在内存中被释放时,自动触发执行
注:
创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中
当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源
class Foo:
def __init__(self,name):
self.name = name
def __del__(self):
print('执行了')
f1 = Foo('alex')
#del f1 #执行了
print('-->>') #若没有删除对象,程序走完后会自动进行垃圾回收机制,同样会执行__del__
__call__
对象昂后面加括号,触发执行
注:
构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __call__(self,*args,**kwargs):
print('实例执行了')
f1 = Foo()
f1() #实例执行了
__next__和__iter__实现迭代器协议
class Foo:
def __init__(self,n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n == 15:
raise StopIteration('终止吧') #定制触发异常
self.n += 1
return self.n
#l = list('hello')
#for i in l:
#print(i)
f1 = Foo(10)
print(f1.__iter__()) #<__main__.Foo object at 0x000002659F3372B0>
print(f1.__next__()) #11
print(f1.__next__())
print(f1.__next__())
print(next(f1))
print(next(f1))
print(next(f1))
########模拟Range,加上步长
class Range:
def __init__(self,n,stop,step):
self.n = n
self.stop = stop
self.step = step
def __iter__(self):
return self
def __next__(self):
if self.n >= self.stop:
raise StopIteration
self.n += self.step
return self.n
r = Range(0,15,2)
for i in r:
print(i)
#############实现斐波那契数列
class Fib:
def __init__(self):
self._a = 0
self._b = 1
def __iter__(self):
return self
def __next__(self):
if self._a > 50:
raise StopIteration('终止吧')
self._a,self._b =self._b,self._a + self._b
return self._a
f1 = Fib()
__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(f) #<__main__.Open object at 0x0000016215684080>
print(f.name) #a.txt
print('-->>')
print('-->>')
print('-->>')
#with里面的代码块执行完后,执行__exit__方法:#with中代码块执行完毕时执行我啊
print('123456789') #最后执行
#######__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')
####下面是异常的三个部分
print(exc_type) #对应异常类 :#<class 'NameError'>
print(exc_val) #对应异常值:#name 'dada' is not defined
print(exc_tb) #对应异常Traceback:#<traceback object at 0x000001EFD5503188>
return True #若__exit()返回值为True,那么异常会被清空,因此不会报异常,with内的代码块同样也不会继续执行,with下面的正常执行
with Open('a.txt') as f: #执行enter
print(f) #<__main__.Open object at 0x000001A13D877748>
print(f.data) #这个变量不存在,则在这个位置报错,但是会触发__exit__方法
print(f.name)
print('操作文件也这么逗嘛') #return True后异常被清空,with内代码块不会执行,with下面代码会执行
执行代码块的两种情况
1.在没有异常的情况下,整个代码块运行完毕后去触发__exit__方法,它的三个参数都为None
2.在有异常的情况下,从异常出现的位置直接触发__exit__
->a:如果__exit__的返回值为True,代表异常清空
->b:如果__exit__的返回值不为True,代表继续触发异常
->c:__exit__的运行完毕就代表了整个with语句的执行完毕
#######模拟open
class Open:
def __init__(self,filepath,mode='r',encoding='utf-8'):
self.filepath = filepath
self.mode = mode
self.encoding = encoding
self.f = open(self.filepath,mode=self.mode,encoding=self.encoding)
def __enter__(self):
return self.f
def __exit__(self,exc_type,exc_val,exc_tb):
self.f.close()
return True
def __getattr__(self,item):
print('又来实现了')
return getattr(self.f,item)
with Open('a.txt','w') as f:
print(f) #<_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'>
f.write('abc')
#f.wasssa #抛出异常,交给__exit__处理
print('执行完了哦')
用途:使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预