クラスの基礎
カプセル化
まずは最も簡単なクラス定義をみてみましょう。
code: 0701_class_simple.py
class Artist:
pass
queen = Artist()
queen.firstname = 'Freddie'
queen.lastname = 'Mercury'
whitesnake = Artist()
whitesnake.firstname = 'David'
whitesnake.lastname = 'Coverdale'
print(queen.firstname)
print(queen.lastname)
print(whitesnake.firstname)
print(whitesnake.lastname)
print(queen)
print(whitesnake)
このクラスの名前は Artist で、慣例的に単語の頭が大文字として記述されますが、言語仕様として定められているわけではありません。そして、このArtistクラスはなんの定義もしていません。これはPytonではクラス定義で必須なものは何もないことを意味しています。
この例で重要なことは、同じArtist クラスから生成した2つのインスタンス queen と whitesnake はオブジェクトIDが異り名前空間(Namespace) が違うということです。
このため同じアトリビュート(firstname と lastname) を持たせて、そこに別の値を設定しても、それぞれ独立しているので壊されることがありません。
code: bash
$ python 0701_class_simple.py
Freddie
Mercury
David
Coverdale
<__main__.Struct object at 0x10cf7c4e0>
<__main__.Struct object at 0x10cf7ca58>
オブジェクト指向の重要な概念のひとつにカプセル化(encapsulation) があります。
これは、データとそれを処理するコードを隠して、オブジェクトのデータにはメソッド(つまりプログラム)でアクセスできるようにすることです。もっと簡単に説明すると、「データと手続きを一緒にまとめる」そして「データには直接アクセスしない」ということです。
__init__()関数
クラスを理解するうえではじめに理解する必要があるのは、__init__()関数です。
この関数はクラスからオブジェクト生成するたびに自動的に呼び出されます。
__init__()関数をコンストラクタ、クラスから生成したオブジェクトをインスタンスと呼びますが、重要なことはその定義方法と実行されるタイミングです。
混乱してもらいたくはないのですが...
厳密にはコンストラクタとは、それが実行された結果インスタンスが生成されるものです。
このため、実は Python での __init__() はコンストラクタではなく、それに似たものです。
つまり、Python は生成されたインスタンスオブジェクトの__init__()メソッドを呼び出しいるからです。
いまの段階では、インスタンスが作成されたときに自動的に実行されるメソッドなんだと、
大まかに理解しておくだけでよいでしょう。
後ほど、もう少し詳しく説明します...
先程のクラスArtistを少し修正して明示的にコンストラクタ__init__()を定義してみましょう。
code: 0702_class_with_constructor.py
class Artist:
def __init__(self):
self.firstname = ''
self.lastname = ''
queen = Artist()
queen.firstname = 'Freddie'
queen.lastname = 'Mercury'
whitesnake = Artist()
whitesnake.firstname = 'David'
whitesnake.lastname = 'Coverdale'
__init__() の第1引数は self ですが、これは慣例的に使用される変数名で、自分自身を表しています。言語仕様的には self でなくても問題ないのですが、まず間違いなくself が使われます。self以外の変数名を使わない方が良いでしょう。
code: 0703_class_about_self.py
# 言語仕様的には問題はなく、エラーにはならない
class Artist:
def __init__(this):
this.firstname = ''
this.lastname = ''
queen = Artist()
インスタンスqueen を生成するとき __init__()が実行され、インスタンス変数self.firstname と self.lastname が初期化されます。
インスタンス変数とクラス変数は同じ名前でも構いません。
code: 0704_class_variables.py
class Artist:
name = "Person" # クラス変数
def __init__(self, name = None):
self.name = name # インスタンス変数
david = Artist("Coverdale")
print(f'{Artist.name} name is {david.name}')
メソッド
カプセル化について「データと手続きを一緒にまとめる」そして「データには直接アクセスしない」と説明しました。クラスで定義している内部データを処理する関数がメソッド(method) です。
code: 0706_class_and_methods.py
class Artist:
def __init__(self):
self.firstname = ''
self.lastname = ''
def show_name(self):
return( f'{self.firstname}_{self.lastname}')
queen = Artist()
queen.firstname = 'Freddie'
queen.lastname = 'Mercury'
print(queen.show_name())
この例は内部データ firstname と lastname を外部から設定しているので、実は良くない例となります。例えば、firstname を firstnane と typo したときはなんのエラーにもならずに、新しく firstnane というアトリビュートを作成してしまうからです。
そこで、クラスのインスタンスオブジェクトを生成するときに値を与えるように、修正してみましょう。
code: 0707_class_and_methods.py
class Artist:
def __init__(self, firstname='', lastname=''):
if type(firstname) != str or type(lastname) != str:
raise(ValueError("firstname and lastname must be 'str'"))
self.firstname = firstname
self.lastname = lastname
def show_name(self):
return( f'{self.firstname}_{self.lastname}')
queen = Artist('Freddie', 'Mercury')
print(queen.show_name())
このクラスはインスタンス生成時に与えられた引数を型チェックしてから保持するようになりましたね。
クラス定義で大事なことは、プログラマがクラスの内部データと処理内容を、なるべく知らなくても良いようにすることです。
とはいえ、組み込み関数 dir() を使えば、内部の変数名は見えてしまいます。
次にこれを隠す方法を見てみましょう。