Pythonをやっていれば必ず直面する「self」。クラスの中で関数を定義するときに引数でselfを渡すことを習慣的に記述している人も多いかと思います。
ここでは、一歩踏み込んで、このselfとは何か?どういった場合にエラーが発生するのか、どんな役割をしているのかをまとめています。
実は、習慣的にselfを使っていますが、selfでなくともエラーが発生することなく動いたりもします。
検証内容
ここでは以下のパターンを実例を用いて検証しています。
- selfあり・なしでの動作の違い
- インスタンスを生成しない場合との関数呼び出しの違い
- スタティックメソッドにした場合
- self以外の引数を渡した場合
- クラスの外で関数を定義した場合との違い
- クラス内の関数の処理で引数使う場合
- クラス内で定義した変数を使う場合
selfあり・なしでの動作の違い
self有無で動作の違いをみるために、下記のようなクラスを作成。
selfありの場合
クラス名 class1の中で、method1という関数を定義。文字列をprintするように指定。引数にselfを渡す。
class class1:
def method1(self):
print("method1を実行しました")
obj = class1()
obj.method1()
#出力
# method1を実行しました
引数にselfを渡している場合、エラーが発生せず問題なく実行できています。
インスタンスの生成とメソッドの実行
- Pythonでクラスからインスタンスを生成するには、クラス名() とします。
- 生成したインスタンスのメソッドを実行するには、インスタンス.メソッド名() とします。
※インスタンスを生成せず、クラス名.メソッド名() として実行することもできます。
selfがない場合
クラス内で定義したメソッドに引数をなにも渡さない状態で、インスタンスからメソッドを実行しようとすると、TypeErrorというエラーが発生します。
class class1:
def method1():
print("method1を実行しました")
obj = class1()
obj.method1()
#出力
# TypeError: method1() takes 0 positional arguments but 1 was given
クラスの中に定義したメソッド「method1」の処理内では引数を使っていないのに、1つの引数が必須というエラーが表示されます。
引数(self)がないとエラーになる原因
Pythonはクラスをインスタンスとしてから中のdefで定義した関数を呼び出す場合は、関数(function)としてではなく、メソッド(method)として呼び出します。
このメソッドには、インスタンス自身を引数として渡さなければいけない設定になっているため、インスタンス自身が入る引数を渡す必要があるためです。
このインスタンス自身を入れる引数には習慣としてselfが使われます。
インスタンスを生成しない場合との関数呼び出しの違い
インスタンスを生成する場合と、生成せずにクラス内の関数を呼び出す場合で処理が変わる実例を紹介します。
インスタンスを生成する場合はmethodになる
インスタンスを生成して、その中の関数を呼び出すと、その関数のタイプはmethodになります。
class class1:
def method1(self):
print("method1を実行しました")
obj = class1()
print ( type(obj) )
#出力
#<class '__main__.class1'>
print( type(obj.method1) )
#出力
#<class 'method'>
インスタンスのタイプは ‘main.class1’、 method1のタイプは ‘method’ となっています。
インスタンスを生成しない場合はfunctionになる
インスタンスを生成せずにクラスから関数を直接実行する場合、そのタイプはfunctionになります。
function、つまり、関数として実行する場合は、インスタンス自身を渡す必要がないので、クラス内のdefの定義の中で引数にselfを渡す必要はりません。
class class1:
def method1():
print("method1を実行しました")
print ( type( class1 ) )
#出力
#<class 'type'>
print ( type( class1.method1 ) )
#出力
#<class 'function'>
インスタンスのタイプは ‘type’、 method1のタイプは ‘function’ となり、先ほどのインスタンスを生成した場合と全く別物として扱われていることがわかります。
type関数
Pythonのtype関数を使うと、引数でしてした要素のタイプを出力します。出力は <class ‘タイプ’>となります。
例えば、文字列であれば、<class ‘str’>、整数であれば<class ‘int’>となります。
print( type("A") ) #<class 'str'>
print( type(1) ) #<class 'int'>
type関数の注意点
type関数で関数やメソッドのタイプを調べるときは カッコ()をつけると、タイプがない状態NoneType になってしまいます。
print ( type(class1.method1) ) #<class 'function'>
print ( type(class1.method1()) ) #<class 'NoneType'>
print( type(obj.method1()) ) #<class 'NoneType'>
スタティックメソッドにした場合
クラスの中でdefで関数を定義するときに、スタティックメソッドとして定義すれば、selfがなくても呼び出すことができます。
スタティックメソッドとはクラスに属するメソッドのことで、インスタンスに依存せず、どのインスタンスで実行しても同じ処理を行います。
クラスメソッドや静的メソッドとも呼ばれたりします。
スタティックメソッドの作り方
クラスの中で関数を定義するときに、defの上に @staticmethod を記述するだけです。
この @~ をデコレーターと呼びます。続く処理を装飾(デコレート)するという意味です。
class class1:
@staticmethod
def method1():
print("method1を実行しました")
x = class1()
x.method1()
#出力
# method1を実行しました
インスタンスから引数(self)なしのメソッドを実行することができました。
self以外の引数を渡した場合
インスタンス自身を渡すときは引数に変数を渡せばいいので、その名前はselfである必要はありません。日本語を渡しても問題なく動きます。
self を a に置き換えた場合
class class1:
def method1(a):
print("method1を実行しました")
x = class1()
x.method1()
#出力
# method1を実行しました
self を あいうえお に置き換えた場合
class class1:
def method1(あいうえお):
print("method1を実行しました")
x = class1()
x.method1()
#出力
# method1を実行しました
どちらも問題なく実行できました。
クラスの外で関数を定義した場合との違い
クラスの外で関数を定義した場合はインスタンスを生成せず、その関数を直接指定して実行できるので、引数selfを渡す必要はありません。
なお、クラスの中のdefで定義した関数をメソッドと呼び、クラスの外はグローバル関数と呼びます。
def method2():
print("method2を実行しました")
method2()
#出力
# method2を実行しました
def method2():
print("method2を実行しました")
class class1:
def method1(あいうえお):
print("method1を実行しました")
method2()
#出力
# method2を実行しました
なお、グローバル関数のタイプはfunctionです。
def method2():
print("method2を実行しました")
print( type(method2) )
#出力
# <class 'function'>
※クラス内のメソッドと逆で、引数を渡すとエラーになります。
def method1():
print("method1を実行しました")
method1('A')
#出力
# TypeError: method1() takes 0 positional arguments but 1 was given
エラー内容:method1() takes 0 positional arguments but 1 was given
本来、引数は必要ない(0個)のに1個余分ですというエラー。
クラス内の関数の処理で引数使う場合
クラス内で定義した関数をインスタンスとして使う場合は、デフォルトで1つ引数を設定する必要があります。
このため、処理の中で引数を使う場合は、通常よりも1つ多く設定する必要があります。
▼クラス内のメソッドの引数に「self」「day」「sky」の3つを設定
class class2:
def weather(self, day, sky):
print(day + 'の天気は' + sky + 'です。')
x = class2()
x.weather('今日', '晴れ')
#出力
# 今日の天気は晴れです。
▼「self」を「a」にした場合
もちろん「self」以外でも正常に動きます。
class class2:
def weather(a, day, sky):
print(day + 'の天気は' + sky + 'です。')
x = class2()
x.weather('今日', '晴れ')
#出力
# 今日の天気は晴れです。
クラス内で定義した変数を使う場合
クラスの大きなメリットはクラス内でのみ有効な関数や変数を設定できることです。(グローバルにならない。スコープといいます)
クラス内で定義した変数を、クラス内の処理で呼び出すには引数で渡した変数(self)をオブジェクトとして指定する必要があります。
クラス内の変数を使う
クラス内で定義した変数「x=123」をメソッドの中で呼び出すには「self.x」とします。
class class3:
x = 123
def method1(self):
print(self.x)
cls = class3()
cls.method1()
#出力
# 123
selfをつけない場合はエラーになる
「self.x」のselfがないとエラーになります。
この時のxはグローバル変数か、method1で渡された変数や参照しようとしていますが、存在しないのでエラーになります。
class class3:
x = 123
def method1(self):
print(x)
cls = class3()
cls.method1()
#出力
# NameError: name 'x' is not defined
エラー:name ‘x’ is not defined
クラス内にある「x=123」は「x」では参照できない。
self以外でクラス内のメソッドで変数を呼び出す方法
グローバル変数を使う
クラスの外で定義された変数(グローバル変数という)を使う場合は、引数を渡さずに、直接変数を指定できます。
x = 123
class class3:
def method1(self):
print(x)
cls = class3()
cls.method1()
#出力
# 123
selfを使うことで、グローバル変数なのか、クラス内の変数なのかを明確に使い分けできます。
ちなみに、クラスの内外などに関わらずどこからでも呼び出せるので「グローバル」という名前がついています。
この場合は、グローバル変数よりも、引数で指定された変数の方が優先されます。
self以外の変数を渡す
self以外の変数を渡しても呼び出すことができます。
class class3:
x = 123
def method1(あいうえお):
print(あいうえお.x)
cls = class3()
cls.method1()
#出力
# 123
self以外に引数を定義する
self以外の変数を渡しても呼び出すことができます。
x = 123
class class3:
def method1(self, x):
print(x)
cls = class3()
hello = "こんにちは"
cls.method1(hello)
#出力
#こんにちは
まとめ
先に結論をまとめておくと以下のようになります。
- インスタンスを生成する場合はクラス内のメソッドの引数を1つ設定しなければならない
- 習慣的にselfを使用
- 引数の記述がないとエラーになる
- インスタンスを生成しない場合は引数は必須ではない
- クラスメソッド(staticメソッド)として定義した場合は引数selfは不要
- 渡す引数の名前は任意
- self以外でも動く
- クラス内でstatic変数を使う場合はself.変数とする。
- クラス内のx=123をクラス内で使うなら self.x とする。
- グローバル変数はクラス内でもそのまま使える
- グローバル変数=クラスの外で定義した変数
- self.が不要