Python学習【365日チャレンジ!】86日目のマスターU(@Udemy11)です。
ホームベーカリーで焼いたパンを切るための包丁を買うはずが、なぜかステーキ用のナイフとフォークのカトラリーを購入してしまいました。
ちょっと高級なglobalのナイフとフォークなので、食器棚の引き出しにガチャガチャと入れているフォークやスプーンと同じように入れるのが嫌だったので、きっちりと収まるオリジナルの収納箱を作ってしまいました。
製作に半日かかったので、若干疲れ気味ですが、出来具合に満足しているので、眺めながらニヤけてしまっています。
という変な趣味の話はここまでにして、今日もPython学習をはじめましょう!
昨日の復習
昨日は、使わなくても問題がないのなら、使わないほうがいい多重継承を学習しました。
最初のプログラム設計では予定していなかったクラスを既存のクラスを継承して作成する場合などに使われます。
チームで開発したり、コードが巨大になってきたときにどうしても必要になったときに使えますが、基本的に多重継承がなくてもいいような設計にするのがベストということでした。
class Human:
def talk(self):
print('Hello')
class Car:
def run(self):
print('run')
class Night2000(Human, Car):
def auto(self):
print('ai drive system')
night2000 = Night2000()
night2000.talk()
night2000.run()
night2000.auto()
出力結果
Hello
run
ai drive system
子供の頃に、【ナイトライダー】というアメリカの刑事モノドラマがあったのですが、そこで出てくる人工知能を持ったスーパーカーが【ナイト2000】といって、めっちゃかっこよかったのを覚えています。
引用元: Amazonから引用
今回の例では、Human
とCar
を継承したNight2000
を作成して、両方のメソッドを継承し、Night2000
だけが持つメソッドを出力してみました。
多重継承についての詳しい内容は、昨日の記事をごらんください。
それでは本日の学習、クラス変数について学習しましょう!
クラス変数
クラス変数は、クラスの中で定義される変数のことで、これまでのレクチャーでは、__init__
で定義される初期設定や関数の引数として記述していたものがほとんどでした。
しかし、クラス変数は第一インデントで独立して指定することも可能で、self
を使って呼び出すことができます。
class Animal:
kind = 'bird'
def __init__(self, name):
self.name = name
def what_is_it(self):
print(self.name, self.kind)
a = Animal('A')
a.what_is_it()
b = Animal('B')
b.what_is_it()
出力結果
A bird
B bird
Animal
クラスを作成し、最初にkind
というクラス変数を定義しています。
定義されたkind
は、Animal
クラスから生成されたオブジェクト(インスタンス)が違っても、what_is_it
メソッドでself.kind
で呼び出して、出力することができます。
もちろん、今までのように、初期化際に次のように定義しても同じ出力結果が得られます。
def __init__(self, name):
self.name = name
self.kind = 'bird'
リストは共有されてしまう
クラス変数を独立して記述した際は、同じクラスから生成された異なるオブジェクトでも共有して使うことが可能ですが、注意点があります。
それが、リストをクラス変数として定義するときです。
class Something:
words = []
def add_word(self, word):
self.words.append(word)
r = Something()
r.add_word('apple')
r.add_word('banana')
print(r.words)
s = Something()
s.add_word('peach')
print(s.words)
出力結果
['apple', 'banana']
['apple', 'banana', 'peach']
Something
クラスを作成し、クラス変数にwords
という空のリストを用意して、引数を加えていくadd_word
メソッドを用意したとしましょう。
次に、Something
クラスからr
オブジェクトを作成して、add_word
メソッドを使って、apple
とbanana
を付け加えると、当然リストには2つの単語が入力されています。
その後、Something
クラスからs
オブジェクトを生成して、add_word
メソッドでpeach
を加えると、r
オブジェクトで加えたapple
とbanana
のあとにpeach
が追加されたリストになってしまいます。
つまり、クラス変数を独立して記述する場合は、同じクラスから生成されたオブジェクト(インスタンス)が、そのクラス変数を共有してしまうので、リストの中身が変わるような場合もきっちりと変更されて共有されるということです。
初期化で記述
クラス変数にリストを指定することはあまりしないほうがいいわけですが、同じクラスから生成されたオブジェクト(インスタンス)でリストを分けるには、初期化の際に記述する必要があります。
class Something:
def __init__(self):
self.words = []
def add_word(self, word):
self.words.append(word)
r = Something()
r.add_word('apple')
r.add_word('banana')
print(r.words)
s = Something()
s.add_word('peach')
print(s.words)
出力結果
['apple', 'banana']
['peach']
このコードであれば、オブジェクトが生成するたびに、__init__
によって、words
が空のリストで生成されるため、異なるオブジェクトで同じリストを共有することにはなりません。
いつも変わったことをしてしまう私なので、次のようなコードでも問題なくできるんじゃないの?なんて思って、試してみました。
def add_word(self, word):
self.words = []
self.words.append(word)
元の3・4行目を削除して、7行目にself.words = []
を追加したわけですが、結果はどうだったと思いますか?
賢明なあなたの予想どおり、まったくもって私の理解不足でした。
出力結果は次のような感じです。
['banana']
['peach']
ちょっと考えればわかることなのですが、add_word
メソッドが呼び出されるたびにクラス変数words
が空のリストになるので、当然のことながら、最初に代入したapple
はなくなって2回めにadd_word
で代入したbanana
だけがリストに残る結果になりました。
想像力を働かせて
今回の私の失敗も少し想像力を働かせれば、すぐに間違いだと気づくわけですが、やってみることも大切です。
こんな出力になるだろうと考えて実行したときに、予想どおりの出力結果が得られれば、考え方が合っていたということですし、間違っていれば、何がおかしいのか考えることができます。
一番ダメなのは、頭の中だけで「これはこうだろう」とわかった気になることです。
やってみて、意外な結果になったり、正しかったりすることで理解度が深まることは間違いないので、思ったら即実行するようにしましょう!
それでは明日もGood Python!