1.1isinstance和issubclass的方法
isinstance(obj,cls)检查对象(obj)是否是类(类的对象)
class Foo(object):
pass
obj = Foo()
print(isinstance(obj, Foo)) #True
print(isinstance(str,Foo)) #false
issubclass(sub, super)检查字(sub)类是否是父( super) 类的派生类
class Foo(object): #父类
pass
class Bar(Foo): #父类的派生类
pass
print(issubclass(Bar, Foo)) #True
#所有的父类都是object派生
print(issubclass(str,object)) #True
print(issubclass(Foo,object)) #True
print(issubclass(str,Foo)) #False
1.2反射
什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
python中面向对象中的反射:通过字符串的形式操作对象的相关属性。python中的一切事物都是对象(都可以使用反射)
可以实现自省的函数
hasattr,getattr,setattr,delattr
下列方法使用与类和对象(类本身也是一个对象)
1.hasattr(object,name) 判断object中有没有一个name字符串对应的方法或者属性
print(hasattr(str,"count")) #True str对象中有count的方法
print(hasattr(str,"sb")) #False
2.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
#得到该name属性在object中的值,不存在则报错
print(getattr(list,"index"))
print(getattr(list,"big sb")) #报错
3setattr(x,y,z)
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
setattr(x, y, v)
print(setattr(dict,"get","super sb")) #报错 #注:这是内置的对象,该属性的是只读的(可以自定义类来修改)
#设置对象中的 "get"属性改为 "super sb"
4delattr(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
#删除对象中的属性
上面函数的代码示例:
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,'name')
delattr(b1,'show_name111')#不存在,则报错
# print(b1.__dict__)
# 四个方法的使用演示
#注:用完一个属性,注释一个
为什么用反射之反射的好处
可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。
1.3__setattr__,__delattr__,__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
# 三者的用法演示
1.4__getattribute__
__getattr__和__getattribute__
class Foo:
# def __setattr__(self, key, value):
# print("setaaaa")
# self.__dict__[key] = value
def __getattr__(self, item): #可以把__getattr__和__getattribute__分别注释看看
print("执行getattr")
def __getattribute__(self, item):
print("执行attribute") #无论有没有该属性,都执行
raise AttributeError("这是错误异常") #遇到异常 ,去执行__getattr__
#raise 错误属性 ("抛出异常的错误内容")
aa = Foo()
aa.ssssss #不存在该属性会执行__getattribute__xxxxxxxxxx
#当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
1.5描述符(__get__,__set__,__delete__)
1.描述符是什么:描述符本质就是一个 新式类,至少出现了__get__(),__set__(),__delete__() 中的一个,这也就称为描述符协议
这也被称为描述符协议:
__get__()调用一个属性时,触发
__set__()为一个属性赋值是,触发
__delete__()采用del删除属性时,触发
class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
2描述符是做什么的:描述符的作用就是来代理另外一个类的属性的(必须把描述符定义成这个类属性),不得定义到构造函数(__init__(self))中。
何时何地会触发描述符:请看下面代码
class Foo: #这是一个描述符
def __get__(self, instance, owner):
print("执行get") #只是定义一个打印
def __set__(self, instance, value):
print("执行set方法")
def __delete__(self, instance):
print("执行delete方法")
#包含这三个方法的新式类称为描述符, 由这个类产生的实例进行属性的调用 / 赋值 / 删除, 并不会触发这三个方法
aa = Foo()
aa.__dict__["bb"] = 123
# print(aa.__dict__)
class Bar:
x=Foo() #x被Foo类代理
def __init__(self,n):
self.x = n
#交给代理去另一类属性才会执行描述符的方法
b1 = Bar(10) #触发set
b1.name = "yi" #不会触发 这是在自已对象新增了一个属性
# print(b1.__dict__)
b1.x #触发get
del b1.x #触发delete
3描述符分两种:
一:数据描述符:至少实现了__get__()和__set__()
class Foo:
def __set__(self, instance, value):
pass
def __get__(self, instance, owner):
pass
二.非数据描述符,没有实现__set__()
class Foo:
def __get__(self, instance, owner):
print('get')
4.注意事项
一描述符本身应该定义成新式类,被代理的类也也该是新式类。
二必须把描述符定义成这个类的类属性,不能定义到构造函数中。
三要严格遵循该优先级,优先级由高到低分别是。
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.找不到的属性会触发__getattr__()
1.类属性高于数据描述符
#描述符Str
class Str:
def __get__(self, instance, owner):
print('Str调用')
def __set__(self, instance, value):
print('Str设置...')
def __delete__(self, instance):
print('Str删除...')
class People:
name=Str()
def __init__(self,name,age): #name被Str类代理
self.name=name
self.age=age
#注在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
People.name #调用类属性name,本质就是在调用描述符Str,触发了__get__()
# print(People.__dict__)
People.name = "yj" #这相当于重新修改了People.name的属性,name 就没给Str描述符代理,就不会触发set
# print(People.__dict__)
del People.name #这相当于重新修改了People.name的属性,跟修改一样
注:
1.:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()
eople.name= “yj ”#那赋值,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
总结:类属性高于数据描述符
2数据描述符高于实例属性
#描述符Str
class Str:
def __get__(self, instance, owner):
print('Str调用')
def __set__(self, instance, value):
print('Str设置...')
def __delete__(self, instance):
print('Str删除...')
class People:
name=Str()
def __init__(self,name,age): #name被Str类代理
self.name=name
self.age=age
p1 = People("yj",18) #生成一个实例 #name会触发Str
#如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
p1.name = "19"
print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
数据描述符高于实例属性
3.实例属性高于非数据描述符(没有set)
class Foo:
def __get__(self, instance, owner):
print('get')
class Room:
name=Foo()
def __init__(self,name,width,length):
self.name=name
self.width=width
self.length=length
#name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
#对实例的属性操作,触发的都是实例自己的
r1=Room('大厅',10,10)
r1.name
r1.name='厨房'
#实例属性高于非数据描述符
描述符的使用
从所周值,python是弱类型语言,即参数的复制没有类型的限制,下面我们通过描述符机制来实现类型限制功能,给传入参数加上类型限制的功能。
class Foo:
def __init__(self,name):
self.name = name
def __get__(self, instance, owner):
print("get》》》",instance,owner)
return instance.__dict__[self.name]
def __delete__(self, instance):
print("delte >>>",instance)
instance.__dict__.pop(self.name) #保存修改实例属性的值
def __set__(self, instance, value):
print("set>>>",instance,value)
instance.__dict__[self.name]=value #保存修改实例属性的值
class People:
name = Foo("name")
def __init__(self,name,age,salary):
self.name = name
self.age = age
self.salary = salary
p1 = People("yj",18,55)
#查看 ————get
p1.name
# print(p1.__dict__)
#赋值 ———set
p1.name = "aa"
# print(p1.__dict__)
#删除————delete
del p1.name
# print(p1.__dict__)
#为了把相应的p1.name 的保存到自已的实例属性的字典里面,通过描述符来实现
现在给name参数做类型,限制,只允许传字符串
class Foo:
def __init__(self,key):
self.key = key
def __get__(self, instance, owner):
print("get》》》",instance,owner)
return instance.__dict__[self.key]
def __delete__(self, instance):
print("delte >>>",instance)
instance.__dict__.pop(self.key) #保存修改实例属性的值
def __set__(self, instance, value):
print("set>>>",instance,value)
if not isinstance(value,str): #如果该值的类型不是字符串则抛出异常
raise TypeError("你传入的类型不是字符串")
instance.__dict__[self.key]=value #保存修改实例属性的值
class People:
name = Foo("name")
def __init__(self,name,age,salary):
self.name = name
self.age = age
self.salary = salary
p = People("aa",12,12)
print(p.__dict__)
# p2 = People(12,2124,45) #报错
上面只是给name做了类型限制,但是现在要age和salary都要做类型限制,就不能像上面这样写了,(上面写死了,必须传入相应的类型,age是int类型,salary是float型)
class Foo:
def __init__(self,key,expected_type):
self.key = key
self.expected_type = expected_type
def __get__(self, instance, owner):
print("get》》》",instance,owner)
if instance is None: #这是类调用name,age 防止报错
return instance
return instance.__dict__[self.key]
def __delete__(self, instance):
print("delte >>>",instance)
instance.__dict__.pop(self.key) #保存修改实例属性的值
def __set__(self, instance, value):
print("set>>>",instance,value)
if not isinstance(value,self.expected_type): #如果该值的类型不是字符串则抛出异常
raise TypeError("%s传入的类型不是%s"%(value,self.expected_type))
instance.__dict__[self.key]=value #保存修改实例属性的值
class People:
name = Foo("name",str) #name属性字符串
age = Foo("age",int) #age属性整型
salary = Foo("salary",float) #salary属性浮点型
def __init__(self,name,age,salary):
self.name = name
self.age = age
self.salary = salary
p = People("aa",12,12.2)
print(p.__dict__)
# p1 = People(12,19,12.2) # 报错 name 不是字符串
# p2 = People("qw","sd",12.2) # 报错age 不是整型
# p3 = People("qw",19,"aaa") # 报错salary 不是浮点型
上面虽然实现了给传入参数,加了类型限制,也实现了相应的功能。
但是问题是,如果我们的类有很多属性,任然采用在定义一堆类属性的方式去实现,实现是太low,这时候,我们可以用到装饰器,给类做装饰
我现在自定义一个类,来给这个类,新增属性。
def test(obj):
obj.x = 1
obj.y = 12
return obj
@test #原理是:Bar=test(Bar) test()运行函数
class Bar:
pass
print(Bar.__dict__)
ok现在给参数做类型限制,用装饰器:
class Typed:
def __init__(self,name,expected_type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance, owner):
print("get>>>")
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
print("set>>>")
if not isinstance(value,self.expected_type):
raise TypeError ("%s该类型不是%s" %(value,self.expected_type))
instance.__dict__[self.name]=value
def __delete__(self, instance):
print("delete>>>")
instance.__dict__.pop(self.name)
def typeset(**kwargs):
def decor(obj):
for key,val in kwargs.items():
setattr(obj,key,Typed(key,val))
return obj
return decor
@typeset(name=str,age=int,salary=float) #1运行typeset(name=str,age=int,salary=float)
# 返回结果是decor一个函数对象
#2 People = decor(People)
class People:
def __init__(self,name,age,salary):
self.name=name
self.age=age
self.salary=salary
print(People.__dict__)
p1 = People("xiaoxi",12,45.4)
print(p1.__dict__)
描述符总结:
描述符是可以实现大部门python类特性中的底层魔@classmethod,@staticmethd,@property甚至是__slots__属性
描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件
现在来做一个自定制@property。
利用描述符原理完成一个自定制@property实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名key,value为描述符类产生的对象)
class Selfproperty:
def __init__(self,func):
self.func = func
def __get__(self, instance, owner):
print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
if instance is None:
return self
# print(self)
return self.func(instance) #func(instance)就是area的返回值
class Room:
def __init__(self,name,width,length):
self.name = name
self.width = width
self.length = length
# @property
@Selfproperty #area=Selfproperty(area)
def area(self):
return self.width * self.length
r1 = Room("hourse",10,20)
print(r1.area)
print(r1.area)
print(r1.area)
现在来实现延迟计算
class Selfproperty:
def __init__(self,func):
self.func = func
def __get__(self, instance, owner):
print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
if instance is None:
return self
else:
value = self.func(instance)
setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
return value
class Room:
def __init__(self,name,width,length):
self.name = name
self.width = width
self.length = length
# @property
@Selfproperty #area=Selfproperty(area)
def area(self):
return self.width * self.length
r1 = Room("hourse",10,20)
print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法
print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算
# 这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
# 200
# 200
#ok 现在就实现了延迟计算,
1.6__setitem__,__getitem,__delitem__
这只是通过字典方式,查看修改,类或者对象的属性
class Foo:
def __init__(self,name):
self.name = name
def __getitem__(self, item):
print("执行getitem")
return self.__dict__[item]
def __setitem__(self, key, value):
print("执行setitem") #为了让效果明显所以用print
self.__dict__[key] = value
def __delitem__(self, key):
print("del obj[key],执行")
self.__dict__.pop(key)
def __delattr__(self, item):
print("del obj.key,执行")
self.__dict__.pop(item)
n = Foo("xiaoxi")
# print(n.__dict__)
# del n.name #执行delattr
# del n["name"] #执行delitem
n["age"] = 18 #执行setitem
print(n["age"]) #执行getitem
总结:
#自省
hasattr(obj,'属性') #obj.属性 是否存在
getattr(obj,'属性') #获取obj.属性 不存在则报错
getattr(obj,'属性','默认值') #获取obj.属性 不存在不会报错,返回那个默认值
setattr(obj,'属性','属性的值') #obj.属性=属性的值
delattr(obj,'属性') #del obj.属性
#__getattr__,__setattr__,__delattr__
obj点的方式去操作属性时触发的方法
__getattr__:obj.属性 不存在时触发
__setattr__:obj.属性=属性的值 时触发
__delattr__:del obj.属性 时触发
#__getitem__,__setitem_,__delitem__
obj[‘属性’]的方式去操作属性时触发的方法
__getitem__:obj['属性'] 时触发
__setitem__:obj['属性']=属性的值 时触发
__delitem__:del obj['属性'] 时触发
#__get__,__set__,__delete__
描述就是一个新式类,这个类至少要实现上述三个方法的一个
class 描述符:
def __get__():
pass
def __set__():
pass
def __delete__():
pass
class 类:
name=描述符()
obj=类()
obj.name #get
obj.name='egon' #set
del obj.name #delete
#__del__:析构方法
垃圾回收时触发
1.7__str__,__repr__,__format__
改变对象的字符串显示__str__,__repr__
自定义格式化字符串
str和repr
class Bar:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
print("this str")
return "名字是%s age%s" %(self.name,self.age)
def __repr__(self): #转换字符串,在解释器中执行
print("thsi repr")
return "%s" %self.name
test = Bar("xi",10)
print(test)
print(test.__repr__()) #print(repr(test)) 执行的是__repr__
str函数或者print函数--->obj.__str__() repr或者交互式解释器--->obj.__repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩方法的返回值必须是字符串,否则抛出异常
自定义__format__练习
#和生成实例化对象的内容相对应
menu = {"ymd":"{0.year}{0.month}{0.day}",
"y-m-d":"{0.year}-{0.month}-{0.day}"}
class Foo:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
def __format__(self, format_spec): #format_spec指定格式化的类型
# print(format_spec,self)
if not format_spec or format_spec not in menu: #如果没有传入格式,或则在menu中没有这个格式
format_spec = "ymd" #则格式化的默认格式是ymd ,在menu中有
fm = menu[format_spec] #通过menu字典格式化成相应格式的类型
# print(fm)
return fm.format(self) #把对象传入,格式化的内容
ss = Foo(2016,12,26)
# print(format(ss,"ymd"))
print(format(ss,"y-m-d"))
print(format(ss,"4565456"))
1.8__slots__
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。
class Bar:
__slots__=['x','y']
n=Bar()
n.x,n.y=1,2
n.z=3#报错
print(n.__slots__) #n不再有__dict__
1.9__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("已经迭代完了") #抛出异常的提示信息 StopIteration: 已经迭代完了
self.n += 1
return self.n
aa = Foo(10)
print(aa.n)
print(next(aa))
print(next(aa))
print(next(aa))
print(next(aa))
print(next(aa))
print(next(aa))
# raise StopIteration("已经迭代完了") #抛出异常的提示信息 StopIteration: 已经迭代完了
# StopIteration: 已经迭代完了
#一般用for循环,遇到异常就结束for循环
# for i in aa:
# print(i)
2.0__doc__
这只是类的描述信息,描述这个的作用及功能,无法继承该属性(__doc__)
class Foo:
"这是测试"
pass
print(Foo.__doc__) #查看Foo类的注释
class Bar(Foo):
pass
print(Bar.__doc__) #没有注释信息,打印的是None
2.1__module__和__class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
class Foo:
"这是测试"
pass
test = Foo()
print(test.__module__)
print(test.__class__)
2.2__del__
这是析构方法,当对象在内存中被释放时,自动触发执行
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
2.3__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
# Foo()() 执行__call__
aa = Foo() #执行__init__
# aa() 执行__call__
2.4__enter__和__exit__
在操作文件对象的时候可以这么写
with open("文件名") as f:
代码
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
上下文管理协议
class Foo:
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("执行完毕exit")
with Foo("a.txt") as f:
print(f)
print(f.name)
print("我是分割线".center(50,"*"))
__exit__()中的三个参数分别代表异常类型(exc_type),异常值(exc_val)和追溯信息(exc_tb),即with语句中代码块出现异常,则with后的代码都无法执行
class Foo:
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("执行exit")
print(exc_type) #<class 'NameError'>
print(exc_val) #name 'aaaaaaaaa' is not defined
print(exc_tb) #Traceback
with Foo("a.txt") as f:
print(aaaaaaaaa) #错误的代码
print("我是分割线".center(50,"*")) #不会执行这个语句
如果__exit__返回值为True,那么异常会被清空,就好像啥都没发生一样,with后语句都正常执行
class Foo:
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("执行exit")
# print(exc_type) #<class 'NameError'>
# print(exc_val) #name 'aaaaaaaaa' is not defined
# print(exc_tb) #Traceback
return True
with Foo("a.txt") as f:
print(aaaaaaaaa)
print("这是测试异常") #没执行
#with后的语句
print("我是分割线".center(50,"*")) #会执行
总结
with obj as f:
"代码块"
1.with obj -----》触发obj.__center()__,拿到返回值
2. with obj as f 等同于 f = obj.__center__() ,返回值赋值f
3.执行代码块
一:没有异常的情况下,整个代码运行完毕后去触发__exit__,它的三个参数都为None
二:有异常的情况下,从异常出现的位置直接触发__exit__,
a.如果__exit__返回值为True,代表吞掉异常 ,执行with下面的语句
b.如果__exit__返回值为True,代表突出异常 (也就是正常报错),不执行下面的with语句
c.__exit__的运行完毕就代表了整个with语句的执行完毕
上下文管理的好处:
1.使用with语句的目的就是把代码块放入with中执行,
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制。
2.5元类metaclass
python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例)
例
class Foo:
pass
f1 = Foo() #f1是同过Foo类实例化的对象
上述:可以看f1是由Foo这个类产生的对象,而Foo本身也是对象,那他又是由那个类产生的呢?
class Foo:
pass
f1 = Foo() #f1是同过Foo类实例化的对象
print(type(f1)) # #查看这个对象是有那个类产生的
print(type(Foo)) #查看这个类是由那个类产生的 ,所有的类都是由type产生的
2、什么是元类?
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的实例为类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象。