Help us understand the problem. What is going on with this article?

Pythonのstaticmethodはどう実装されているのか?

同僚と「デコレータの実装って難しい」という話から、「Pythonの組み込みのデコレータ、例えば staticmethod はどう実装されているの?」という話になりました。私もけっこう長い間Pythonを使っているのですが、そういえば挙動を知らなかったので調べてみることにします。

話していた間は、selfを穴埋めするような実装(このドキュメントにあるような以下の実装)を想定していたのですが、ただオーバーヘッドを作っているだけなので、もっと良い実装をしているはずです。

# Using the non-data descriptor protocol, a pure Python version of :func:`staticmethod` would look like this:

class StaticMethod:
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f

staticmethodとは

次のコードのように、クラス定義のメソッドの self を省略するためのデコレータです。詳しくは公式ドキュメントを読んでください

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

ちなみに私自身はあまり staticmethod は使いません。スタティックメソッドは要らない子?という記事も2010年の時点で書かれている不憫な印象があります😅

ドキュメントによると

組み込み関数のドキュメントには以下のように記載されています。

静的メソッドについて詳しい情報は 標準型の階層 を参照してください。

リンク先のドキュメントにはこのように書かれています。

静的メソッド (static method) オブジェクト

静的メソッドは、上で説明したような関数オブジェクトからメソッドオブジェクトへの変換を阻止するための方法を提供します。静的メソッドオブジェクトは他の何らかのオブジェクト、通常はユーザ定義メソッドオブジェクトを包むラッパです。静的メソッドをクラスやクラスインスタンスから取得すると、実際に返されるオブジェクトはラップされたオブジェクトになり、それ以上は変換の対象にはなりません。静的メソッドオブジェクトは通常呼び出し可能なオブジェクトをラップしますが、静的オブジェクト自体は呼び出すことができません。静的オブジェクトは組み込みコンストラクタ staticmethod() で生成されます。

どうやら通常のメソッド定義では「クラス内で関数からメソッドに変換」していて、 staticmethod でラップするとその変換が行われないようです。

実際に試したところ、現状のPython3.8では次のような挙動になっています。

# メソッドと静的メソッドを持つクラスを定義
class C:                                                                                                                                    
    def method(self):
        pass

    @staticmethod
    def stmethod(self):
        pass

# クラスの場合は両方ともfunction
print(type(C.method))
# => <class 'function'>
print(type(C.stmethod))
# => <class 'function'>

# インスタンスの場合はメソッドはmethodに変換されていて、静的メソッドはfunctionのまま
c = C()
print(type(c.method))
# => <class 'method'>
print(type(c.stmethod))
# => <class 'function'>

ただ、そのすぐ上、「内部型」のドキュメントには以下のようにも書かれています。

これらの定義は将来のインタプリタのバージョンでは変更される可能性がありますが、ここでは記述の完全性のために触れておきます。

実際のコードを見てみようと思ってCPythonのリポジトリを見たのですが、構造体の定義箇所を見つけた以上は読み解けませんでした。ここから実際に初期化されるときに上記のような挙動に実装されているのが確認できたら面白かったのですが…。

まとめ

「(現行のPython3.8では)クラス内で定義された関数が、methodに変換されており、staticmethod でラップするとその変換処理を打ち消すことができる」ようです。ただ、過去のバージョンや、今後のバージョンでは実装方法が違う可能性があります。

CPythonの実装がどうなっているのか、もし詳しく調べられる方がいたらコメントを頂けると嬉しいです。

ninomiyt
株式会社LIFULLの愉快なPythonistaです
https://medium.com/@ninomiyt
lifull
日本最大級の不動産・住宅情報サイト「LIFULL HOME'S」を始め、人々の生活に寄り添う様々な情報サービス事業を展開しています。
https://lifull.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした