python对象不是iterable或subscriptab

2024-05-03 10:15:35 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个用cython包装的c++代码,可以从python中使用。在

演示示例

在食品xcd.pxd在

cdef extern from "Foos.hpp":
    cdef cppclass Foos

在美食x.pyx在

^{pr2}$

函数get_foo在cpp或python中都返回一个类对象。返回的对象是Foos类型的,它是Foo类型的对象的集合。我可以将返回值存储在Pyfoos()类型的变量a中。但是,我不能从python中的foos对象迭代单个foo。但是我可以访问 A=get_foos(“*”) 甲[5] 或者

for(auto x:A){print x;} 

在c++中,我想在python中添加一些必要的东西,使类成为iterable或subscribable。在

注意:我知道get_foos()返回一个foos()类型的对象,它是Foo的集合,但不知道我在这里是否准确地表示了它。另外,我无法访问cpp函数,但可以保证get_foos()将返回正确的对象[这是对象的集合]。但是,我不知道打包对象Foos的结构

Python

>> import foo
>> A = Pyfoos()
>> A = get_foos("*")
>> A[2]
TypeError: "Foos" object is not subscriptable
>> for x in A:
...    print(x)
...
TypeError: "Foos" object is not iterable

鉴于我无法控制cpp/hpp文件。但我从他们那里得到了目标。如何使它们在for循环中可编辑或直接订阅?在

我得到了这个错误 类型错误:'福布斯。皮福斯'对象不可编辑

我想调用foos中每个foo的x.bar属性

谢谢


Tags: 对象函数类型forgetfooiterablecpp
1条回答
网友
1楼 · 发布于 2024-05-03 10:15:35

简而言之:您需要实现__iter__(以创建Python iterable),或者为Pyfoos实现__getitem__和{}。你不需要同时做这两件事。在

<>你没有提供足够的C++界面细节来确切地知道它是什么,所以我猜了一下。我提供的示例可以独立工作,但是它可能与您的实际界面不完全匹配。这是你的问题。。。我们对C++接口的了解是:^ {CD5>}:

  • 有一个operator[],它可能返回一个Foo&。(我们之所以知道这一点,是因为您声称能够做到A[0]
  • 具有返回某种迭代器类型的beginend函数。(我们知道这一点是因为您声称能够在for (auto x: A)样式循环中使用它)。不幸的是,auto不能与Cython一起工作,所以我假设这个类型被称为FooIter。在

我还假设Foos有一个成员函数size_t size()

为了创建一个快速测试用例,我创建了下面的c++文件。我为vector使用了typedef以节省时间,因为它公开了完全正确的接口:

#include <vector>

class Foo {
public:
    Foo(int v): val(v) {}
    int bar() { return val; }
private:
    int val;
};

typedef std::vector<Foo> Foos;
typedef Foos::iterator FooIter;

inline Foos get_foos() {
    return Foos{ Foo(1), Foo(2), Foo(3) };
}

然后我们将这个接口的相关部分公开给Cython

^{pr2}$

然后我们为Foo创建一个Python包装器。它包含一个指针,但实际上并不拥有该对象。相反,它引用了拥有Foo(可能是PyFoos)的Python对象,以确保它适当地保持活动状态:

cdef class PyFoo:
    cdef Foo* thisptr
    cdef object owner
    def bar(self):
        return self.thisptr.bar()

然后我们创建PyFoos,容器包装:

cdef class Pyfoos:
    cdef Foos thisobj

def py_get_foos():
    cdef Foos foox = get_foos()
    cdef Pyfoos fooy = Pyfoos()
    fooy.thisobj = foox # note this is copied, not moved. This may be expensive
    return fooy

实现^ {CD2>}/^ {CD3>}接口,只需使用C++ ^ {< CD6>}和^ {CD24>}:

# add this to `PyFoos`:

def __len__(self):
    return self.thisobj.size()
def __getitem__(self,int n):
    if n>=self.thisobj.size():
        raise IndexError()
    cdef Foo* f = &self.thisobj[n]
    py_f = PyFoo()
    py_f.thisptr = f
    py_f.owner = self # ensure that a reference to self is kept while py_f exists
    return py_f

如果您想实现__iter__接口,那么您可以创建一个迭代器类。这个类采用与PyFoo相同的所有权方法(即,它保持一个引用以确保Pyfoos对象保持活动状态)。它使用begin()end()函数来获得迭代器,然后递增迭代器直到到达end()dereference执行*iter,它获取迭代器指向的Foo的引用。在

cdef class PyFooIter:
    cdef FooIter i
    cdef FooIter end
    cdef object owner

    def __init__(self, Pyfoos foos):
        self.i = foos.thisobj.begin()
        self.end = foos.thisobj.end()
        self.owner = foos # ensure foos is kept alive until we are done

    def __iter__(self):
        return self

    def __next__(self):
        cdef Foo* f
        if self.i==self.end:
            raise StopIteration()
        f = &dereference(self.i)
        py_f = PyFoo()
        py_f.thisptr = f
        py_f.owner = self

        preincrement(self.i) # ++(self.i)
        return py_f

您还需要将以下代码添加到Pyfoos

def __iter__(self):
    return PyFooIter(self)

这些可以用

for f in PyFooWrapper.py_get_foos():
    print(f.bar())

它按预期打印了123(在单独的行上)。在

相关问题 更多 >