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で何かが変更されていますか?
これは間違いなくPython 3.6.0b1で導入されたバグです。iter()
実装は最近_PyObject_FastCall()
の使用に切り替わりました(最適化。 を参照)問題27128 )、そしてこれを破っているのはこの呼び出しでなければなりません。
同じ問題が、引数クリニックの構文解析に裏打ちされた他の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)
_
私は issue 30524-iter(classmethod、sentinel)broken for Argument Clinic class methods? with Pythonプロジェクト。これに対する修正が適用され、3.6.2rc1の一部になっています。
別のPython実装ではなく、CPythonを使用していると仮定します。CPython3.6.1で問題を再現できます(PyPy、Jython、IronPythonなどがありません...だから私はこれらを確認することはできません)。
この場合の違反者は、CでPyObject_Call
に相当する _PyObject_CallNoArg
を callable_iterator.__next__
に置き換えることです(オブジェクトはcallable_iterator
)メソッド。
PyObject_Call
は新しいdatetime.datetime
インスタンスを返しますが、_PyObject_CallNoArg
はNULL
を返します(これはPythonの例外とほぼ同等です)。
CPythonソースコードを少し掘り下げます。
_PyObject_CallNoArg
は _PyObject_FastCall
の単なるマクロであり、これは _PyObject_FastCallDict
のマクロです。
この_PyObject_FastCallDict
関数 関数のタイプ(C
-関数またはPython関数など)をチェックし、-に委任します _PyCFunction_FastCallDict
この場合、datetime.now
はC関数であるため。
datetime.datetime.now
にはMETH_FASTCALL
フラグがあるため、4番目のcase
になりますが、 _PyStack_UnpackDict
はNULL
と関数を返します呼び出されることさえありません。
ここで停止して、Python開発者にそこで何が問題なのかを理解させます。@ MartijnPietersはすでにバグレポートを提出しており、修正する予定です(すぐに修正されることを願っています)。
したがって、これは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)