iter() 不适用于 datetime.now()
iter() not working with datetime.now()
Python 3.6.1 中的一个简单片段:
import datetime
j = iter(datetime.datetime.now, None)
next(j)
returns:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
而不是打印出每个 next()
的经典 now()
行为。
我在 Python 3.3 中看到过类似的代码,我是不是遗漏了什么或者在 3.6.1 版中有什么变化?
这绝对是 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
如果您需要解决方法,请将可调用对象包装在 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 的一部分。
我假设您使用的是 CPython 而不是另一个 Python 实现。我可以用 CPython 3.6.1 重现这个问题(我没有 PyPy、Jython、IronPython,...所以我无法检查这些)。
这种情况下的违规者是 callable_iterator.__next__
(您的对象是 callable_iterator
)方法的 C 等价物中 PyObject_Call
with _PyObject_CallNoArg
的替换。
PyObject_Call
做 return 一个新的 datetime.datetime
实例,而 _PyObject_CallNoArg
returns NULL
(这大致相当于一个异常在 Python).
深入了解 CPython 源代码:
_PyObject_CallNoArg
只是 _PyObject_FastCall
which in turn is a macro for _PyObject_FastCallDict
的一个宏。
This _PyObject_FastCallDict
function checks the type of the function (C
-function or Python function or something else) and delegates to _PyCFunction_FastCallDict
在这种情况下,因为 datetime.now
是一个 C 函数。
因为 datetime.datetime.now
有 METH_FASTCALL
标志,所以它在第四个 case
结束,但是 _PyStack_UnpackDict
returns NULL
和甚至从未调用函数。
我会就此打住,让 Python 开发人员弄清楚那里出了什么问题。 @Martijn Pieters 已经提交了错误报告,他们会修复它(我只是希望他们尽快修复它)。
所以这是他们在 3.6 中引入的错误,在修复之前,您需要确保该方法不是带有 METH_FASTCALL
标志的 CFunction
。作为解决方法,您可以包装它。除了@Martijn Pieters 提到的可能性之外,还有一个简单的:
def now():
return datetime.datetime.now()
j = iter(now, None)
next(j) # datetime.datetime(2017, 5, 31, 14, 23, 1, 95999)
Python 3.6.1 中的一个简单片段:
import datetime
j = iter(datetime.datetime.now, None)
next(j)
returns:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
而不是打印出每个 next()
的经典 now()
行为。
我在 Python 3.3 中看到过类似的代码,我是不是遗漏了什么或者在 3.6.1 版中有什么变化?
这绝对是 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
如果您需要解决方法,请将可调用对象包装在 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 的一部分。
我假设您使用的是 CPython 而不是另一个 Python 实现。我可以用 CPython 3.6.1 重现这个问题(我没有 PyPy、Jython、IronPython,...所以我无法检查这些)。
这种情况下的违规者是 callable_iterator.__next__
(您的对象是 callable_iterator
)方法的 C 等价物中 PyObject_Call
with _PyObject_CallNoArg
的替换。
PyObject_Call
做 return 一个新的 datetime.datetime
实例,而 _PyObject_CallNoArg
returns NULL
(这大致相当于一个异常在 Python).
深入了解 CPython 源代码:
_PyObject_CallNoArg
只是 _PyObject_FastCall
which in turn is a macro for _PyObject_FastCallDict
的一个宏。
This _PyObject_FastCallDict
function checks the type of the function (C
-function or Python function or something else) and delegates to _PyCFunction_FastCallDict
在这种情况下,因为 datetime.now
是一个 C 函数。
因为 datetime.datetime.now
有 METH_FASTCALL
标志,所以它在第四个 case
结束,但是 _PyStack_UnpackDict
returns NULL
和甚至从未调用函数。
我会就此打住,让 Python 开发人员弄清楚那里出了什么问题。 @Martijn Pieters 已经提交了错误报告,他们会修复它(我只是希望他们尽快修复它)。
所以这是他们在 3.6 中引入的错误,在修复之前,您需要确保该方法不是带有 METH_FASTCALL
标志的 CFunction
。作为解决方法,您可以包装它。除了@Martijn Pieters 提到的可能性之外,还有一个简单的:
def now():
return datetime.datetime.now()
j = iter(now, None)
next(j) # datetime.datetime(2017, 5, 31, 14, 23, 1, 95999)