静かなる名辞

pythonとプログラミングのこと



【python】missing 1 required positional argument: 'self'などの対処法

はじめに

 pythonに不慣れな方は、よくタイトルのようなエラーを見かけると思います。

 実際には、このエラーはTypeErrorで、全体は以下のようなものです。

TypeError: メソッドの名前 missing 1 required positional argument: 'self'

 では、どうしてこのエラーは出るのでしょうか。そして、どうすれば良いのでしょうか。簡単に解説していきます。なおpython3を使っていることを前提とします。

クラスをインスタンス化していない

 このエラーは、なんとなくエラーの文面だけ見ると、引数を渡してあげれば良いのかな? というような気がします。実際、引数を要求する関数を引数無しで呼び出すとほぼ同じエラーが出ます。

>>> def f(a):
...     pass
... 
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required positional argument: 'a'

 だけど、ライブラリやモジュールのドキュメントをのぞいてもselfという引数に関する記載はなかったりします

 実は、このエラーにはpythonのクラスの言語仕様が関係しています。

 pythonでは、通常のメソッドでは第一引数にオブジェクトのインスタンスそのもの(Javaでいうthis)が渡されてやってくる、という仕様になっています。

 たとえば次のようなコードを試しに実行してみます。

test_hoge.py

class Hoge:
    def print_hoge():
        print("hoge")

h = Hoge()
h.print_hoge()

実行結果

Traceback (most recent call last):
  File "test_hoge.py", line 6, in <module>
    h.print_hoge()
TypeError: print_hoge() takes 0 positional arguments but 1 was given

 print_hoge()は1つも引数を取らない関数として定義されているのに、1つ渡されています、というエラーが出ます。ここで渡されているものがインスタンス自身です。

 インスタンス自身を受け取る名前は、実はどんな名前でも構わないのですが、慣習的にpythonでは「self」を使います。さっきのコードをselfを受け取るように書き換えましょう。

test_hoge.py

class Hoge:
    def print_hoge(self):
        print("hoge")

h = Hoge()
h.print_hoge()

実行結果

hoge

 無事実行できるようになりました。よかったね、という話なのですが、これを今度はインスタンス化しないで呼んでみましょう。

test_hoge.py

class Hoge:
    def print_hoge(self):
        print("hoge")

Hoge.print_hoge()

実行結果

Traceback (most recent call last):
  File "test_hoge.py", line 5, in <module>
    Hoge.print_hoge()
TypeError: print_hoge() missing 1 required positional argument: 'self'

 はい、エラーが再現しました。このような状況になっていた訳ですね。

対策

 このエラーに遭遇した場合、大抵はインスタンス化して使うべきクラスを不適切にインスタンス化せずに使っていたというケースだと思います。インスタンス化してあげましょう。上の例の、ちゃんと実行できるコードを再掲します。

class Hoge:
    def print_hoge(self):
        print("hoge")

h = Hoge()  # この行でインスタンス化
h.print_hoge()

 ただし、自作クラスで「インスタンス自身なんか処理に使わないから、クラスのまま呼べるようにしたい」という場合もあると思います。その場合は、@staticmethodとメソッド宣言の前に付けてあげるとスタティックメソッドを定義できます。
 (「@staticmethod」そのものは特別な記述子とかではなく、pythonでは一般的に使われるデコレータという機能で実装されています。詳細はドキュメントやヘルプを参照してください)

class Hoge:
    @staticmethod  # これによってスタティックメソッドになる
    def print_hoge():
        print("hoge")

Hoge.print_hoge()

 ただし、これはつまるところただの関数と何も違いません。スタティックメソッドを使うメリットはかなり限られた場面にしかありません。だいたい、pythonではクラスを入れ物的に使うということをあまりしません(通常はトップレベル関数として定義し、モジュールで管理する)。本当にそのスタティックメソッド必要? と考え直す視点は常に持っておいた方が良いです。

 なお、似たようなデコレータに@classmethodというというものもありますが、これは第一引数に「クラス自身」が渡されるというもので、スタティックメソッドとはまた別物です。間違えないようにしてください。

色々なパターン

 これに似ているパターンが幾つかあると思うので、カバーしておきます。

引数を受け取るメソッド

 このようなケースです。

test_hoge.py

class Hoge:
    def print_hoge(s):
        print("hoge", s)

h = Hoge()
h.print_hoge("fuga")

実行結果

Traceback (most recent call last):
  File "test_hoge.py", line 6, in <module>
    h.print_hoge("fuga")
TypeError: print_hoge() takes 1 positional argument but 2 were given

 この記事をここまで読んできた皆さんは、もうどうすれば良いかおわかりだと思います。

正しいコード

class Hoge:
    def print_hoge(self, s):
        print("hoge", s)

h = Hoge()
h.print_hoge("fuga")

まとめ

 pythonのクラス周りは他の言語と比べてちょっと独特ですが、「通常のメソッドではインスタンス自身が暗黙的に第一引数に渡される」というルールさえ理解してしまえば、むしろわかりやすいと言えます。「thisが自動的にインスタンス自身を表すキーワードとして機能する」という魔法がない分、一貫性があってすっきりしているからです。


 ただ、理解するまではタイトルのようなエラーにはよく遭遇すると思います。早く覚えてこんなエラーは出さないようにしましょう。