クラスを利用することで、データとそれを利用する関数の論理グループを定義することが可能になります。また、ロガーに対してコンテキストデータ (その時々の状況で値の異なるデータ) を渡すこともできます (例えばログを書き込むファイル名など)。
クラスを最大限に活用するには、新たな考え方を少し取り入れる必要があります。
関連のあるデータと関数をクラスとしてまとめる、ということは、データとそれを操作する関数から成る実体 (instances) を作成するための雛形 (青写真: blue print) を定義する、ということです。そしてこのクラスの実体をオブジェクト (object) と呼んでいます。
我々が作成しているロガーを例にとって考えてみましょう。このロガーをより汎用的に利用できるようにするためにこのロガークラスがファイル名を受け取るようにしておけば (これがコンテキストデータになります)、その情報を基に作成されたこのクラスの実体は、そのファイルに対して書き込みを行うロガーになります。つまり、このロガー (実体) が、指定したファイルに対して書き込みを行うロガークラスのインスタンス (オブジェクト) ということです。
データと、そのデータのみを唯一のエンティティとして操作する関数を組み合わせて考えることが、オブジェクト指向プログラミング (Object-Oriented programming) の基礎になります。
from pathlib import Path
class Logger(object):
def __init__(self, file_name):
self.file_name = file_name
def _write_log(self, level, msg):
with Path(self.file_name).open('a') as log_file:
log_file.write(f'[{level}] {msg}\n')
def critical(self, msg):
self._write_log('CRITICAL', msg)
def error(self, msg):
self._write_log('ERROR', msg)
def warn(self, msg):
self._write_log('WARN', msg)
def info(self, msg):
self._write_log('INFO', msg)
def debug(self, msg):
self._write_log('DEBUG', msg)
ここで少し「属性 (attributes)」と「メソッド (methods)」について触れておきます。「属性」は、クラス実装時に定義された変数など、オブジェクトの「状態」を表す「データ」を指します。そして、それらのデータを操作する関数が「メソッド」です。あるクラス定義を基にインスタンス化されたオブジェクトのメソッドをそのオブジェクト内で呼び出すには次のようにします:
self.some_method()
この時に Python のインタプリタは、暗黙的に「オブジェクト自身」をこのメソッドに渡してこのメソッドを呼び出します。ですからクラス内で定義したメソッドは必ず第1パラメータとしてこれを受け取るようにします (このパラメータの名前が 'self' でなければダメ、というわけではありません。しかし習慣的にこの名前が使われています)。
そして、先程も少し書きましたが、オブジェクト内で属性を参照するには次のように記述します:
self.some_attribute
この時に Python のインタプリタがやっているのは、オブジェクト自身と指定された属性名をパラメータとして __getattr__() 関数を呼び出すことです。
この __init__() の中では、このオブジェクトの file_name という属性に、呼び出し元が渡した値 (ログファイル名) をセットしています。
残りのコードの内容は以前とほぼ変わりありません。ただし今説明したように、オブジェクト内のメソッドには第1パラメータとして self が渡されており、メソッドの呼び出し時にも self を付けて呼び出しています。
クラス実装の良いパターンは、__init__() 実行終了時にオブジェクトの使用準備が完全に整っているようにすることです。つまり、オブジェクトを使用する際に、これ以外に何らかの設定作業が必要だったり、あるメソッドを実行する必要がないようにすべき、ということです。
また _write_log() メソッド名の先頭に _ (アンダースコア) がついていることを疑問に思う方もいるかもしれません。これは Python における慣習の1つで、このコードを見る他のプログラマーに対して「このメソッドは private と想定しているので、オブジェクト外部から直接呼び出すべきではない」ということを伝えるためのものです。
では、今回実装した新しい Logger クラスを利用してみましょう:
new_script_with_logger.py
from logger_class import Logger
logger_object = Logger('/var/log/class_logger.log')
logger_object.info('クラスロガー経由で書き込んでいます')
このコードを実行後の /var/log/class_logger.log ファイルには、以下のテキストが書き込まれています:
[INFO] クラスロガー経由で書き込んでいます