如何为类赋予类似语句的功能?

Posted

技术标签:

【中文标题】如何为类赋予类似语句的功能?【英文标题】:How to give with-statement-like functionality to class? 【发布时间】:2012-01-03 07:34:05 【问题描述】:

[我为这个无能的标题道歉;我想不出更好的办法。欢迎提出更好标题的建议。]

我想实现HDF5 文件的接口,通过文件锁定支持多进程级并发。该模块的预期环境是通过 NFS 访问共享磁盘的 Linux 集群。目标是允许在多个不同主机上运行的多个并行进程对同一文件进行并发访问(通过 NFS)。

我希望能够通过h5py.File 类的包装类来实现锁定功能。 (@987654322@ 已经提供了对线程级并发的支持,但是底层的 HDF5 库不是线程安全的。)

如果我能本着这样的精神做一些事情,那就太好了:

class LockedH5File(object):
    def __init__(self, path, ...):
        ...
        with h5py.File(path, 'r+') as h5handle:
            fcntl.flock(fcntl.LOCK_EX)
            yield h5handle
        # (method returns)

我意识到上面的代码是错误的,但我希望它传达了主要思想:即让表达式LockedH5File('/path/to/file')为客户端代码提供一个开放句柄,然后可以对客户端执行各种任意读/写操作它。当这个句柄超出范围时,它的析构函数关闭句柄,从而释放锁。

促成这种安排的目的有两个:

    将句柄的创建(由库代码)与随后在句柄上请求的操作(由客户端代码)分离,并且

    确保手柄关闭并释放锁,无论在使用过程中发生什么 中间代码的执行(例如异常,未处理 信号,Python 内部错误)。

如何在 Python 中实现这个效果?

谢谢!

【问题讨论】:

【参考方案1】:

要完成这项工作,您的类需要实现context manager protocol。或者,使用 contextlib.contextmanager 装饰器编写生成器函数。

你的班级可能大致是这样的(h5py 的使用细节可能大错特错):

class LockedH5File(object):
    def __init__(self, path, ...):
        self.h5file = h5py.File(path, 'r+')
    def __enter__(self):
        fcntl.flock(fcntl.LOCK_EX)
        return self.h5file
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.h5file.close()

【讨论】:

【参考方案2】:

嗯,上下文管理器和with 语句。一般来说,Python 中的析构函数不能保证完全运行,所以除了故障安全清理之外,你不应该依赖它们。提供__enter____exit__方法,像这样使用

with LockedFile(...) as fp:
    # ...

【讨论】:

【参考方案3】:

可以在with 语句中使用的对象称为上下文管理器;他们实现了一个简单的接口。它们必须提供两个方法,一个__enter__ 方法,它不带参数并且可以返回任何东西(将分配给as 部分中的变量)和一个__exit__ 方法,它带三个参数(它将填入sys.exc_info()) 的结果并返回非零表示处理了异常。您的示例可能如下所示:

class LockedH5File(object):
    def __init__(self, path, ...):
        self.path = path

    def __enter__(self):
        self.h5handle = h5handle = h5py.File(self.path, 'r+')
        fcntl.flock(fcntl.LOCK_EX)
        return h5handle

    def __exit__(self, exc_type, exc_info, exc_tb):
        self.h5handle.close()

【讨论】:

以上是关于如何为类赋予类似语句的功能?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 PyQt 提供类似宏的录制功能

如何为垂直文本赋予 2 种不同颜色的字体,以便文本 1 和文本 2 在一个“div 类”中?

如何为 Flutter 中的文本赋予边框/背景?

如何为数据库中的类似对象执行太多的多功能?

如何为单个 UIView 提供两个不同的类引用?

如何为网站添加百度统计功能