我正在使用pylibtiff
库(托管here),我正在尝试扩展class TIFF(ctypes.c_void_p)
。我在使用以下方法时遇到问题:
# lib has been loaded with the path to libtiff.so, or equivalent
libtiff = ctypes.cdll.LoadLibrary(lib)
class TIFF(ctypes.c_void_p):
@classmethod
def open(cls, filename, mode='r'):
""" Open tiff file as TIFF.
"""
tiff = libtiff.TIFFOpen(filename, mode)
if tiff.value is None:
raise TypeError ('Failed to open file '+`filename`)
return tiff
(全班here)
现在,这里的预期使用模式是我们打开一个文件并收到一个class TIFF
实例,如下所示:
import libtiff
t = libtiff.TIFF.open('myfile.tiff')
# e.g. grab a numpy array of the data:
arr = t.read_image()
这是什么魔法?来自libtiff typed as a TIFF*
的C变量如何返回到pythonland并自动成为class TIFF
实例?大概这与ctypes.c_void_p
我问,因为我试图覆盖class TIFF
上的方法:
import libtiff
class TIFFdifferent(libtiff.TIFF):
def read_image(self, verbose=False, different=False):
if not different:
return libtiff.TIFF.read_image(self, verbose)
# my different code ...
但是,当我尝试创建class TIFFdifferent
个实例时,我得到class TIFF
代替:
In [3]: t = libtiffdifferent.TIFFdifferent.open('file.tiff')
In [4]: arr = t.read_image(different=True)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-8bdc24ec874c> in <module>()
----> 1 arr = t.read_image(different=True)
TypeError: read_image() got an unexpected keyword argument 'different'
In [5]: t.read_image?
Type: instancemethod
String Form:<bound method TIFF.read_image of <TIFF object at 0x10b67df80>>
File: /Library/Python/2.7/site-packages/libtiff/libtiff_ctypes.py
Definition: t.read_image(self, verbose=False)
Docstring:
Read image from TIFF and return it as an array.
In [6]: t
Out[6]: <TIFF object at 0x10b67df80>
所以,我需要做的是覆盖open
- 我不知道怎么做而不理解从C TIFF*
到python class TIFF
实例的魔术转换,没有构造函数,强制转换或任何其他明确的。
答案 0 :(得分:2)
魔法在libtiff_ctypes.py的第1041行:
libtiff.TIFFOpen.restype = TIFF
正如ctypes
文档中的15.17.1.8 Return types所述,您不必使用ctypes
类型作为结果类型;你可以指定任何Python可调用 - 通常是一个函数,它将验证实际的C返回值以引发异常,或者一个类,它将真正的C返回值作为其构造函数参数。
您可能会注意到TIFF
类没有定义__init__
方法。这是来自c_void_p
的继承的地方:您可以从指针构造c_void_p
;因此,如果你有一个c_void_p
的子类,并且它不需要进行任何其他初始化,它可以依靠基类构造来处理它。
所以,我需要做的是覆盖open - 我不知道怎么做而不理解从C
TIFF*
到python类TIFF
实例的魔术转换,没有构造函数,强制转换或任何其他显式。
好吧,你显然可以在你的覆盖中做一个明确的构造调用,即使原始版本没有这样做:
def myopen(*args):
tiffpointer = libtiff.cfunc(*args)
return TIFF(tiffpointer)
但是如果你不能只定义libtiff.cfunc.restype
,你只想这样做(例如,因为pylibtiff
中的其他一些代码依赖于它返回不同的东西)。
无论如何,我不确定这是你真正想做的事情。你要做的是写下这样的东西:
class TIFFDifferent(TIFF):
…
@classmethod
def open(cls, filename, mode='r'):
tiff = libtiff.TIFFOpen(filename, mode)
if tiff.value is None:
raise TypeError ('Failed to open file '+`filename`)
return cls(tiff)
这是有效的,你无需了解restype
背后的魔力。除非你担心构造一个短暂的临时TIFF
对象的成本(正如你所见,没有成员,没有构造函数代码超出c_void_p
的对象),isn'这很简单吗?
还有一种可能性,我通常不建议,也可能在这里不合适,但可能是:如果pylibtiff
的设计方式是通过子类覆盖其行为很难做到,你可以(a)分叉代码并使用你自己的版本,或(b)在运行时monkeypatch它。正如您自己的评论中所建议的那样:
class TIFFDifferent(TIFF):
…
@classmethod
def open(cls, filename, mode='r'):
stash = libtiff.TIFFOpen.restype
libtiff.TIFFOpen.restype = cls
tiff = libtiff.TIFFOpen(filename, mode)
libtiff.TIFFOpen.restype = stash
if tiff.value is None:
raise TypeError ('Failed to open file '+`filename`)
return tiff
如果您的构造函数可能抛出异常,您希望将隐藏包裹在contextmanager
(或使用try:
/ finally:
)中。否则你将离开restype
补丁。