(python) __new__と__init__の違いについて:具体例と解説

TL;DR

__new__はインスタンスを作る前に実行されるもの
__init__はインスタンスが作られた後に実行されるもの

さらに詳しく

__new__はインスタンス作成にまつわる挙動を書くのに便利。typeを戻り値として設定する必要があり、その戻り値が新しいインスタンスのtypeとなる
__init__はインスタンスの変数を定義するのに便利(self.hogeのようなやつ)。戻り値は設定できないので、すでに作成されたインスタンスの変数を設定するのに使われる

__new__と__init__の違いを理解するにはクラス、インスタンスについてよくわかっている必要があるので、まずは初歩的なところから説明していく↓

クラス、インスタンスとは

クラスとはクッキーの型、インスタンスは型によって作られたクッキー自身という例えがある。

似たような機能を持つクッキーを複数作りたいとき、クッキーの型があると便利。

実際にクッキーを作ってみる:

class Cookies():
    pass  # なにもしない

my_cookie = Cookies()
your_cookie = Cookies()
type(my_cookie)  #  =>  __main__.Cookies

my_cookieとyour_cookieという2つのインスタンスが作れた。

メソッドとは

クラスが持っている関数 (= defで始まるやつ) のこと

これによりクッキーに機能をもたせることが出来る

class Cookies():
    price = 100
    
    def add_chocolate(self):
        self.sozai = "ミルクチョコレート"

my_cookie = Cookies()
your_cookie = Cookies()

print(my_cookie.sozai)  # sozaiはまだ設定してないのでエラー

my_cookie.add_chocolate()
print(my_cookie.sozai)  # ミルクチョコレート


my_cookie.sozai = 'ミント'
print(my_cookie.sozai)  # => 'ミント'

print(my_cookie.price)  # => 100
my_cookie.price = 200
print(my_cookie.price)  # => 200

↑の例では add_chocolateというメソッドを実行させることで、self, つまりmy_cookieにsozaiを足すことが出来る。ちなみに、selfはクッキー自身のことである

ここで、足したいチョコレートはミルクチョコレート以外にもあるかもしれないので、add_chocolateを改造して便利にしたい

class Cookies():
    price = 100
    
    def add_chocolate(self, choco_kind='ミルクチョコレート'):
        self.sozai = choco_kind

my_cookie = Cookies()
my_cookie.add_chocolate('いちごチョコレート')
print(my_cookie.sozai)  # => 'いちごチョコレート'

と、このように好きな素材をセットすることも出来る。

コンストラクターとは (__init__と__new__)

新しくクッキー (インスタンス) を作った時に、常に中にチョコレートを入れたいとき、いちいちadd_chocolate()を実行するのは面倒だし忘れそう。そんなときにコンストラクターを使う

class Cookies():

    def __init__(self, choco_kind='ミルクチョコレート'):
        self.sozai = choco_kind

my_cookie = Cookies()
print(my_cookie.sozai)  # => 'ミルクチョコレート'
your_cookie = Cookies(choco_kind='ビターチョコレート')
print(your_cookie.sozai)  # => 'ビターチョコレート'

ここで、インスタンスを作る前に、今までに作られたクッキーの数をクッキーの型にメモしていき、「クッキーがすでに2つ以上あったら新しいクッキーを作らないようにする」という機能を追加したいとする。

そこで__new__の出番だ。

class Cookies():
    max_cookie = 2
    current_cookie = 0
    def __new__(cls):
        if cls.current_cookie >= cls.max_cookie:
            print('すでに2つクッキーがあるので新しいクッキーは作りません')
            return None
        cls.current_cookie += 1
        return super().__new__(cls)  # いつも通り普通にクッキーを作ってねという命令


my_cookie = Cookies()
your_cookie = Cookies()
nobita_cookie = Cookies()  # すでに2つクッキーがあるので...と出る
print(nobita_cookie)  # => None

これにより、新しいクッキーが作られるのを阻止できた。

新しいインスタンスが作成されるのを防げるということは、シングルトンを実装するのに便利。