44 votes

iter () ne fonctionne pas avec datetime.now ()

Un simple extrait de code dans Python 3.6.1:

 import datetime
j = iter(datetime.datetime.now, None)
next(j)
 

résultats:

 Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
 

au lieu d’imprimer le comportement classique now() avec chacun next() .

J'ai vu du code similaire fonctionner dans Python 3.3, est-ce que quelque chose me manque ou quelque chose a changé dans la version 3.6.1?

43voto

Martijn Pieters Points 271458

C'est certainement un bug introduit en Python 3.6.0b1. L' iter() mise en œuvre est récemment passé à l'aide d' _PyObject_FastCall() (une optimisation, voir question 27128), et il doit être cet appel qui est en train de rompre cette.

La même question arrises avec d'autres C classmethod méthodes soutenu par l'Argument de la Clinique de l'analyse:

>>> 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 vous avez besoin d'un travail autour de, envelopper le callable en functools.partial() objet:

from functools import partial

j = iter(partial(datetime.datetime.now), None)

J'ai déposé question 30524 -- iter(classmethod, sentinel) cassé pour l'Argument de la Clinique des méthodes de la classe? avec le projet de Python. Le correctif de ce qui a débarqué et fait partie de 3.6.2rc1.

16voto

MSeifert Points 6307

Je suppose que vous êtes à l'aide Disponible et pas un autre Python de mise en œuvre. Et je peux reproduire le problème avec Disponible 3.6.1 (je n'ai pas de PyPy, Jython, IronPython, ... donc je ne peux pas vérifier ces).

Le délinquant dans ce cas est le remplacement de l' PyObject_Call avec _PyObject_CallNoArg dans le C équivalent de l' callable_iterator.__next__ (votre objet est un callable_iterator) de la méthode.

L' PyObject_Call t de retour d'un nouveau datetime.datetime tout _PyObject_CallNoArg retours NULL (ce qui est à peu près équivalent à une exception en Python).

En creusant un peu à travers le Disponible le code source:

L' _PyObject_CallNoArg est juste une macro pour _PyObject_FastCall ce qui est une macro pour _PyObject_FastCallDict.

Cette _PyObject_FastCallDict fonction vérifie le type de la fonction (C-fonction ou une fonction Python ou autre chose) et les délégués à l' _PyCFunction_FastCallDict dans ce cas car l' datetime.now est une fonction C.

Depuis datetime.datetime.now a METH_FASTCALL drapeau, il se retrouve dans le quatrième case mais _PyStack_UnpackDict retours NULL et la fonction n'est jamais appelée.

Je vais m'arrêter là et laisser le Python devs comprendre quel est le problème là-dedans. @Martijn Pieters déjà déposé un rapport de Bug et ils vont le réparer (j'espère juste qu'ils le fixer soonish).

Si c'est un Bug qu'ils ont introduit dans la 3.6 et jusqu'à ce qu'il fixe, vous devez vous assurer que la méthode n'est pas un CFunction avec l' METH_FASTCALL drapeau. Comme solution de contournement, vous pouvez l'envelopper. Outre les possibilités de @Martijn Pieters mentionné il y a aussi un simple:

def now():
    return datetime.datetime.now()

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X