#クラス定義とインスタンスの扱いついて
こちらで備忘録を兼ねたまとめを作成していた際に、改めて気付いたり、発見したことがあったので、ここで記載したい。
##クラス変数とインスタンス変数
###はじめに
"Hello World"と出力するサンプル。クラス変数c_var
とインスタンス変数i_var
をそれぞれ定義。
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
)で検証。
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())
出力結果から、クラス変数とインスタンス変数は別もので、インスタンス変数は変更可能であることがわかる。
Hello World
Hey World
Hey World
なお、vars(t)
でインスタンス変数、vars(Test)
でクラス変数が確認できる。
###クラス変数とインスタンス変数の検証(その2)
今度はメソッドではなく、クラスメソッドとスタティックメソッドで、クラス変数を変更してみる。出力結果から、共に__init__
が呼ばれ、またクラス変数が変更されている。
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())
Class World
Static World
###クラス変数とインスタンス変数の検証(その3)
今度は浅いコピーと深いコピーをしたときのクラス変数とインスタンス変数の変化について検証してみる。
深いコピーでもクラス変数の値が変更されている。
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))
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
を定義したクラス。
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())
出力結果は以下の通り。
nothing
examine1
examine2
ここで、クラスメソッドのようにprint(Test.examin1())
を実行したところ、もちろんエラー。
examine1() missing 1 required positional argument: 'self'
ということは第1引数self
にインスタンスを指定することができるのだろうか。
t1 = Test()
print(Test.examine1(t1.examine2()))
print(Test.examine2(t1.examine1()))
上記を実行すると、下記結果が出力された。
examine1
examine2
##callableなクラス
###callableなクラスの例
まとめでcallableなクラスをご提示いただいて、目から鱗と同時に、自分の勉強不足を痛感してしまった。
class Callable(int):
def __call__(self, *arguments):
print(arguments[0])
a = Callable(5)
print(a)
a(3)
5
3
上記では、a
が関数としても変数としても利用できるということを意味している。a
はCallable
インスタンスの参照値が代入されているので、print(a)
で、インスタンス生成時に引数として渡した値が表示される。また、a
を関数のように呼び出すと、__call__
が呼び出されるので、第一引数の値がprint
される。
###callableなクラスの活用例
int
をcallableに拡張し、関数呼び出しで第1引数を単位(名)として扱うような実装。int = Callable
で標準int
クラスを置き換えている。
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
の値が変化しなくなった。
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))
1983644176
1983644272
1983644112
1736248227544
1736248227544
1736248227544
__mul__
、__matmul__
、__truediv__
、__floordiv__
、__mod__
、__divmod__
、__pow__
、__lshift__
、__rshift__
、__and__
、__xor__
、__or__
も必要に応じて実装する。
###ミュータブルな数値オブジェクトをクラス変数とインスタンス変数で利用する
クラス変数とインスタンス変数の検証(その3)ではint
を利用したが、ここで定義したミュータブルな数値オブジェクトを利用して検証してみた。
イミュータブルのときと同様にクラス変数は深いコピーでも値の変更が可能。
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))
4 5
4 5
--- shallow copy ---
6 7
6 7
6 7
--- deep copy ---
8 7
8 7
8 9
8 7