CPython:有没有办法将PyObject作为序列遍历而不必随后创建和销毁对象?

时间:2017-05-05 18:03:49

标签: python cpython

要创建一个充当序列的PyObject,我只是在sq_item的变量tp_as_sequence的插槽PyTypeObject中添加了一个函数。

这是我的sq_item功能:

static PyObject *py_myseq__sq_item(PyMySeq *self, unsigned int keynum)
{
    if (keynum < 0) keynum += self->len; /* ex.:>>> my_seq[-1] */
    if (keynum >= 0 && keynum < self->len) {
        MyItem *item = &self->items[keynum];

        return PyMyItem_New(item);
    }

    PyErr_Format(PyExc_IndexError,
                 "PyMySeq[index]: index %d out of range", keynum);
    return NULL;
}

每次我想获得序列的项目时,都会调用PyMyItem_New函数。在大多数情况下这很好。但是对于像使用for循环这样的情况,这是非常低效的:

for i in myPyObjSeq:
    print(i)

如果我的序列有一百万个项目。这个项目将在这个循环中创建和销毁100万次!!!

问题是:有没有办法避免这种情况?

1 个答案:

答案 0 :(得分:2)

您有几个选择:

  • 首先,在CPython API中,分配和释放许多小对象很常见。 CPython有一个针对此优化的分配器,因此通常无法避免。
    See this question for details.
  • 如果你需要遍历数百万个项目,你可能需要考虑实现一个迭代器,这样你就可以循环遍历项目但不会一次性分配所有项目。
  • 另一个选项,(不是Pythonic) - 是在你的序列上有一个可调用的方法。类似于如何将回调传递给list.sort(key=function)
    在这种情况下,您可以将相同的对象传递给每个函数,并使用修改后的索引。不要被愚弄,调用函数创建PyObject也是如此!
  • 如果数据是原始C结构,则可以使用缓冲区接口公开,也可以参见memory-views。
  • 您可以随时使用单个Python对象进行就地修改,但这会导致您的API用户有些困惑,因为他们可能会访问索引并且没有意识到进一步的访问会更改其他变量(不良做法) ,不要这样做`)