2017-05-31 9 views
43

のPython 3.6.1で簡単スニペット:ITER()datetime.nowで動作していない()

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で変更されていますか?

+2

私はこれがうまくいくと思います。それは3.4と3.5でも使えます。 –

+2

'datetime.datetime.now'を' lambda:datetime.datetime.now() 'または' partial(datetime.datetime.now) 'に置き換えると動作します。 –

+2

私はあなたの[バグトラッカー](https://bugs.python.org/)でこれを報告すべきだと思います。 – MSeifert

答えて

43

これは間違いなく、Python 3.6.0b1で導入されたバグです。 iter()実装は最近、_PyObject_FastCall()(最適化、issue 27128を参照)を使用して切り替えられました。これを破るこの呼び出しでなければなりません。

引数クリニックの解析に裏打ちされた他のC classmethodの方法と同じ問題のarrises:

>>> 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の一部です。

+9

+1ですが、興味のある点として、なぜあなたのコメントのような 'lambda 'のようなものに' partial'を好むのですか? – erip

+10

@erip:*高速ですので*。作成されるPythonフレームはなく、C実装は 'datetime.datetime.now'というC関数を直接呼び出すことができます。 –

16

あなたはCPythonを使用していて、別のPython実装ではないと仮定します。そして、私はCPython 3.6.1でこの問題を再現できます(私はPyPy、Jython、IronPythonを持っていないので、これらはチェックできません)。

この場合の犯罪者は、(あなたのオブジェクトはcallable_iterator)のCに相当するのPyObject_Callで置き換えられます。

PyObject_Calldatetime.datetimeインスタンスを返しますが、_PyObject_CallNoArgNULL(これはPythonの例外とほぼ同じです)を返します。

_PyObject_CallNoArgが順番に_PyObject_FastCallDictマクロある_PyObject_FastCallためだけのマクロがある:CPythonのソースコードを介してビットを掘る

This _PyObject_FastCallDict functionチェック関数(C -functionまたはPython関数または何か)の種類及び委任この場合_PyCFunction_FastCallDictからdatetime.nowは、C関数であるからです。 datetime.datetime.now以来

は、それが第四caseで終わるMETH_FASTCALLフラグを持っていますが、そこ_PyStack_UnpackDict戻りNULLと機能であっても呼び出されることはありません。

私はそこで停止し、Pythonの開発者に何が間違っているのか理解させてもらいます。 @Martijn Pietersは既にBugレポートを提出しており、修正する予定です(私はただちに修正することを願っています)。

これは3.6で導入されたバグです。固定されるまでは、CFunctionMETH_FASTCALLフラグが設定されていないことを確認する必要があります。回避策として、それをラップすることができます。可能性を除けば@Martijn Pietersもシンプルです:

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

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

偉大な答え、あまりにも犯人の洞察力を追加しました。彼が既にPythonプロジェクトで問題を提出しているので、私はMartijnの答えに受け入れられたマークを保持します。 – Vidak

+3

FASTCALLの最適化のバグであり、Victor Stinnerはhttps://github.com/python/cpython/pull/1886で予備的なパッチを出しています –

関連する問題