0

我正在使用pylibtiff图书馆(托管在这里),我正在尝试扩展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

(这里全班)

现在,这里的预期使用模式是我们打开一个文件并接收一个class TIFF实例,如下所示:

import libtiff
t = libtiff.TIFF.open('myfile.tiff')

# e.g. grab a numpy array of the data:
arr = t.read_image()

这是什么魔法?如何将 libtiff 中的 C 变量键入为 TIFF*返回 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>

所以,我需要做的也是重写——如果不了解从 C到 python实例的魔法转换,没有构造函数、强制转换或任何其他显式的东西open,我不知道该怎么做。TIFF*class TIFF

4

1 回答 1

3

神奇之处在于libtiff_ctypes.py的第 1041 行:

libtiff.TIFFOpen.restype = TIFF

15.17.1.8 文档中的返回类型中所述ctypes,您不必使用ctypes类型作为结果类型;您可以指定任何 Python 可调用对象——通常是一个函数,它将验证真正的 C 返回值以引发异常,或者一个类,它将真正的 C 返回值作为其构造函数参数。

您可能会注意到TIFF该类没有定义__init__方法。这就是继承 from 的来源:您可以从指针c_void_p构造 a ;c_void_p因此,如果你有一个 的子类c_void_p,并且它不需要做任何其他初始化,它可以只依靠基类构造来处理它。

TIFF*所以,我需要做的是也覆盖 open - 如果不了解从 C到 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对象的成本(正如您所见,除了 a 之外没有成员和构造函数代码c_void_p),这还不够简单吗?

还有一种可能性,我通常不会建议,也可能不适合这里,但可能是:如果pylibtiff设计的方式很难通过子类覆盖其行为,您可以(a)分叉编写代码并使用您自己的版本,或者 (b) 在运行时对其进行猴子补丁。正如您自己的评论中所建议的那样,如下所示:

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

如果您的构造函数可能抛出异常,您可能希望将存储包装在 a contextmanager(或使用try:/ )中;finally:否则你会留下restype补丁。

于 2012-12-13T19:26:47.107 回答