__init__ ってなに?


__new__ の記事は分離、移設しました。





__init__ 自体は、かなり難しくて、つまずきどころかなと思います。 なぜ難しいかというと「名前空間」「スコープ」といった難しい概念がしれっと入っていたり、 いろんなことを自動的にやっているからです。

伝わるかは厳しいのですが __init__ について、以下に説明させていただきます。   そのため理解する必要はなく、 まず __init__ の動作を覚えてしまうことが大事かなと感じたりもします。  

Step 1. オブジェクトってなに?



複数の値と処理をまとめたものです。


例えば猫 cat には名前があり、鳴くこと say ができます。

>>> cat.name
'たま'
>>>
>>> cat.type
'猫科'
>>>
>>> cat.say()
'にゃー'
>>>




Step 2. クラスってなに?



オブジェクトに 共通 の値と処理をまとめたものです。


例えば猫 cat はみな '猫科' の動物で、みな鳴くこと say ができます。 もし Python で書けば次のようになります。

class Cat:
    type = '猫科'
    
    def say(self):
        print('にゃー')


Step 3. オブジェクトをインスタンス化する。

あるクラスからオブジェクトを作ることをインスタンス化と言います。

オブジェクト = クラス名()
# コピペで動きます。
class Cat:
    type = '猫科'
    
    def say(self):
        print('にゃー')

# 猫クラスをインスタンス化
tama = Cat()
tama.name = "たま"
print(tama.name)
>>> print(tama.name)
たま
>>> 




Step 4. インスタンス化する時に実行する処理を追加する

オブジェクトをインスタンス化する時に、ちょっと改造したいことがあります。 例えば猫クラス Cat をインスタンス化してから名前をつけるのは面倒なので、 インスタンス化する時に名前をつけたいとします。

実はインスタンス化する時に __init__ が、自動的に呼び出されるのでこれを使います。 ここで大事なのは  普通の関数とは違い return 文を使わなくても自動的に self が返される  ということです。

# コピペで動きます。
class Cat:
    type = '猫科'
    
    def say(self):
        print('にゃー')
    
    def __init__(self):
        # 1. 第一引数 self には名前のない猫オブジェクトが
        #    自動的に代入されています。
        self.name = "たま"
        
        # 2. self は return しない        
        # return self

tama = Cat()
print(tama.name)  # たま
>>> tama = Cat()  # <- Cat クラスをインスタンス化しています。
>>> print(tama.name)  # たま
たま
>>> 

吾輩わがはいは猫である。名前はまだ無い。
夏目漱石 - 吾輩は猫である

(注意)頭に self をつける。

 頭に self をつけていない変数は、あとから参照できません。 

class Cat:
    def __init__(self): 
        name = "たま"

tama = Cat()
print(tama.name)  # AttributeError
>>> print(tama.name)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Cat' object has no attribute 'name'
>>> 

(余談)インスタンス変数とローカル変数

self をつけた変数をインスタンス変数と言い、後から確認することができます。 self をつけなかった変数をローカル変数といい、後から確認することはできません。

class Cat:
    def __init__(self):        
        # インスタンス変数
        #   そこで名前をつけてあげる。
        #   その時、必ず self をつけないといけません。
        self.name = "たま"
        
        # ローカル変数
        #   self をつけない変数は参照できません。
        age = 0

自分は Python を習いたての頃、self をつけ忘れて辛い思いをしました笑 「インスタンス変数」と「ローカル変数」というのは、 「スコープ」、「名前空間」という、とても深い内容と密接に関わっていたりします。





Step 5. インスタンス化をさらにカスタマイズする。

全ての猫の名前が「たま」では不都合があるので、 名前を変えられるようにしましょう。 ここで大事なのは  __init__(self, name) メソッドと、 Cat("ドラえもん") の引数の数が違うこと  です。

# コピペで動きます。
class Cat:
    type = '猫科'
    
    def say(self):
        print('にゃー')
    
    def __init__(self, name):
        self.name = name

tama = Cat("たま")
print(tama.name)  # たま

dora = Cat("ドラえもん")
print(dora.name)  # ドラえもん





__init__ は難しい

__init__ の説明は以上になります。 __init__ 自体結構難しいと思います。 次のような概念を説明しないといけないからです。 まず第一に、インスタンス変数とローカル変数が違うからです。 また第二に、いろんなことを 自動的 にしてくれているからです。

  1. 関数とは違い自動的に、self にインスタンスが代入される
  2. 関数とは違い自動的に、インスタンス化した時に呼び出される
  3. 関数とは違い自動的に、return していないのに self が返される

でも上の説明では、それを全て割愛しています... 自動的にやってくれるというのは、便利にはなるのですが、 説明すること以上に理解することが結構難しくなったりします。

下記の文章は、PEP 20 という Python のすごい人が書いた Python のコツみたいな文書からの引用です。 下手に自動化させるよりも面倒でもベタ書きしてもらった方がわかりやすいコードになるよ。 という意味だと個人的に思っています。

明示的であることは、暗黙的であるより良い。
Explicit is better than implicit.
PEP 20 - The Zen of Python

self ってなに?

self については以下の記事でご紹介させていただきました。

class User:
    def __init__(self, name):
        #        ^^^^ なにこれ?
        self.name = name

user = User('サーバルちゃん')
Last Updated: 5/24/2019, 2:47:11 AM