我正在使用cffi
为C库编写Python包装器。
C库必须初始化并关闭。另外,cffi
需要一些位置来保存从ffi.dlopen()
返回的状态。
我在这里可以看到两条路径:
我要么将整个有状态业务包装在这样的类中
class wrapper(object):
def __init__(self):
self.c = ffi.dlopen("mylibrary")
self.c.initialize()
def __del__(self):
self.c.terminate()
或者我提供了两个全局函数,它们将状态隐藏在全局变量中
def initialize():
global __library
__library = ffi.dlopen("mylibrary")
__library.initialize()
def terminate():
__library.terminate()
del __library
第一条路径有些麻烦,因为它要求用户始终创建一个对象,除了管理库状态外,该对象实际上没有其他用途。另一方面,它可以确保每次实际上都调用
terminate()
。第二条路径似乎使API更加容易。但是,它暴露了一些隐藏的全局状态,这可能是一件坏事。另外,如果用户忘记调用
terminate()
,则C库不会正确卸载(在C端这不是大问题)。这些路径中的哪一个更适合pythonic?
最佳答案
仅当库实际上在一个应用程序中支持多个实例之类时,才公开包装对象在python中才有意义。如果它不支持它,或者不真正相关,请引用kindall的建议,并在导入时初始化库,并添加atexit处理程序进行清理。
在无状态api甚至不支持保持不同状态集的api周围添加包装器并不是真正的Python,这会提高人们对不同实例具有某种隔离的期望。
示例代码:
import atexit
# Normal library initialization
__library = ffi.dlopen("mylibrary")
__library.initialize()
# Private library cleanup function
def __terminate():
__library.terminate()
# register function to be called on clean interpreter termination
atexit.register(__terminate)
有关atexit的更多详细信息,this question和python documentation当然也有更多详细信息。
关于python - Python模块中的全局状态,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17346428/