iter()不使用datetime.now()

时间:2017-05-31 11:27:02

标签: python datetime python-3.6 iterable

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中遗漏了什么或者有什么变化?

2 个答案:

答案 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}}