staticmethod が callable になりました

github.com

Pythonで定義した関数とCで定義した関数は型が異なり、振る舞いにも幾らかの違いがあります。これがPython実装とC実装の両方を提供する場合に、挙動の一貫性がないという問題になります。

今回問題になったのは、Cで定義した関数は勝手にメソッドにはならないという点です。

>>> def mylen(x): return len(x)
...
>>> class C:
...     len = len
...     mylen = mylen
...
>>> C().len([])
0
>>> C().mylen([])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: mylen() takes 1 positional argument but 2 were given

Python で定義した mylen(x) をクラス変数に代入してインスタンスの属性として呼び出すと、自動的にメソッド扱いされてCのインスタンスx に渡されてしまいます。

インスタンスメソッドやクラスメソッドにしないように @staticmethod を使うと良さそうですが、Python 3.9まで staticmethod はただのディスクリプタで、クラスやインスタンス経由で利用しないといけませんでした。

>>> @staticmethod
... def mylen(x): return len(x)
...
>>> class C:
...     mylen = mylen
...
>>> C().mylen([])
0
>>> mylen([])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'staticmethod' object is not callable

これが先ほどのコミットで変更され、 staticmethod がそのまま呼び出せるようになりました。

Python 3.10.0a7+ (heads/master:553ee2781a, Apr 12 2021, 13:46:28) [Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> @staticmethod
... def mylen(x): return len(x)
...
>>> class C:
...     mylen = mylen
...
>>> C().mylen([])
0
>>> mylen([])
0

Python 3.10a7 はリリース済みなので、この変更が反映されるのは次の Python 3.10b1 からになります。

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。