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)