今回は、プログラミングにおいて、__init__
やself
のことを深く理解してない状態だったので、備忘録として残しておきたいと思います。
開発環境
MacOS
Python3.7(anaconda)
VSCode
クラスと self と init
【Python入門】クラスの基本を1から解説する―完全版
Python の super() 関数の使い方
__init__とは
__init__
とは、インスタンスを生成した際に、1番最初に呼び出される関数になります。
class Human:
name = 'Jack'
def __init__(self):
self.name = 'Bob'
boku = Human()
print(boku.name) -> Bob
といった具合に、インスタンス化
が行われた後に、__init__
が呼び出されるため、元々はnameにはJackが入っていましたが、実行した際はBob
が出力されることになります。
追記(2020/12/19) @shiracamusさんより
name='Jack'
のnameはクラス変数であり、self.name='Bob'
のnameはインスタンス変数になります。
なので、boku.name
で呼び出す時には、インスタンス変数の中身であるBob
が呼び出され、Human.name
で呼び出す時には、クラス変数であるJack
が呼び出されます。
そして、もしも、boku.name
で呼び出した際に、インスタンス変数の方のname
が存在しない場合、クラス変数のname='Jack'
が呼び出されるという仕組みになっております。
インスタンス化とは何か
インスタンス化とは、簡単に言えば、型作りのようなものですね。
人間という大雑把な形を作って、Jack
やBob
という個人を作る部分に分かれてくるという具合です。
class Human:
def __init__(self)
self.name1 = 'Jack'
self.name2 = 'Bob'
boku = Human() <- ここがインスタンス化
print(boku.name1) -> Jack
print(boku.name2) -> Bob
といった具合に、boku = Human()
によってインスタンス化が行われます。
インスタンス名 = クラス名()
といった書き方になります。
self.変数とは何か
追記(2020/12/28)
self.変数
に関する説明はしていなかったのと、正直自分も、self.変数
自体はちゃんと理解できていなかったので、備忘録として残しておきたいと思います。
まずは、self
を使わないバージョンを見ていきましょう!
class Numbers:
def __init__(self):
number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def Next(self):
print(number)
numbers = Numbers().Next() ==>> エラー
こちらを実行した場合、エラーになってしまいます。
__init__
の中で定義されたnumberとNext
の中で出力されているnumberは別の物として認識されているのが原因です。
それでは、self
を使ったバージョンを見ていきましょう!
まずは、__init__
の方にだけself
を付けた場合を見ていきましょう!
class Numbers:
def __init__(self):
self.number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def Next(self):
print(number)
numbers = Numbers().Next() ==>> エラー
こちらの方も、__init__
のself.number
とNext
のnumber
は別物であるというワケです。
では、両方にself
を使った場合を見ていきましょう!
class Numbers:
def __init__(self):
self.number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def Next(self):
print(self.number)
numbers = Numbers().Next() ==>> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
こちらは無事に出力されました。
self
を使うことによって、メソッドを跨いだとしても、同じ変数であるということが認識されているワケですね。
self
は1つのクラスの中で同じ変数を使い回したい時に活躍してくれます。
self.変数=変数とは何か
まずは、self.name
に実名を入れていくだけの処理を見ていきます。
class Human:
def __init__(self):
self.name1 = 'Jack'
self.name2 = 'Bob'
boku = Human()
print(boku.name1)
print(boku.name2)
これだと、nameを100人分作る場合は、変数nameを100個
用意しなければいけない状態になります。
次に、self.name=name
を使うバージョン
class Human:
def __init__(self, name):
self.name = name
boku = Human('Jack')
こちらは、Human('Steve')
やHuman('Bob')
に変更することによって、名前を変更することができます。
こちらの処理の方が、よりシンプルに書けて無駄な処理をしなくて良い状態になっております。
クラスとメソッドを使う上での注意点
クラスとその中でメソッドを使う時の注意点を個人的に理解したつもりなので、備忘録として残しておきたいと思います。
まずは、クラス内にある別の関数を呼び出すときは、self.メソッド名
という書き方をします。
この書き方によって、そのクラスのメソッド
という意味になります。
下の例で見てみると、self.Next()
はHumanクラスのNextメソッド
という扱いです。
class Human:
def __init__(self):
self.name1 = 'Jack'
print('私の名前は' + self.name1 + 'です')
def Before(self):
self.name2 = 'Boku'
self.Next()
def Next(self):
self.name1 = 'Mary'
self.name2 = 'Smith'
print('私の名前は' + self.name2 + 'です')
human = Human() ==>> 私の名前はJackです
このような出力結果となります。
別に、呼び出したわけでもないのに、実行されるのが、__init__
になりますね。
インスタンス生成
をした時点で必要な変数などは定義しておくと便利ですね。
次に、Beforeクラス
を呼び出す場合の、処理を書いていきたいと思います。
class Human:
def __init__(self):
self.name1 = 'Jack'
self.name2 = 'Boku'
def Before(self):
self.name3 = self.name1
print('私の名前は' + self.name3 + 'です')
print('私の名前は' + self.name2 + 'です')
def Next(self):
self.name4 = self.name3
self.name2 = 'Smith'
print('私の名前は' + self.name4 + 'です')
self.Before()
human = Human().Next() ==>> エラー
こちらを実行したらエラーになってしまいます。
これを見ると、関数とは、呼び出されて初めて処理が行われる
というのがわかるのではないでしょうか。
Beforeメソッド
は、Nextメソッド
よりも先に定義はされているが、Nextメソッド
の中で呼び出されて初めて実行されるので、self.name4=self.name3
の部分ではself.name3
というのはまだ存在しておらず、エラーになるという状態です。
個人的には、
self.メソッド名()
が書かれた場所から、そのメソッドの処理が書かれている部分に飛んで、メソッド内の処理が行われる。
と、勝手に認識しております。
こちらはもっとわかりやすい認識の方法がございましたら、教えていただけるとありがたいです。
下に示しますが、self.Before()
をself.name4=self.name3
の前に持ってくれば無事に出力されます。
class Human:
def __init__(self):
self.name1 = 'Jack'
self.name2 = 'Boku'
def Before(self):
self.name3 = self.name1
print('私の名前は' + self.name3 + 'です')
print('私の名前は' + self.name2 + 'です')
def Next(self):
self.Before()
self.name4 = self.name3
self.name2 = 'Smith'
print('私の名前は' + self.name4 + 'です')
human = Human().Next() ==>> 私の名前はJackです
私の名前はBokuです
私の名前はJackです
superとは何か
次に、__init__
の後によく見かけるsuper
って何ですかいっていう説明をします。
まず、複数のクラスで同じような処理をする場合は、毎度同じ処理を書くわけではありません。
1番最初に、メインのクラスを書いたら、そのクラスを継承する
ことで、同じ処理の記述を省くことにしています。
class Human:
def __init__(self, name, sex, weight):
self.name = name
self.sex = sex
self.weight = weight
boku = Human('Jack')
print(boku.name) -> Jack
class Bird(Human):
def __init__(self, name, sex, height):
super().__init__(name, sex)
self.height = height
このように、クラス名の後に、継承したい親クラスの名前
を書きます。
そして、super().__init__(親クラスと同じ処理をする変数)
という記述をすることで、self.name
やself.sex
を書く必要がなく、同じ処理を行うことができます。
****argsと*****kwargsとは
メソッドを作る時に、よく見かけるのが、*args
と**kwargs
だと思います。
これは、引数に決まったものが来ない場合でも使える最強アイテムでして、
**argsは、複数の値をタプル型で取得可能
***kwargsは、辞書型で取得可能
というような感じで、とりあえず、この2つを引数に書いておけば色んな引数をカバーできるんじゃないかなって思っております。
追記(2020/12/25)
あやふやな理解のまま、*args
と**kwargs
を使っていたのですが、しっかり理解して使わないとエラーの原因がわからないなと思ったので、記述しておきたいと思います。
*args
と**kwargs
を仮引数(関数側の引数)として書く場合は、処理を書く部分でargs
とkwargs
を使って表現する必要があります。
まず、間違った例から紹介したいと思います。
def Try(*args):
print('僕の名前は'+ name + 'です')
Try('Jack') ==>> エラー
自分は当初、このように他の変数名を用いても出力はできるのかと思っていたのですが、そうは問屋が卸さないというワケでした。
def Try(*args):
print('僕の名前は'+ args + 'です')
Try('Jack') ==>> 僕の名前はJackです
こちらのように、しっかり、仮引数と同じ名前
でアクセスする必要があります。
また、複数の引数を受け取る場合のアクセス方法は以下のようになります。
def Try(*args):
print('僕の名前は'+ args[0] + 'です')
Try('Jack', 'Bob', 'Mary') ==>> 僕の名前はJackです
このような感じで、インデックス番号
でアクセスする形になります。
次に、**kwargs
の例も載せておきます。
def Try(*args, **kwargs):
print('僕の名前は'+ kwargs['name'] + 'です')
Try('Jack', 'Bob', name='Mary') ==>> 僕の名前はMaryです
**kwargs
で引数を受け取るときは、変数名=実引数
という書き方をする必要があります。
まとめ
理解しなくても処理を行うことはできるかと思いますが、個人的にモヤモヤした状態になってしまうので、今回記事にさせてもらいました。
わかりにくい部分が多いと思いますが、皆さんのお助けになればと思います。