Python基础之面向对象2
一、类的方法
类的成员可以分为三大类:字段、方法和属性。
注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
a、字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
- 普通字段属于对象
静态字段属于类a
class Foo: # 静态字段 country = "China" def __init__(self, name): # 普通字段 self.name = name # 直接访问静态字段 Foo.country # 直接访问普通字段 obj = Foo("山西")
由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。
- 静态字段在内存中只保存一份
- 普通字段在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
b、方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数;
class Foo: #静态方法 @staticmethod def xo(arg1, arg2): #无默认参数,可不传参数,可传任意参数 print("xo") #类方法 @classmethod def xxoo(cls): #定义类方法,至少有一个cls参数 print(cls) #普通方法,类中 def show(self): #定义普通方法,至少有一个self参数 print("show") # 调用静态方法 Foo.xo(1,2) # 调用类方法 Foo.xxoo() # 调用普通方法 obj = Foo: obj.show()
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
c.属性
属性的基本使用
由属性的定义和调用要注意一下几点:
- 定义时,在普通方法的基础上添加 @property 装饰器;
- 定义时,属性仅有一个self参数
- 调用时,无需括号
方法:foo_obj.func()
属性:foo_obj.prop
注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class Foo: def __init__( self , name): self .name = name # 属性,将方法伪造成一种字段 @property def end( self ): return self .name # 修改end值 @end .setter def end( self , new_name): self .name = new_name obj = Foo( "nick" ) # 调用属性,不需要加括号 result2 = obj.end print (result2) # 调用修改end.setter属性(自动将jenny传入当参数new_name) obj.end = "jenny" result3 = obj.end print (result3) |
属性的两种定义方式
属性的定义有两种方式:
- 装饰器 即:在方法上应用装饰器
- 静态字段 即:在类中定义值为property对象的静态字段
装饰器方式:在类的普通方法上应用@property装饰器
我们知道Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。( 如果类继object,那么该类是新式类 )
经典类,具有一种@property装饰器
# ############### 定义 ############### class Goods: @property def price(self): return "nick" # ############### 调用 ############### obj = Goods() result = obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
新式类,具有三种@property装饰器
# ############### 定义 ############### class Goods(object): @property def price(self): print '@property' @price.setter def price(self, value): print '@price.setter' @price.deleter def price(self): print '@price.deleter' # ############### 调用 ############### obj = Goods() obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值 obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数 del obj.price # 自动执行 @price.deleter 修饰的 price 方法
注:经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deltter def price(self, value): del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 del obj.price # 删除商品原价 实例
静态字段方式,创建值为property对象的静态字段
所以,定义属性共有两种方式,分别是【装饰器】和【静态字段】,而【装饰器】方式针对经典类和新式类又有所不同。
当使用静态字段的方式创建属性时,经典类和新式类无区别
class Foo: def get_bar(self): return 'nick' BAR = property(get_bar) obj = Foo() reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值 print reuslt
property的构造方法中有个四个参数
- 第一个参数是方法名,调用
对象.属性
时自动触发执行方法 - 第二个参数是方法名,调用
对象.属性 = XXX
时自动触发执行方法 - 第三个参数是方法名,调用
del 对象.属性
时自动触发执行方法 - 第四个参数是字符串,调用
对象.属性.__doc__
,此参数是该属性的描述信息
class Foo: def get_bar(self): return 'nick' # *必须两个参数 def set_bar(self, value): return 'set value' + value def del_bar(self): return 'nick' BAR = property(get_bar, set_bar, del_bar, 'description...') obj = Foo() obj.BAR # 自动调用第一个参数中定义的方法:get_bar obj.BAR = "jenny" # 自动调用第二个参数中定义的方法:set_bar方法,并将“jenny”当作参数传入 del Foo.BAR # 自动调用第三个参数中定义的方法:del_bar方法 obj.BAR.__doc__ # 自动获取第四个参数中设置的值:description...
由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 def get_price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price def set_price(self, value): self.original_price = value def del_price(self, value): del self.original_price PRICE = property(get_price, set_price, del_price, '价格属性描述...') obj = Goods() obj.PRICE # 获取商品价格 obj.PRICE = 200 # 修改商品原价 del obj.PRICE # 删除商品原价 实例class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 def get_price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price def set_price(self, value): self.original_price = value def del_price(self, value): del self.original_price PRICE = property(get_price, set_price, del_price, '价格属性描述...') obj = Goods() obj.PRICE # 获取商品价格 obj.PRICE = 200 # 修改商品原价 del obj.PRICE # 删除商品原价
二、类成员的修饰符
每一个类的成员都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能方法
私有成员和公有成员的定义不同:私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
class Foo: xo = "xo" #公有字段 __ox = "ox" #私有字段 def __init__(self): self.name = "nick" #公有字段 self.__name2 = "nick" #私有字段
私有成员和公有成员的访问限制不同:
静态字段
- 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;
双下划线开头的名字
class C: name = "公有静态字段" def func(self): print C.name class D(C): def show(self): print C.name C.name # 类访问 obj = C() obj.func() # 类内部可以访问 obj_son = D() obj_son.show() # 派生类中可以访问 公有静态字段
class C: __name = "公有静态字段" def func(self): print C.__name class D(C): def show(self): print C.__name C.__name # 类访问 ==> 错误 obj = C() obj.func() # 类内部可以访问 ==> 正确 obj_son = D() obj_son.show() # 派生类中可以访问 ==> 错误
方法、属性的访问于上述方式相似,即:私有成员只能在类内部使用
ps:如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。
三、类的特殊成员
成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。
1. __doc__
表示类的描述信息
class Foo: """ 描述类信息 """ def func(self): pass print Foo.__doc__ #输出:类的描述信息 __doc__
2. __module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
#lib/xx
class C: def __init__(self): self.name = 'nick'
from lib.xx import C obj = C() print obj.__module__ # 输出 lib.xx,即:输出模块 print obj.__class__ # 输出 lib.xx.C,即:输出类
3. __init__
构造方法,通过类创建对象时,自动触发执行
复制代码 class Foo: def __init__(self, name): self.name = name self.age = 18 obj = Foo('nick') # 自动执行类中的 __init__ 方法
4. __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,
所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
class Foo: def __del__(self): pass
5. __call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,
即:对象() 或者 类()()
# __call__ class Foo: def __init__(self): print("This is init") def __call__(self, *args, **kwargs): print("This is call") return "CC" obj = Foo() # 执行 __init__ obj() # 执行 __call__ result = Foo()() # 执行 __call__ print(result) __call__
6. __dict__
类或对象中的所有成员
class Province: country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): print 'func' # 获取类的成员,即:静态字段、方法、 print Province.__dict__ # 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None} obj1 = Province('shangxi',10000) print obj1.__dict__ # 获取 对象obj1 的成员 # 输出:{'count': 10000, 'name': 'shangxi'} obj2 = Province('shangdong', 3888) print obj2.__dict__ # 获取 对象obj1 的成员 # 输出:{'count': 3888, 'name': 'shangdong'} __dict__
7. __str__
如果一个类中定义了__str__方法,那么在打印对象时,默认输出该方法的返回值。
class Foo: def __str__(self): return 'nick' obj = Foo() print obj # 输出:nick
8、__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据
class Foo: def __getitem__(self, item): print(item) def __setitem__(self, key, value): print(key, value) def __delitem__(self, key): print(key) obj = Foo() obj["nick"] # 自动触发执行 __getitem__ obj["nick"] = "jenny" # 自动触发执行 __setitem__ del obj["nick"] # 自动触发执行 __delitem__ __getitem____setitem____delitem__
9、__getslice__、__setslice__、__delslice__
该三个方法用于分片操作(2.0x),如:列表
class Foo(object): def __getslice__(self, i, j): print '__getslice__',i,j def __setslice__(self, i, j, sequence): print '__setslice__',i,j def __delslice__(self, i, j): print '__delslice__',i,j obj = Foo() obj[-1:1] # 自动触发执行 __getslice__ obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__ del obj[0:2] # 自动触发执行 __delslice__ __getslice____setslice____delslice__
10. __iter__
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__
#!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __init__(self, sq): self.sq = sq def __iter__(self): return iter(self.sq) obj = Foo([11,22,33,44]) for i in obj: print i __iter__
#!/usr/bin/env python # -*- coding:utf-8 -*- obj = iter([11,22,33,44]) while True: val = obj.next() print val
11. __mro__ 和__slot__
在解析父类的__init__时,定义解析顺序的是子类的__mro__属性,内容为一个存储要解析类顺序的元组。
class A(object): def __init__(self): print ' -> Enter A' print ' <- Leave A' class B(A): def __init(self): print ' -> Enter B' # A.__init__(self) super(B, self).__init__() print ' <- Leave B' class C(A): def __init__(self): print " -> Enter C" # A.__init__(self) super(C, self).__init__() print " <- Leave C" class D(B, C): def __init__(self): print " -> Enter D" # B.__init__(self) # C.__init__(self) super(D, self).__init__() print " <- Leave D" if __name__ == "__main__": d = D() print "MRO:", [x.__name__ for x in D.__mro__] print type(D.__mro__) 执行以上代码,得到的输出为: -> Enter D -> Enter C -> Enter A <- Leave A <- Leave C <- Leave D MRO: ['D', 'B', 'C', 'A', 'object'] <type 'tuple'>
__slot__定义类中可以被外界访问的属性。
当父类中定义了__slot__时,不能向父类中添加属性。如果子类中没有定义__slot__,则子类不受父类__slot__定义的限制。
如果父类与子类中都定义了__slot__,则邮箱的结果为父类与子类__slot__的合集。
12. __new__ 和 __metaclass__
阅读以下代码:
1
2
3
4
5
6
|
class Foo( object ): def __init__( self ): pass obj = Foo() # obj是通过Foo类实例化的对象 |
上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
1
2
|
print type (obj) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print type (Foo) # 输出:<type 'type'> 表示,Foo类对象由 type 类创建 |
所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
那么,创建类就可以有两种方式:
1> 普通方式
1
2
3
4
|
class Foo( object ): def func( self ): print 'hello word' |
2> 特殊方式(type类的构造函数)
1
2
3
4
5
6
7
|
def func( self ): print 'hello word' Foo = type ( 'Foo' ,( object ,), { 'func' : func}) #type第一个参数:类名 #type第二个参数:当前类的基类 #type第三个参数:类的成员 |
==》 类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
class MyType(type): def __init__(self, what, bases=None, dict=None): super(MyType, self).__init__(what, bases, dict) def __call__(self, *args, **kwargs): obj = self.__new__(self, *args, **kwargs) self.__init__(obj) class Foo(object): __metaclass__ = MyType def __init__(self, name): self.name = name def __new__(cls, *args, **kwargs): return object.__new__(cls, *args, **kwargs) # 第一阶段:解释器从上到下执行代码创建Foo类 # 第二阶段:通过Foo类创建obj对象 obj = Foo()
13.__get__,__set__,__delete__
描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议。
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符 2 def __get__(self,instance,owner): 3 print('get方法') 4 def __set__(self, instance, value): 5 print('set方法') 6 def __delete__(self, instance): 7 print('delete方法')
作用:用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
class Foo: def __get__(self,instance,owner): print('===>get方法') def __set__(self, instance, value): print('===>set方法') def __delete__(self, instance): print('===>delete方法') #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法 f1=Foo() f1.name='egon' print(f1.name) del f1.name #疑问:何时,何地,会触发这三个方法的执行
描述符应用在什么时候,什么地方
class D: def __get__(self, instance, owner): print("-->get") def __set__(self, instance, value): print("-->set") def __delete__(self, instance): print("-->delete") class E: e = D() # 描述谁? ee = E() ee.y = 10 # 此时描述的是e y则不会被描述 ee.e # 访问e属性,则会触发__get__ ee.e = 2 # 为e进行赋值操作,则会触发__set__ del ee.e # 删除e的属性,则会触发__delete__ # print(ee.__dict__)
a.数据描述符(至少实现了__get__()和__set__()两种方法)
class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get')
b.非数据描述符(没有实现__set__()方法)
1 class Foo: 2 def __get__(self, instance, owner): 3 print('get')
注意事项:
- 描述符本身应该定义成新式类,被代理的类也应该是新式类
- 必须把描述符定义成另外一个类触发的类属性,不能为定义到构造函数
应用:
class Typed: def __get__(self, instance,owner): print('get方法') print('instance参数【%s】' %instance) print('owner参数【%s】' %owner) # owner是显示对象是属于谁拥有的 def __set__(self, instance, value): print('set方法') print('instance参数【%s】' %instance) # instance是被描述类的对象(实例) print('value参数【%s】' %value) # value是被描述的值 def __delete__(self, instance): print('delete方法') print('instance参数【%s】'% instance) class People: name=Typed() def __init__(self,name,age,salary): self.name=name #触发的是代理 self.age=age self.salary=salary p1=People('alex',13,13.3) #'alex' #触发set方法 p1.name #触发get方法,没有返回值 p1.name='age' #触发set方法 print(p1.__dict__) #{'salary': 13.3, 'age': 13} # 因为name已经被描述,所以实例的属性字典并不存在name # 当然也说明一点实例属性的权限并没有数据描述符的权限大 set方法 instance参数【<__main__.People object at 0x000001CECBFF0080>】 value参数【alex】 get方法 instance参数【<__main__.People object at 0x000001CECBFF0080>】 owner参数【<class '__main__.People'>】 set方法 instance参数【<__main__.People object at 0x000001CECBFF0080>】 value参数【age】 {'salary': 13.3, 'age': 13} 描述符所包含的参数是什么
class Foo: def __init__(self,key,pd_type): self.key = key self.pd_type = pd_type def __get__(self, instance, owner): print("get") return instance.__dict__[self.key] # 返回值是 instace对象属性字典self.key所对应的值 def __set__(self, instance, value): print(value) # 输出value所对应的值 if not isinstance(value,self.pd_type): # 判断被描述的值 是否 属于这个类的 raise TypeError("%s 传入的类型不是%s" %(value,self.pd_type)) # 为否 则抛出类型异常 instance.__dict__[self.key] = value # True 对instance对象的属性字典进行赋值操作 def __delete__(self, instance): print("delete") instance.__dict__.pop(self.key) # 如果进行删除操作,也是对instance对象的属性字典进行删除操作 class Sea: name = Foo("name",str) # 向描述符传入俩个值 history = Foo("history",int) def __init__(self,name,addr,history): self.name = name self.addr = addr self.history = history s1 = Sea("北冰洋","北半球",10000) print(s1.__dict__) print(s1.name) # 对被描述的属性进行访问,触发__get__ 北冰洋 {'addr': '北半球', 'history': 10000, 'name': '北冰洋'} get 北冰洋 用描述符实现的类型检测
# 描述符和类装饰器的结合使用 class Foo: """ 描述符 """ def __init__(self,key,pd_type): self.key = key self.pd_type = pd_type def __get__(self, instance, owner): print("get") return instance.__dict__[self.key] def __set__(self,instance,value): """ instance是对象(实例) """ print("set") if not isinstance(value,self.pd_type): # 如果判断类型为False raise TypeError("%s 传入的类型不是%s" %(value,self.pd_type)) # 抛出异常 instance.__dict__[self.key] = value #操作对象(实例)的属性字典设置值 def __delete__(self, instance): print("delete") instance.__dict__.pop(self.key) def Typed(**kwargs): def func(obj): for key,value in kwargs.items(): # 遍历字典的键值对 setattr(obj,key,Foo(key,value)) # 为Sea的属性字典设置值。 并执行Foo 给Foo传入俩个值 # print("--->" ,obj) return obj # func的返回值是Sea return func @Typed(name=str,addr=str,history=int) # Typed函数运行结束实际上就是 @func --> sea=func(Sea) # Typed运行,并将参数全部传给kwargs class Sea: def __init__(self,name,addr,history): self.name = name self.addr = addr self.history = history s1 = Sea("大西洋","地球",10000) print(s1.__dict__) print(Sea.__dict__) # 此时的name,addr,history均被Foo所描述 装饰器和描述符实现类型检测的终极版本
描述符总结
- 描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性
- 描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
- 利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)
# 伪造的property class Wzproperty: def __init__(self,func): self.func = func def __get__(self, instance, owner): """ 如果类去调用 instance 为None""" print("get") if instance is None: return self setattr(instance,self.func.__name__,self.func(instance)) # 给实例字典设置值,避免重复计算 return self.func(instance) class Sea: def __init__(self,name,history,speed): self.name = name self.history = history self.speed = speed @Wzproperty # test = Wzptoperty(test) def test(self): return self.history * self.speed s1 = Sea("大西洋",10,20) # print(Sea.__dict__) # print(Sea.test) # 如果类去调用 描述符的instance 此时是None print(s1.test) print(s1.test) # 这一次就不会触发描述符,因为实例属性字典就有 """因为有了为实例的属性字典设置了结果。所以会率先从自己的属性字典找 其次触发非数据描述符,同时也声明了实例属性的权限大于非数据描述。 如果给描述符+__set__,描述符就变为数据描述符,根据权限实例再去用不会先去 自己的属性字典,而是触发描述符的操作""" print(s1.__dict__) 控制台输出 get 200 {'test': 200, 'speed': 20, 'name': '大西洋', 'history': 10} # 实例的属性字典 伪造的property以及阐释
# 伪造的classmethod class Wzclassmethod: def __init__(self,func): self.func = func def __get__(self, instance, owner): print("get") def bar(): return self.func(owner) # test(Sea) return bar def __set__(self, instance, value): print("set") class Sea: long = 10 kuan = 20 @Wzclassmethod # test = Wzclassmethod(test) def test(cls): print("长%s 宽%s" %(cls.long,cls.kuan)) Sea.test() 伪造的classmethod
# 伪造的staticmethod import hashlib,time class Wzstaticmethod: def __init__(self,func): self.func = func def __set__(self, instance, value): print("set") def __get__(self, instance, owner): print("get") def bar(): if instance is None: return self.func() return bar def __delete__(self, instance): print("delete") class Onepiece: def __init__(self): pass @Wzstaticmethod # test = Wzstaticmethod(test) def test(x=1): hash = hashlib.md5() hash.update(str(time.time()).encode("utf-8")) filename = hash.hexdigest() print(filename) return filename # print(Onepiece.__dict__) Onepiece.test() 伪造的staticmethod
14、__enter__ __exit__
1.__entter__ __exit__ class Open: def __init__(self,name): self.name=name def __enter__(self): print("执行enter") def __exit__(self, exc_type, exc_val, exc_tb): print("执行exit") return True with Open("a.txt") as f: print("--->") print(xx) print("爱吹牛逼") 执行顺序 1, 开始执行__enter__ 正常:执行with下级的程序---》执行__exit__————》with下面的程序 异常:执行with下级的程序-->遇到异常马上执行__exit__-->__exit__返回不为True则终止程序 __exit__返回值为True 则继续运行with as 之下的语句,不在运行with下级程序