如何编写能够返回Python迭代器对象的Python代码(Python模块)?
后,我已经在短期内用C写一个简约Python3.6扩展模块成功++(see here)我打算提供一个Python模块,它不一样的下面的Python功能iterUniqueCombos()
:如何编写能够返回Python迭代器对象的Python代码(Python模块)?
def iterUniqueCombos(lstOfSortableItems, sizeOfCombo):
lstOfSortedItems = sorted(lstOfSortableItems)
sizeOfList = len(lstOfSortedItems)
lstComboCandidate = []
def idxNextUnique(idxItemOfList):
idxNextUniqueCandidate = idxItemOfList + 1
while (
idxNextUniqueCandidate < sizeOfList
and
lstOfSortedItems[idxNextUniqueCandidate] == lstOfSortedItems[idxItemOfList]
): # while
idxNextUniqueCandidate += 1
idxNextUnique = idxNextUniqueCandidate
return idxNextUnique
def combinate(idxItemOfList):
if len(lstComboCandidate) == sizeOfCombo:
yield tuple(lstComboCandidate)
elif sizeOfList - idxItemOfList >= sizeOfCombo - len(lstComboCandidate):
lstComboCandidate.append(lstOfSortedItems[idxItemOfList])
yield from combinate(idxItemOfList + 1)
lstComboCandidate.pop()
yield from combinate(idxNextUnique(idxItemOfList))
yield from combinate(0)
我有一些基本的了解Python和C++编程,但绝对不知道如何将Pythons 产生转换为Python扩展模块的C++代码。所以我的问题是:
How to write C++ code (of a Python module) able to return a Python iterator object?
让我开始任何提示的欢迎。
UPDATE(状态2017年5月7日):
两个注释:产量没有C++当量。我首先在Python中手动实现迭代器协议,以摆脱思维模式下的收益率和收益率。 - user2357112 4月26日1:16和danny
在回答这个问题的答案的提示是一样的问:“我该如何实现,而无需使用产量迭代器”,但在C++的扩展,而不是纯Python。为了消除yield
并从头开始编写Python扩展模块的C代码(导致下雨Segmentation Fault
错误),我通过重写算法代码的方式重新编写代码,从而使我的编程工作陷入了错误的方向。
The state-of-the-art of my current knowledge on the subject of the question is that using Cython it is possible to translate the above Python code (which is using
yield
) directly into C code of a Python extension module.
这不仅可以使用Python代码只是因为它是(无需重写任何东西),但除了通过用Cython使用yield
运行在算法创建的扩展模块的速度至少两倍快从所创建的使用__iter__
和__next__
重写算法一个迭代类的扩展模块(如果没有用Cython特定速度优化代码被添加到Python脚本后者是有效的)。
这比一个完整的回答你的问题编辑响应 - 我同意丹尼的回答的要点,你需要在一个类来实现这个一个__next__
/next
方法(取决于Python的版本)。在你的编辑中你断言它一定是可能的,因为Cython可以做到这一点。我认为值得看看Cython究竟是怎么做的。
开始与一个基本的例子(拾取,因为它具有一些不同的yield
语句和一个循环):
def basic_iter(n):
a = 0
b = 5
yield a
a+=3
yield b
b+=2
for i in range(n):
yield a+b+n
a = b+1
b*=2
yield 50
的第一件事用Cython所做的是限定与实现__next__
一个__Pyx_Generator_Next
方法的__pyx_CoroutineObject
C级/ next
。该__pyx_CoroutineObject
的几个相关属性:
-
body
- 实现你所定义的逻辑C函数指针。 -
resume_label
- 一个整数用来记住你多远通过body
-
closure
定义的功能得到了 - 存储内body
使用的所有变量的自定义创建的C类。
在一个稍微迂回的方式,__Pyx_Generator_Next
调用body
属性,这是你已经定义了Python代码的翻译。
让我们再看看功能如何分配给body
作品 - 在我的例子称为__pyx_gb_5iters_2generator
的情况。它做的第一件事就是用resume_label
跳转到正确的yield
声明:
/* a = 0 # <<<<<<<<<<<<<< */
__pyx_cur_scope->__pyx_v_a = __pyx_int_0
yield
设置resume_label
和:
switch (__pyx_generator->resume_label) {
case 0: goto __pyx_L3_first_run;
case 1: goto __pyx_L4_resume_from_yield;
case 2: goto __pyx_L5_resume_from_yield;
case 3: goto __pyx_L8_resume_from_yield;
case 4: goto __pyx_L9_resume_from_yield;
default: /* CPython raises the right error here */
__Pyx_RefNannyFinishContext();
return NULL;
}
任何变量赋值通过closure
结构(本地命名__pyx_cur_scope
完成(resume_label
允许您下次直接跳回):
__pyx_generator->resume_label = 1;
return __pyx_r;
循环稍微复杂一些,但基本上是一样的 - 它使用goto
跳转到C循环(这是合法的)。
最后,一旦它到达最终它提出了一个StopIteration
错误:
PyErr_SetNone(PyExc_StopIteration);
总之,用Cython做你已经被告知该怎么做:它定义了__next__
或next
类方法并使用该类来跟踪状态。因为它是自动化的,所以它能很好地跟踪引用计数,从而避免您遇到的错误Segmentation Fault
。使用goto
返回到前一个执行点是有效的,但需要小心。
我可以看到,为什么在一个__next__
/next
功能方面重写你生成函数C是吸引人,而用Cython明确提供这样做没有用C写你自己的一个简单的方法,但它不使用任何特殊技术在你已经被告知的情况下做翻译。
你写道:“Cython显然提供了一种直接的方式来完成它,而不用自己写C,但它并没有使用任何特殊的技术来完成你已经被告知的翻译。”并且同时如果我已经理解你的答案了),请解释一下Cython用来将Python'yield'翻译成一个合适的C-equivalent的特殊技术:我使用了goto语句来切换迭代状态保持在一个特殊的“封闭”结构中。 – Claudio
你的理解是正确的。我认为我的观点是,这不是你自己写的。我怀疑如果你写了一个可以被多次调用的函数来像Python生成器一样为C程序员工作,他们可能会想出类似的东西(也许避免使用goto ...)。无论如何 - 我大多认为值得关注一下Cython是如何做到这一点的。它会显示一个有用的机制来复制,或者说服你,你宁可不自己写它,你应该只使用Cython(这两个都是好结果) – DavidW
在这种情况下,知道Python解释器在它自己的C代码中执行它。如果Cython只是复制Python本身的功能,我不会感到惊讶。 – Claudio
在Python迭代器是发电机的一种特殊形式,并且由含有方法__iter__
和next
类实现其中__iter__
返回self
和next
返回反过来每个值,在迭代结束提高StopIteration
- see PEP。
为了提供C++等价物,C++代码需要实现那些相同的python函数以符合协议。生成的扩展类型是一个迭代器。
换句话说,在这个问题的答案是一样的问:“如何实现一个迭代器,而无需使用yield
”,但在C++的扩展,而不是纯Python。这个堆栈溢出有几个现有的答案。
NB - next
是__next__
关于Python 3
'yield'没有C++等价物。我会从[手动实现迭代器协议]开始(https://www.python。org/dev/peps/pep-0234 /),以摆脱'yield'和'yield from'的思维模式。 – user2357112