Python 进阶 魔术方法 上下文管理器 with、__enter__、__exit__

本贴最后更新于 584 天前,其中的信息可能已经时移世异

实践是检验真理的唯一标准


"""
1、为什么使用with的时候,可以省去执行 对象.close()???

2、with到底做了什么???
    with启动了对象的上下文管理器

3、上下文管理器协议: 
    __enter__: 进入
        enter方法返回的结果被as后面的变量接收,with ... as f

    __exit__: 退出 
        with中所有的语句执行完毕执行 执行 __exit__
        该方法内会手动调用 self.close(),故省去执行 对象.close()

4、在python中所有实现了上下文管理器协议的对象 都可以用使用with操作

5、with中有报错 __exit__ 三个参数会抛出异常

"""
import requests

# 文件操作
f = open('斗罗大陆.txt', 'w')
f.close()  # 关闭会话

# 创建会话
session = requests.Session()
session.close()  # 关闭会话

# dir(n)返回参数的属性、方法列表
print(dir(f))
# ['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']

print(dir(session))
# ['__attrs__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'adapters', 'auth', 'cert', 'close', 'cookies', 'delete', 'get', 'get_adapter', 'get_redirect_target', 'head', 'headers', 'hooks', 'max_redirects', 'merge_environment_settings', 'mount', 'options', 'params', 'patch', 'post', 'prepare_request', 'proxies', 'put', 'rebuild_auth', 'rebuild_method', 'rebuild_proxies', 'request', 'resolve_redirects', 'send', 'should_strip_auth', 'stream', 'trust_env', 'verify']

# 因 open() 和 Session()的属性都包含'__enter__', '__exit__'
# 所以可以用 with
with open('斗罗大陆.txt', 'w') as f:
    f.write('唐三封神')
    print('这里open()')
# 这里open()

with requests.Session() as session:
    print('这里是Session()')
    pass
# 这里是Session()


print('====自定义文件操作的上下文管理器协议 demo ==========================================')

# 对象里面既没有__enter__,也没有__exit__,用with报错
class MyOpenNo:
    pass


# 对象里面 有__enter__,没__exit__,用with报错
class MyOpenNoExit:
    def __enter__(self):
        return self


# 对象里面__enter__、__exit__都有,用with正常
class MyOpen:
    def __enter__(self):
        print('这里是 __enter__')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('这里是 __exit__')


print('对象里面既没有__enter__,也没有__exit__,用with报错 ===================================')
with MyOpenNo() as f:
    print('这里是with中的操作')
print('整个with执行已执行完')
# python3.9打印结果:
# AttributeError: __enter__

# Python3.11打印结果:
# TypeError: 'MyOpenNo' object does not support the context manager protocol


print('对象里面有__enter__,没__exit__,用with报错 ===================================')
with MyOpenNoExit() as f2:
    print('这里是with中的操作')
print('整个with执行已执行完')
# python3.9打印结果:
# AttributeError: __exit__

# Python3.11打印结果:
# TypeError: 'MyOpenNoExit' object does not support the context manager protocol (missed __exit__ method)


print('对象里面__enter__、__exit__都有,用with正常 ===================================')
with MyOpen() as f3:
    print('这里是with中的操作')
print('整个with执行已执行完')
# python3.9打印结果:
# 这里是 __enter__
# 这里是with中的操作
# 这里是 __exit__
# 整个with执行已执行完

# Python3.11打印结果:
# 这里是 __enter__
# 这里是with中的操作
# 这里是 __exit__
# 整个with执行已执行完



print('案例Demo自己写一个open类:===========================================')


class MyOpenDemo:
    def __init__(self, filename, mode, encoding):
        self.filename = filename
        self.mode = mode
        self.encoding = encoding

    def __enter__(self):
        print("---__enter__---方法")
        self.f = open(self.filename, self.mode, encoding=self.encoding)
	# print(这一行会报错__enter__)
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        :param exc_type: 异常类型 例:NameError
        :param exc_val: 异常信息 例:name '这一行会报错' is not defined
        :param exc_tb: 异常溯源对象 例:溯源报错行 Traceback...File "D:\Documents\PycharmProjects\pythonCk_13\testCk_13.py", line 27, in <module>...
        :return:
        """
        print('----__exit__---')
        self.f.close()


with MyOpenDemo('哔哩哔哩.txt', 'w', encoding='utf-8') as f:
    print(f.write('东风快递,全球包邮,使命必达。'))
    # print(这一行会报错)
# 打印结果:不添加报错代码“print(这一行会报错)”
"""
---__enter__---方法
15
----__exit__---
"""


# 打印结果:添加报错代码“print(这一行会报错)”
"""
---__enter__---方法
15
----__exit__---
Traceback (most recent call last):
  File "D:\Documents\PycharmProjects\pythonCk_13\testCk_13.py", line 27, in <module>
    print(这一行会报错)
NameError: name '这一行会报错' is not defined
"""
# 结论:
# 1、with内有报错代码时,__enter__、__exit__代码仍执行。
# 2、__exit__的三个参数会抛出with内的报错信息。
# 3、__enter__中有代码报错,with和__exit__内代码不执行。


f.write('检测__exit__中是否有关闭')
# 如果__exit__中没有close()打印结果:
# 不报错:文件正常执行
"""
---__enter__---方法
15
----__exit__---
"""

# 如果__exit__中有close()打印结果:
# 报错:提示文件已关闭
"""
ValueError: I/O operation on closed file.
"""

2 操作
qq5942527 在 2023-04-10 19:34:59 更新了该帖
qq5942527 在 2023-04-10 12:36:59 更新了该帖
回帖
请输入回帖内容 ...