Python 3.6.1中的一个简单片段:
import datetime
j = iter(datetime.datetime.now, None)
next(j)
返回:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
而不是使用每个now()
打印出经典的next()
行为。
我见过类似的代码在Python 3.3中运行,我在版本3.6.1中遗漏了什么或者有什么变化?
答案 0 :(得分:43)
这绝对是Python 3.6.0b1中引入的错误。最近iter()
实施已切换为使用_PyObject_FastCall()
(优化,请参阅issue 27128),而且此调用必须打破这一点。
同样的问题与Argument Clinic解析支持的其他C classmethod
方法有关:
>>> from asyncio import Task
>>> Task.all_tasks()
set()
>>> next(iter(Task.all_tasks, None))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
如果需要解决方法,请将callable包装在functools.partial()
对象中:
from functools import partial
j = iter(partial(datetime.datetime.now), None)
我向Python项目提交了issue 30524 -- iter(classmethod, sentinel) broken for Argument Clinic class methods?。对此的修复已落地,并且是3.6.2rc1的一部分。
答案 1 :(得分:16)
我假设你正在使用CPython而不是另一个Python实现。我可以用CPython 3.6.1重现这个问题(我没有PyPy,Jython,IronPython,......所以我不能检查这些)。
在这种情况下,违规者将PyObject_Call
替换为callable_iterator.__next__
与callable_iterator
(您的对象为PyObject_Call
)方法的C等效项。
datetime.datetime
会返回新的_PyObject_CallNoArg
实例,而NULL
会返回_PyObject_CallNoArg
(这大致相当于Python中的异常)。
通过CPython源代码挖掘一下:
C
只是_PyObject_CallNoArg
的一个宏,后者又是_PyObject_FastCall
的宏。
_PyObject_FastCallDict
检查函数的类型(datetime.now
- 函数或Python函数或其他东西)并在这种情况下委托给This _PyObject_FastCallDict
function因为datetime.datetime.now
是一个C函数。
由于METH_FASTCALL
具有case
标记,因此它会在第四个NULL
中结束,但_PyCFunction_FastCallDict
会返回CFunction
,并且该函数甚至不会被调用。< / p>
我会停在那里让Python开发人员弄清楚那里有什么错误。 @Martijn Pieters已经提交了一份Bug报告,他们会修复它(我希望他们能很快修复它)。
所以它是他们在3.6中引入的一个Bug,直到它被修复,你需要确保该方法不是带有METH_FASTCALL
标志的def now():
return datetime.datetime.now()
j = iter(now, None)
next(j) # datetime.datetime(2017, 5, 31, 14, 23, 1, 95999)
。作为解决方法,您可以包装它。除了@Martijn Pieters提到的可能性之外,还有一个简单的:
{{1}}