Pythonのクラス定義とインスタンスの扱い

#クラス定義とインスタンスの扱いついて
[こちら](http://qiita.com/hiroykam/items/2fea445fd2d489354c34)で備忘録を兼ねたまとめを作成していた際に、改めて気付いたり、発見したことがあったので、ここで記載したい。

##クラス変数とインスタンス変数
###はじめに
"Hello World"と出力するサンプル。クラス変数`c_var`とインスタンス変数`i_var`をそれぞれ定義。

```py
class Test(object):
    c_var = "Hello"

    def __init__(self):
        self.i_var = "World"

    def __str__(self):
        return Test.c_var + " " + self.i_var

print(Test())
```

###クラス変数とインスタンス変数の検証（その1）
クラス変数と同じ名前のインスタンス変数を定義するパターン(`examine1`)とクラス変数を変更するパターン(`examine2`)で検証。

```py
class Test(object):
    c_var = "Hello"

    def __init__(self):
        self.i_var = "World"

    def __str__(self):
        return Test.c_var + " " + self.i_var

    def examine1(self):
        self.c_var = "Hey"
        return self

    def examine2(self):
        Test.c_var = "Hey"
        return self

t = Test()
print(t.examine1())
print(t.examine2())
print(Test())
```

出力結果から、クラス変数とインスタンス変数は別もので、インスタンス変数は変更可能であることがわかる。

```text:出力結果
Hello World
Hey World
Hey World
```

なお、`vars(t)`でインスタンス変数、`vars(Test)`でクラス変数が確認できる。

###クラス変数とインスタンス変数の検証（その2）
今度はメソッドではなく、クラスメソッドとスタティックメソッドで、クラス変数を変更してみる。出力結果から、共に`__init__`が呼ばれ、またクラス変数が変更されている。

```py
class Test(object):
    c_var = "Hello"

    def __init__(self):
        self.i_var = "World"

    def __str__(self):
        return Test.c_var + " " + self.i_var

    @classmethod
    def class_method(cls):
        cls.c_var = "Class"

    @staticmethod
    def static_method():
        Test.c_var = "Static"

Test.class_method()
print(Test())
Test.static_method()
print(Test())
```

```text:出力結果
Class World
Static World
```

###クラス変数とインスタンス変数の検証（その3）
今度は浅いコピーと深いコピーをしたときのクラス変数とインスタンス変数の変化について検証してみる。
深いコピーでもクラス変数の値が変更されている。

```py
import copy


class Test(object):
    c_var = 2

    def __init__(self, obj):
        self.i_var = obj

    def __str__(self):
        return "{0} {1}".format(Test.c_var, self.i_var)

    def examine1(self):
        self.i_var += 2
        return self

    def examine2(self):
        Test.c_var += 2
        return self


a = int(3)
t1 = Test(a)
t1.examine1().examine2()
print(t1)
print(Test(a))

print("--- shallow copy ---")

t2 = copy.copy(t1)
t2.examine1().examine2()
print(t1)
print(t2)
print(Test(a))

print("--- deep copy ---")

t3 = copy.deepcopy(t1)
t3.examine1().examine2()
print(t1)
print(t2)
print(t3)
print(Test(a))
```

```text:出力結果
4 5
4 3
--- shallow copy ---
6 5
6 7
6 3
--- deep copy ---
8 5
8 7
8 7
8 3
```

##メソッドの第1引数`self`
2つのメソッド`examine1`と`examine2`を定義したクラス。

```py
class Test(object):
    def __init__(self):
        self.i_var = "nothing"

    def __str__(self):
        return self.i_var

    def examine1(self):
        self.i_var = "examine1"
        return self

    def examine2(self):
        self.i_var = "examine2"
        return self

t1 = Test()
print(t1)
print(t1.examine1())
print(t1.examine2())
```

出力結果は以下の通り。

```text:出力結果
nothing
examine1
examine2
```

ここで、クラスメソッドのように`print(Test.examin1())`を実行したところ、もちろんエラー。

```text:エラー
examine1() missing 1 required positional argument: 'self'
```

ということは第1引数`self`にインスタンスを指定することができるのだろうか。

```py
t1 = Test()
print(Test.examine1(t1.examine2()))
print(Test.examine2(t1.examine1()))
```

上記を実行すると、下記結果が出力された。

```text:出力結果
examine1
examine2
```

##callableなクラス
###callableなクラスの例
[まとめ](http://qiita.com/hiroykam/items/2fea445fd2d489354c34)でcallableなクラスをご提示いただいて、目から鱗と同時に、自分の勉強不足を痛感してしまった。

```py
class Callable(int):
    def __call__(self, *arguments):
        print(arguments[0])

a = Callable(5)
print(a)
a(3)
```

```text:出力結果
5
3
```

上記では、`a`が関数としても変数としても利用できるということを意味している。`a`は`Callable`インスタンスの参照値が代入されているので、`print(a)`で、インスタンス生成時に引数として渡した値が表示される。また、`a`を関数のように呼び出すと、`__call__`が呼び出されるので、第一引数の値が`print`される。

###callableなクラスの活用例
`int`をcallableに拡張し、関数呼び出しで第1引数を単位(名)として扱うような実装。`int = Callable`で標準`int`クラスを置き換えている。

```py
class Callable(int):
    def __call__(self, unit):
        print("{0} {1}".format(self, unit))

int = Callable
print(int("3") * 4)
int("5")("meters")
int(2017 - 2010)("years old")
```

```出力結果
12
5 meters
7 years old
```

##ミュータブルな数値オブジェクト
`__add__`と`__sub__`を実装したクラスを用意した。下記のように足しても引いても参照値は変わらない。こちらも標準`int`を置き換えた。置き換えた後`id`の値が変化しなくなった。

```py
class MyInt(object):
    def __init__(self, v):
        self.__v = v

    def __add__(self, other):
        self.__v += other
        return self

    def __sub__(self, other):
        self.__v -= other
        return self

a = int(2)
print(id(a))
a = a + 3
print(id(a))
a = a - 5
print(id(a))

int = MyInt

a = int(3)
print(id(a))
a = a + 3
print(id(a))
a = a - 5
print(id(a))
```

```text
1983644176
1983644272
1983644112
1736248227544
1736248227544
1736248227544
```

`__mul__`、`__matmul__`、`__truediv__`、`__floordiv__`、`__mod__`、`__divmod__`、`__pow__`、`__lshift__`、`__rshift__`、`__and__`、`__xor__`、`__or__`も必要に応じて実装する。

###ミュータブルな数値オブジェクトをクラス変数とインスタンス変数で利用する
クラス変数とインスタンス変数の検証（その3）では`int`を利用したが、ここで定義したミュータブルな数値オブジェクトを利用して検証してみた。
イミュータブルのときと同様にクラス変数は深いコピーでも値の変更が可能。

```py
import copy


class MyInt(object):
    def __init__(self, v):
        self.__v = v

    def __str__(self):
        return str(self.__v)

    def __add__(self, other):
        self.__v += other
        return self

    def __sub__(self, other):
        self.__v -= other
        return self

int = MyInt


class Test(object):
    c_var = int(2)

    def __init__(self, obj):
        self.i_var = obj

    def __str__(self):
        return "{0} {1}".format(Test.c_var, self.i_var)

    def examine1(self):
        self.i_var += 2
        return self

    def examine2(self):
        Test.c_var += 2
        return self


a = int(3)
t1 = Test(a)
t1.examine1().examine2()
print(t1)
print(Test(a))

print("--- shallow copy ---")

t2 = copy.copy(t1)
t2.examine1().examine2()
print(t1)
print(t2)
print(Test(a))

print("--- deep copy ---")

t3 = copy.deepcopy(t1)
t3.examine1().examine2()
print(t1)
print(t2)
print(t3)
print(Test(a))
```

```text:出力結果
4 5
4 5
--- shallow copy ---
6 7
6 7
6 7
--- deep copy ---
8 7
8 7
8 9
8 7
```