iter()不工作datetime.now()

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 

而不是打印出每个next()的经典now()行为。

我已经看到类似的代码在Python 3.3中工作,我错过了什么或有更改3.6.1版本的东西?

这绝对是Python 3.6.0b1中引入的一个bug。 最近iter()实现切换到使用_PyObject_FastCall() (一个优化,请参阅问题27128 ),它必须是这个打破这一点的调用。

同样的问题与由参数诊所分析支持的其他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 

如果您需要解决方法,请将可调用对象包装在functools.partial()对象中:

 from functools import partial j = iter(partial(datetime.datetime.now), None) 

我提出了问题30524 – iter(classmethod,sentinel)为参数诊所类方法打破? 与Python项目。 对此的修复已经降落,并且是3.6.2rc1的一部分。

我假设你正在使用CPython而不是另一个Python实现。 我可以重现CPython 3.6.1(我没有PyPy,Jython,IronPython,…所以我不能检查这些)的问题。

在这种情况下,违规者是用_PyObject_CallNoArg代替callable_iterator.__next__ (你的对象是一个callable_iterator )方法的C等价物中的PyObject_Call

PyObject_Call确实返回一个新的datetime.datetime实例,而_PyObject_CallNoArg返回NULL (这大致相当于Python中的一个exception)。

通过CPython源代码挖掘一下:

_PyObject_CallNoArg只是_PyObject_CallNoArg的一个macros,它是_PyObject_FastCallDict一个macros。

在这种情况下, _PyObject_FastCallDict函数检查函数的types( C函数或Python函数或其他),并委托给_PyCFunction_FastCallDict ,因为datetime.now是一个C函数。

由于datetime.datetime.now具有METH_FASTCALL标志,所以它在第四种case结束,但是_PyStack_UnpackDict返回NULL并且该函数甚至不会被调用。

我会在那里停下来,让Python开发者找出那里有什么问题。 @Martijn彼得斯已经提交了一个错误报告,他们将解决它(我只是希望他们早日修复)。

所以这是他们在3.6中引入的一个Bug,直到它解决了,你需要确保该方法不是带有METH_FASTCALL标志的CFunction 。 作为解决方法,您可以包装它。 除了@Martijn彼得斯提到的可能性,还有一个简单的:

 def now(): return datetime.datetime.now() j = iter(now, None) next(j) # datetime.datetime(2017, 5, 31, 14, 23, 1, 95999)