python 对象不可迭代或不可下标

Posted

技术标签:

【中文标题】python 对象不可迭代或不可下标【英文标题】:python object is not iterable or subscriptable 【发布时间】:2017-10-20 18:30:19 【问题描述】:

我已经用 cython 封装了 c++ 代码,以便在 python 中使用。

演示示例

Foopxd.pxd

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

Foopyx.pyx

cdef class Pyfoos:
    cdef Foos thisobj

def get_foos(pattern):
    cdef Foos foox
    foox = Foopxd.get_foos(pattern)
    fooy = Pyfoos()
    fooy.thisobj = foox
    return fooy

函数get_foo 在 cpp 或 python 中返回一个类对象。返回的对象是Foos 类型,它是Foo 类型对象的集合。我可以将返回值存储在Pyfoos() 类型的变量A 中。但是,我无法从 python 中的对象foos 中遍历单个foo。但是我可以访问

A = get_foos("*")
A[5] 

for(auto x:A)print x; 

来自 C++

我想在 python 中添加所需的内容以使类可迭代或可下标。

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

蟒蛇

>> 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 循环中可迭代或可直接下标?

我收到了这个错误

TypeError: 'Foopxd.Pyfoos' object is not iterable

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

【问题讨论】:

这显然不是minimal reproducible example。当您的代码不包含名为 Foo 的类型时,为什么会得到 TypeError: "Foo" object is not subscriptable 好的。但仍然存在基本问题:您有一个名为Foopxd.get_foos 的函数,我们对此一无所知,但它似乎按值返回Foos。我们对Foos 几乎一无所知,除了它可能是一个不同类的容器(foo?),而且我们应该知道如何迭代这个神秘的容器。最重要的是,您的编辑仍然没有显示您的 Python 代码会产生的错误。这是无法回答的。 【参考方案1】:

简而言之:您需要实现 __iter__(创建 Python 可迭代对象)或 __getitem____len__ 实现 Pyfoos。你不需要两者都做。

您没有提供有关您的 C++ 接口的足够详细信息,无法确切知道它是什么,所以我猜测了一下。我提供的示例可以独立运行,但它可能与您的真实界面不完全匹配。这是你的问题......我们所知道的关于你的 C++ 接口的是 Foos:

有一个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

#cython: language=c++
# distutils: include_dirs=<some path>

from cython.operator import dereference, preincrement
from libcpp cimport bool

cdef extern from "cpp_interface.hpp":
    cdef cppclass Foo:
        int bar()

    cdef cppclass FooIter:
        Foo& operator*()
        bool operator==(const FooIter&)
        FooIter operator++()

    cdef cppclass Foos:
        FooIter begin()
        FooIter end()
        Foo& operator[](size_t n)
        size_t size()

    Foos get_foos()

然后我们为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

要实现__getitem__/__len__ 接口,您只需使用C++ operator[]size()

# 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 对象保持活动状态)。它使用Foosbegin()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())

按预期打印 1 2 3(在单独的行上)。

【讨论】:

以上是关于python 对象不可迭代或不可下标的主要内容,如果未能解决你的问题,请参考以下文章

Ode 集成器 Python TypeError 'float' 对象不可下标

Python TypeError:“_asyncio.Future”对象不可下标

在我尝试使用 python 解决的迷宫问题中,显示类型错误 int 对象在老鼠中不可下标

TypeError:“NoneType”对象在函数中不可下标

keras.layers.Concatenate 给出 'NoneType' 对象不可下标

函数对象不可下标