15
19

More than 3 years have passed since last update.

__init__やselfに関する備忘録

Last updated at Posted at 2020-12-18

今回は、プログラミングにおいて、__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'が呼び出されるという仕組みになっております。

インスタンス化とは何か

インスタンス化とは、簡単に言えば、型作りのようなものですね。
人間という大雑把な形を作って、JackBobという個人を作る部分に分かれてくるという具合です。


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__の中で定義されたnumberNextの中で出力されている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.numberNextnumberは別物であるというワケです。

では、両方に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を使うことによって、メソッドを跨いだとしても、同じ変数であるということが認識されているワケですね。
self1つのクラスの中で同じ変数を使い回したい時に活躍してくれます。

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.nameself.sexを書く必要がなく、同じ処理を行うことができます。

*argsと**kwargsとは

メソッドを作る時に、よく見かけるのが、*args**kwargsだと思います。
これは、引数に決まったものが来ない場合でも使える最強アイテムでして、

*argsは、複数の値をタプル型で取得可能
*
*kwargsは、辞書型で取得可能

というような感じで、とりあえず、この2つを引数に書いておけば色んな引数をカバーできるんじゃないかなって思っております。

追記(2020/12/25)

あやふやな理解のまま、*args**kwargsを使っていたのですが、しっかり理解して使わないとエラーの原因がわからないなと思ったので、記述しておきたいと思います。

*args**kwargsを仮引数(関数側の引数)として書く場合は、処理を書く部分でargskwargsを使って表現する必要があります。

まず、間違った例から紹介したいと思います。

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で引数を受け取るときは、変数名=実引数という書き方をする必要があります。

まとめ

理解しなくても処理を行うことはできるかと思いますが、個人的にモヤモヤした状態になってしまうので、今回記事にさせてもらいました。

わかりにくい部分が多いと思いますが、皆さんのお助けになればと思います。

15
19
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
19