example - strftime python
iter() no funciona con datetime.now() (2)
Este es definitivamente un error introducido en Python 3.6.0b1. La implementación iter()
recientemente cambió a usar _PyObject_FastCall()
(una optimización, ver el número 27128 ), y debe ser esta llamada la que está rompiendo esto.
El mismo problema surge con otros métodos de classmethod
C respaldados por el análisis de Argument Clinic:
>>> 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
Si necesita una functools.partial()
el invocable en un objeto functools.partial()
:
from functools import partial
j = iter(partial(datetime.datetime.now), None)
Archivé el problema 30524 - iter (classmethod, sentinel) roto para los métodos de clase de Argument Clinic? con el proyecto Python. La solución para esto ha aterrizado y es parte de 3.6.2rc1.
Un fragmento simple en Python 3.6.1:
import datetime
j = iter(datetime.datetime.now, None)
next(j)
devoluciones:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
en lugar de imprimir el comportamiento clásico de now()
con cada next()
.
He visto código similar trabajando en Python 3.3, ¿me falta algo o algo ha cambiado en la versión 3.6.1?
Supongo que estás usando CPython y no otra implementación de Python. Y puedo reproducir el problema con CPython 3.6.1 (no tengo PyPy, Jython, IronPython, ... así que no puedo verificar esto).
El infractor en este caso es el reemplazo de PyObject_Call
con _PyObject_CallNoArg
en el equivalente en C del callable_iterator.__next__
(su objeto es un callable_iterator
).
PyObject_Call
devuelve una nueva instancia de datetime.datetime
mientras _PyObject_CallNoArg
devuelve NULL
(que es más o menos equivalente a una excepción en Python).
Excavando un poco a través del código fuente CPython:
_PyObject_CallNoArg
es solo una macro para _PyObject_FastCall
que a su vez es una macro para _PyObject_FastCallDict
.
Esta función _PyObject_FastCallDict
comprueba el tipo de la función (función C
o función de Python u otra cosa) y delega a _PyCFunction_FastCallDict
en este caso porque datetime.now
es una función C.
Dado que datetime.datetime.now
tiene el indicador METH_FASTCALL
, termina en el cuarto case
pero allí _PyStack_UnpackDict
devuelve NULL
y la función nunca se llama.
Me detendré allí y dejaré que los desarrolladores de Python se den cuenta de lo que está mal allí. @Martijn Pieters ya presentó un informe de error y lo arreglarán (solo espero que lo arreglen pronto).
Entonces, es un error que introdujeron en 3.6 y hasta que se solucione, debes asegurarte de que el método no sea una CFunction
con el indicador METH_FASTCALL
. Como solución alternativa puede envolverlo. Además de las posibilidades que mencionó @Martijn Pieters, también hay un simple:
def now():
return datetime.datetime.now()
j = iter(now, None)
next(j) # datetime.datetime(2017, 5, 31, 14, 23, 1, 95999)