在cython中为__contains__指定libcpp.string类型

时间:2016-06-06 03:49:58

标签: python c++ cython

我为某些C ++数据结构实现了一个cython包装器,它将c ++字符串作为键和值。

我在pxd中使用C ++方法原型,对于具有模板键/值类型的类(抱歉,我从未使用过C ++,只有C,所以我不确定正确的术语,如果不清楚,请告诉我。)

然后我在.pyx文件中定义一个类,以便能够从python调用,它包装了一个my_type[string, string]的实例:

from libcpp.string cimport string
cdef class MyType:
    ## This field is declared in .pxd:
    # cdef my_type[string, string]* thisptr

    def __cinit__(self, f=None):
        self.thisptr = new my_type[string, string]()

    def __init__(self, arg=None):
        if hasattr(arg, 'fileno'):
            self.load(arg)
        elif isinstance(arg, int):
            self.thisptr.resize(arg)
        elif isinstance(arg, str):
            with open(arg, 'rb') as f:
                self.load(f)
        elif arg is not None:
            raise ValueError("Cannot interpret argument of type %s" % type(arg))        

    def __contains__(self, string item):
        return self.thisptr.count(item) > 0

现在,我有另一个.pyx脚本,我正在测试这个功能,我在其中定义一个python字符串,将字节分配给C ++字符串,并尝试使用in运算符:

from libcpp.string cimport string

def test():
    m = MyType()
    bytes_key = 'asdf'
    bytes_val = 'jkl;'
    cdef string key = bytes_key
    cdef string val = bytes_val
    m[key] = val
    print('len(): %d' % len(m))
    assert len(m) == 1, len(m)
    print('__repr__(): %r' % (m, ))
    assert key in m

如果我注释掉最后一行,那么一切都会编译,我会得到输出

len(): 0
len(): 1
__repr__(): {'asdf': 'jkl;'}

但是,如果包含assert语句,我在编译期间会收到以下错误:

Error compiling Cython file:
------------------------------------------------------------
...
    cdef string val = bytes_val
    m[key] = val
    print('len(): %d' % len(m))
    assert len(m) == 1, len(m)
    print('__repr__(): %r' % (m, ))
    assert key in m
              ^
------------------------------------------------------------

test_internal.pyx:72:15: Invalid types for 'in' (string, MyType)

如果我将libcpp.string.string替换为libc.stdint.uint16_t,那么一切正常。正在寻找如何解决这个问题。谢谢!

EDIT 为了进一步解开这个谜团,如果我将违规行更改为assert m.__contains__(key),它会编译并运行正常。

但是,如果我然后转到另一个目录,然后重新导入MyType,请尝试if not my_obj.__contains__(key)key cdefstring' d为TypeError: an integer is required ),我收到运行时错误,SELECT * FROM Visit_History ORDER BY Ref_Num, Ref_Date ...

1 个答案:

答案 0 :(得分:3)

好的,我设法让您的代码正常运行。 我不确定我知道你的问题来自哪里:我唯一一次得到关于所需整数的错误就是我实现了__setitem__方法......

但是,我认为修改这样的功能应该可以解决问题:

def __contains__(MyType self, item):
    if not isinstance(item, bytes):
        item = bytes(item, "UTF-8")
    return (self.thisptr.count(item) > 0)

在python 3中,strbytes之间存在差异(前者无法转换为c ++ string)因此您需要注意转换:ask for {{ 1}}参数中的类型可能会比你更麻烦。

如果这还不够,这里有一个mwe:

string

decl.pxd

from libcpp.map cimport map as my_type from libcpp.string cimport string cdef class MyType: cdef my_type[string, string]* thisptr

decl.pyx

编辑: cdef class MyType: def __cinit__(MyType self, arg=None): self.thisptr = new my_type[string, string]() def __contains__(MyType self, item): if not isinstance(item, bytes): item = bytes(item, "UTF-8") return (self.thisptr.count(item) > 0) def __setitem__(MyType self, key, value): if not isinstance(key, bytes): key = bytes(key, "UTF-8") if not isinstance(value, bytes): value = bytes(value, "UTF-8") self.thisptr[0][key] = value def __len__(MyType self): return self.thisptr.size() (我将setup.py重命名为test.pyx

ctest.pyx

EDIT2:当然,我忘了通知您,但您不应该使用c ++ from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext setup( cmdclass = {'build_ext': build_ext}, package_data = { '': ['*.pxd'] }, ext_modules = [ Extension("ctest", ["ctest.pyx"], language='c++', include_dir=["."]), Extension("decl", ["decl.pyx"], language='c++', include_dir=["."])] ) 作为密钥:

string