在Python自定义类中实现'with object() as f'的使用
我需要在Python中打开一个类似文件的对象(这是通过/dev/进行的串口连接),然后再把它关闭。这在我类的几个方法中会做好几次。我之前的做法是,在构造函数里打开文件,然后在析构函数里关闭它。不过我遇到了一些奇怪的错误,我觉得这可能和垃圾回收有关,我还不太习惯不知道我的对象到底什么时候会被删除。
我之所以这么做,是因为每次打开文件时,我都需要用到tcsetattr
这个函数,并且要传入一堆参数,这样反复操作起来很麻烦。所以我想实现一个内部类来处理这些事情,这样我就可以用下面的方式来使用它:
with Meter('/dev/ttyS2') as m:
我在网上查了一下,但没找到一个很好的关于with
语法是怎么实现的答案。我看到它使用了__enter__(self)
和__exit__(self)
这两个方法。但我只需要实现这两个方法,就可以使用with
语法了吗?还是说还有其他的要求?
有没有示例或者一些文档可以让我看看,了解一下文件对象是怎么实现这个的?
3 个回答
我找到的第一个谷歌搜索结果解释得很简单:
http://effbot.org/zone/python-with-statement.htm
而PEP(Python增强提案)则解释得更准确(不过也更啰嗦):
最简单的方法可能是使用标准的Python库模块 contextlib:
import contextlib
@contextlib.contextmanager
def themeter(name):
theobj = Meter(name)
try:
yield theobj
finally:
theobj.close() # or whatever you need to do at exit
# usage
with themeter('/dev/ttyS2') as m:
# do what you need with m
m.read()
这样做并不会让 Meter
本身变成一个上下文管理器(所以对这个类没有影响),而是用一个工厂函数 themeter
来“装饰”它(这里的“装饰”不是指Python中的“装饰器语法”,而是有点像装饰者设计模式;-))。这个 themeter
是一个上下文管理器(它是通过 contextlib.contextmanager
装饰器从你写的“单个 yield
”生成器函数构建而来的)。这样做可以让你更容易地分开进入和退出的条件,避免嵌套等问题。
这些方法基本上就是让对象能和 with
语句一起使用所需要的全部内容。
在 __enter__
方法中,你需要在打开文件并进行设置后返回这个文件对象。
在 __exit__
方法中,你需要关闭这个文件对象。写入文件的代码会放在 with
语句的主体部分。
MODE = 'rb'
class Meter():
def __init__(self, dev):
self.dev = dev
def __enter__(self):
#ttysetattr etc goes here before opening and returning the file object
self.fd = open(self.dev, MODE)
return self
def __exit__(self, exception_type, exception_value, exception_traceback):
#Exception handling here
self.fd.close()
meter = Meter('/dev/tty0')
with meter as m:
#here you work with the file object.
m.fd.read()