そもそもクラスとは??
一つ例を示して考えてみましょう。例えば飲み物について考えてみましょう。
まず飲み物でできることを考えてみましょう
・飲むことができる
・コップに注ぐことができる。
このようなある分野で同じようなことをできるのをまとめているのをクラスと言います。
では飲み物で例を示していきたいと思います。
classによる定義
class Drink():
pass
passというのは存在はしているけどなんの操作もしませんよ。ということになっています。またクラスを定義するにあったって最初は大文字にしましょう。これでDrinkというクラスは出来上がりました。次にクラスにインスタンスをいれていきます。
cider = Drink()
と定義することができます。これでDrinkクラスにサイダーが入った感じになります。次にサイダーの中にも商品名がたくさんありますね。それを表示できるようにしましょう。引数に商品名を入れるとそれはインスタンスと同じくDrinkクラスにいれられます。まず__init__()
関数を定義していきます。これはこのクラスがPythonのクラスであることを表しています。ここに色々な情報を入れることができます。
class Drink():
def __init__(self, name):
self.name = name
少しずつ説明していきます。まず__init__()
の中に入っている引数の二つについて説明していきます。まずselfはクラスの中での関数を定義する時に必須となってくる。私はこのselfをクラスの中で関数を定義するっていうのが少し謎だったためこれはインスタンスだと考えた。これはあっているかわからないので気にしなくてもいいが、こうすると少しだけわかりやすく感じる。ciderはselfであると考えたら残るはnameでそれは引数にいれるという動作だけで済む。僕はそう考えているのでってことで参考にしてみてください!そしてこれからクラスにciderの三ツ矢サイダーを追加していきましょう。
cider = Drink('三ツ矢サイダー')
これでインスタンスの作成ができた。これからこのように定義したインスタンスの三ツ矢サイダーを表示できるようにやってみよう。
print('これから私が飲むものは', cider.name)
# 実行結果
これから私が飲むものは三ツ矢サイダー
このようにしてciderのnameを出すことができた。この時にこう考えてみてはどうだろうか。selfはcider,nameは'三ツ矢サイダー'こう考えるとselfはciderとわかりやすく考えることができる。
##継承
これから継承について説明していく。元あるクラスをどんどん編集したりするのもいいが、どんどんついかするかたちでやってみたらどうであろうか。またこの場合だとDrinkの中にも炭酸や果実ジュースなどがありますよね?そういう分け方もできるのです。
class Drink():
def drink(self):
print('This is Drink')
class Sparkling(Drink):
def sparkring(self):
print('This is Sparkling')
class FruitJoice(Drink):
def fruit(self):
print('This is Fruit joice')
joice = Drink()
cider = Sparkling()
bananajoice = FruitJoice()
cider.drink()
cider.sparkring()
bananajoice.fruit()
joice.drink()
# 実行結果
This is Drink
This is Sparkling
This is Fruit joice
This is Drink
ここで一ついうことがある。
継承元のクラスは親クラスといい、継承しようと思っているものを子クラスという。ここでいう親クラスはDrinkで子クラスはSparklingとFruitJoiceです。また親クラスで定義したインスタンスは子クラスの関数を使うことができないので注意しよう。
メソッドのオーバーライド
これからは今ある親のメソッドを上書き(オーバーライド)していきましょう。
class Drink():
def drink(self):
print('This is Drink')
class Sparkling(Drink):
def drink(self):
print('I want to drink')
a = Drink()
b = Sparkling()
a.drink()
b.drink()
# 実行結果
This is Drink
I want to drink
このようにオーバーライドすることができる。しかし上書きと言われるとDrinkにいれたaインスタンスのdrink関数も上書きされるのではないかと思うのだがどうだろうか。この続きにこのようなことを書いてチェックしてみましょう
a.drink()
# 実行結果
This is Drink
このように親クラスで定義したインスタンスは上書きされても関数は変わらない。
superによる親メソッドへの呼び出し
子クラスで独自に定義したり、オーバーライドをしたりするやり方は今習得した。しかし子クラスが親メソッドを呼び出したい時にどうすればいいのか。それはsuperを使う
class Drink():
def __init__(self, name):
self.name = name
class Sparkling(Drink):
def __init__(self, name):
super().__init__(name)
cider = Sparkling('Mityuya')
print(cider.name)
# 実行結果
Mityuya
このようにDrinkの__init__
関数をそのまま使うことができます。これをすると何がいいのかというと例えばいきなりドリンクに株式会社と名前をつくことが義務付けられたらどうなるだろうか。いちいち全て変えようとするのはめんどくさい。いざという時のためにってことのためにsuperを全てやっておけばこのような変更
class Drink():
def __init__(self, name):
self.name = "株式会社" + name
と変更することができる。これで実行結果を確認しようと
cider = Sparkling('Mityuya')
print(cider.name)
# 実行結果
株式会社Mityuya
これでできます。
プロパティ
プロパティとはクラス内で定義されたものを変えられないように設定することである。例えば炭酸のものにこれは炭酸ではないと設定されたら大変ですし、炭酸では無くなりますよね?
class Drink():
def print_this_is_drink(self):
print('This is drink')
class Sparkling(Drink):
def __init__(self, this_is_sparkling=True):
self.this_is_sparkling = this_is_sparkling
cider = Sparkling()
print(cider.this_is_sparkling)
# 実行結果
True
このままだとthis_is_sparklingを変更しようとしたら変更できます。
class Drink():
def print_this_is_drink(self):
print('This is drink')
class Sparkling(Drink):
def __init__(self, this_is_sparkling=True):
self.this_is_sparkling = this_is_sparkling
cider = Sparkling()
self.this_is_sparkling = False
print(cider.this_is_sparkling)
# 実行結果
False
このように変更できちゃいます。これを変更できないようにしましょう。それは「self.this_is_sparkling = this_is_sparkling」にアンダースコアをつけていきます。
self._this_is_sparkling = this_is_sparkling
これでできるのですが、_this_is_sparkling = Falseと設定してもまだ結果はFalseに変わってしまいます。ではどうすればいいのかというとプロパティを使った関数を作っていきます。Sparklingクラスの中にコのような関数をいれてください。
@property
def it_is_sparkling(self):
return self._this_is_sparkling
そしたら全体的にこのような構造になると思います。
class Drink():
def print_this_is_drink(self):
print('This is drink')
class Sparkling(Drink):
def __init__(self, this_is_sparkling=True):
self.this_is_sparkling = this_is_sparkling
@property
def it_is_sparkling(self):
return self._this_is_sparkling
cider = Sparkling()
cider.this_is_sparkling = False
print(cider.this_is_sparkling)
# 実行結果
Traceback (most recent call last):
<module>
cider.this_is_sparkling = False
AttributeError: can't set attribute
このようにエラーが出てきます。よって変更はできないということでプロパティが成立します。今のように変更できなくしたことをゲッターというものです。これからセッターという変更できるものを使っていきたいと思います。やり方はプロパティのように@マークを使用します。全体図としては
class Drink():
def print_this_is_drink(self):
print('This is drink')
class Sparkling(Drink):
def __init__(self, this_is_sparkling=True):
self._this_is_sparkling = this_is_sparkling
@property
def this_is_sparkling(self):
return self._this_is_sparkling
# 追加部分
@this_is_sparkling.setter
def this_is_sparkling(self, is_sparkling):
self._this_is_sparkling = is_sparkling
# 追加終わり
cider = Sparkling()
cider.this_is_sparkling = False
print(cider.this_is_sparkling)
# 実行結果
False
is_sparklingのなかに引数としてFalseが入っている感じになっている。次にやりたいのはまずそのデータがないみたいなことをやってみたいと思います。例えば、先ほどやったthis_is_sparklingについてやっていきたいと思います。データをクラスでの定義内での変更やりようが可能だが、クラス定義内でthis_is_sparklingがどのようなものかチェックしようとするとそのオブジェクトはありませんよ。と表示されます。やってみましょう
class Drink():
def print_this_is_drink(self):
print('This is drink')
class Sparkling(Drink):
def __init__(self, this_is_sparkling=True):
self.__this_is_sparkling = this_is_sparkling
@property
def this_is_sparkling(self):
return self._this_is_sparkling
@this_is_sparkling.setter
def this_is_sparkling(self, is_sparkling):
self._this_is_sparkling = is_sparkling
cider = Sparkling()
print(cider.__this_is_sparkling)
やり方はthis_is_sparkling
を__this_is_sparkling
にするだけで可能になります。これで実行してみると
Traceback (most recent call last):
File
<module>
print(cider.__this_is_sparkling)
AttributeError: 'Sparkling' object has no attribute '__this_is_sparkling'
このようにありません。とエラーが出てきます。これをクラス定義内で__this_is_sparkling
をチェックしてみるとどうなるかというと
class Drink():
def print_thi_is_drink(self):
print('This is drink')
class Sparkling(Drink):
def __init__(self, this_is_sparkling=True):
self.__this_is_sparkling = this_is_sparkling
@property
def this_is_sparkling(self):
return self._this_is_sparkling
@this_is_sparkling.setter
def this_is_sparkling(self, is_sparkling):
self._this_is_sparkling = is_sparkling
def check_sparkling(self):
print(self.__this_is_sparkling)
cider = Sparkling()
cider.check_sparkling()
全体図としてはこのようになる。そうすると実行結果は
True
とクラス定義内だとチェックできる。
クラスメソッド
クラスメソッドはクラス全体に影響を与えるメソッドでクラスに加えた変更は全てのクラス内でのオブジェクトに影響を与える。クラスメソッドの定義方法としてはデコレーターを使って@classmethodを使ってやっていく。
class Drink():
count = 0
def __init__(self):
Drink.count += 1
@classmethod
def count_drink(cls):
print('He has', cls.count)
cider = Drink()
apple_joice = Drink()
grape_joice = Drink()
Drink.count_drink()
# 実行結果
He has 3
これをすると現状のクラス内に入っているものや状況がわかるので楽である。
次に静的メソッドを紹介していこう。これもクラスメソッドと同様デコレーターをつけてやっていく。@staticmethodを使ってやっていくのだ。
class Drink():
@staticmethod
def drink():
print('This is drink')
Drink.drink()
# 実行結果
This is drink
このようにオブジェクトを作らないでもメソッドを実行することができる。
ダックタイピング
ダックタイピングとはクラスが別々のものを同じメソッドを使用することができてそれぞれのクラスで作ったものを同じ操作でそれぞれの処理をできることをさします。要はクラスが違くても同じ関数で場合分けすることができるよってことです。試しに投票というものでやってみましょう。大人でない方は投票することができず、大人の方は投票することができる。そんなものを作ってみましょう。
class Person():
def __init__(self, age=0):
self.age = age
def vote(self):
if self.age < 18:
print('You cannot vote')
else:
print('You can vote')
class NotAdult(Person):
def __init__(self, age=0):
if age < 18:
super().__init__(age)
else:
print('You can vote')
class Adult(Person):
def __init__(self, age=18):
if age >= 18:
super().__init__(age)
else:
print('You cannot vote')
class Vote(object):
def vote_yes(self, which_adult):
which_adult.vote()
not_adult = NotAdult()
adult = Adult()
votes = Vote()
votes.vote_yes(not_adult)
votes.vote_yes(adult)
# 実行結果
You cannot vote
You can vote
細かく説明していきましょう。
1Personという親クラスを作成し、子クラスが使える場合分けをする(18歳かどうか)関数を作ります。
218歳以上なのかどうなのかという子クラスをそれぞれ作っていきます。ここではデフォルト引数が設定されていてそれぞれのelse文にはそうでない場合はErrorが出るようにします。(ここでは、ダックタイピングなのでそのエラーは表示されない。)
3最後にそれらをまとめるものを作ります。そしてそれぞれの子クラスが入った時に先ほど1で作成した関数が使えるようにしていきます。これで完了です。
特殊メソッド
今まで学んできた特殊メソッドとして__init__
があります。この他にもたくさんあるので紹介していきます。まず特殊メソッドがある意味について教えていきたいと思います。特殊メソッドの代表的なものとして__eq__
というものがあります。これは一方と他方が等しいことを表します。これを使ってイコールを示すのか、特殊メソッドを使って示すのどちらが楽かということをやっていきたいと思います。
class A():
def __init__(self, a):
self.a = a
def equal(self, b):
return self.a == b.a
def __eq__(self, b):
return self.a == b.a
first = A('a')
second = A('a')
# equal関数を使った時
print(first.equal(second))
# 特殊メソッドを使った時
print(first == second)
結果はどっちともTrueになりますが、どちらがみやすくて描きやすいですかと言われますとやっぱり__eq__
の方だと思います。このようにみやすく書きやすくされたものが特殊メソッドだと思います。
他にもたくさんの特殊メソッドがあるのでチェックしてみてください。
https://blog.codecamp.jp/python-class-code