Pythonプログラミングで

2048 を作る

Stage 2 プログラムの土台を作る

2-1 ログを設定する

まずはコードの config.py というファイルをご覧ください。

                        
from logging import getLogger, Formatter, StreamHandler, FileHandler, DEBUG, INFO, WARNING, ERROR, CRITICAL



### LOG SET UP
DEFAULT_LOG_FILE_NAME = 'log.txt'
DEFAULT_LOG_FORMAT = Formatter('%(asctime)s - %(levelname)s - logger:%(name)s - %(filename)s - L%(lineno)d - %(funcName)s - %(message)s')

# file handler
DEFAULT_FHANDLER = FileHandler(DEFAULT_LOG_FILE_NAME, mode='w')
DEFAULT_FHANDLER.setFormatter(DEFAULT_LOG_FORMAT)
DEFAULT_FHANDLER.setLevel(DEBUG)
# stream handler
DEFAULT_SHANDLER = StreamHandler()
DEFAULT_SHANDLER.setFormatter(DEFAULT_LOG_FORMAT)
DEFAULT_SHANDLER.setLevel(WARNING)

# set up function
def logger_setup(logger_name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_FILE_NAME, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=WARNING, streamformat=DEFAULT_LOG_FORMAT):
    logger = getLogger(logger_name)
    logger.setLevel(level)
    
    # file handler
    fhandler = fhandler or FileHandler(filename, mode=filemode)
    fhandler.setLevel(fhandler_level)
    fhandler.setFormatter(fileformat)
    logger.addHandler(fhandler)
    
    # stream handler
    shandler = shandler or StreamHandler()
    shandler.setLevel(shandler_level)
    shandler.setFormatter(streamformat)
    logger.addHandler(shandler)
    
    return logger



### INDEXES
ROW = 0
COL = 1



### SQUARES STATUS
EMPTY = 0



### DIRECTIONS
UP = [-1, 0]
DOWN = [+1, 0]
LEFT = [0, -1]
RIGHT = [0, +1]



### RETURN VALUES
SUCCEEDED = True
FAILED = False



### DEFAULT VALUES
DEFAULT_PROB4 = 1 / 8
DEFAULT_SIZE = 4  # board size = 4 * 4
DEFAULT_GOAL = 2048
MAX_GOAL = 65536
                        
                    
UNAVAILABLE

クソなげーなおい

冒頭で logging からいろいろインポートしていますね。

                        
from logging import getLogger, Formatter, StreamHandler, FileHandler, DEBUG, INFO, WARNING, ERROR, CRITICAL
                        
                    

logger と logging の違いを明確にするために、logging という文字を書かなくていいようにしています。その意義についてはこちらの記事で大いに力説されていますので、是非ご覧ください。

ログ出力のための print と import logging はやめてほしい

~~~~~~~~~~~~~~~

まずはログの設定をします。

                        
### LOG SET UP
DEFAULT_LOG_FILE_NAME = 'log.txt'
DEFAULT_LOG_FORMAT = Formatter('%(asctime)s - %(levelname)s - logger:%(name)s - %(filename)s - L%(lineno)d - %(funcName)s - %(message)s')

# file handler
DEFAULT_FHANDLER = FileHandler(DEFAULT_LOG_FILE_NAME, mode='w')
DEFAULT_FHANDLER.setFormatter(DEFAULT_LOG_FORMAT)
DEFAULT_FHANDLER.setLevel(DEBUG)
# stream handler
DEFAULT_SHANDLER = StreamHandler()
DEFAULT_SHANDLER.setFormatter(DEFAULT_LOG_FORMAT)
DEFAULT_SHANDLER.setLevel(WARNING)

# set up function
def logger_setup(logger_name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_FILE_NAME, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=WARNING, streamformat=DEFAULT_LOG_FORMAT):
    logger = getLogger(logger_name)
    logger.setLevel(level)
    
    # file handler
    fhandler = fhandler or FileHandler(filename, mode=filemode)
    fhandler.setLevel(fhandler_level)
    fhandler.setFormatter(fileformat)
    logger.addHandler(fhandler)
    
    # stream handler
    shandler = shandler or StreamHandler()
    shandler.setLevel(shandler_level)
    shandler.setFormatter(streamformat)
    logger.addHandler(shandler)
    
    return logger
                        
                    
UNAVAILABLE

こんなに長いの何やるんだよ

大したことないですって。見てけばわかりますから。

~~~~~~~~~~~~~~~

はじめにログを出力するデフォルトのファイルの名前、フォーマットを設定しておきます。

                        
### LOG SET UP
DEFAULT_LOG_FILE_NAME = 'log.txt'
DEFAULT_LOG_FORMAT = Formatter('%(asctime)s - %(levelname)s - logger:%(name)s - %(filename)s - L%(lineno)d - %(funcName)s - %(message)s')
                        
                    

次いで各種ハンドラーのデフォルトの値を宣言します。

                        
# file handler
DEFAULT_FHANDLER = FileHandler(DEFAULT_LOG_FILE_NAME, mode='w')
DEFAULT_FHANDLER.setFormatter(DEFAULT_LOG_FORMAT)
DEFAULT_FHANDLER.setLevel(DEBUG)
# stream handler
DEFAULT_SHANDLER = StreamHandler()
DEFAULT_SHANDLER.setFormatter(DEFAULT_LOG_FORMAT)
DEFAULT_SHANDLER.setLevel(WARNING)
                        
                    

今後ロガーは基本的にこのデフォルトに従って出力しますね。

~~~~~~~~~~~~~~~

下に行くと関数がありますよね。

                        
# logger set up function
def logger_setup(logger_name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_FILE_NAME, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=WARNING, streamformat=DEFAULT_LOG_FORMAT):
    logger = getLogger(logger_name)
    logger.setLevel(level)
    
    # file handler
    fhandler = fhandler or FileHandler(filename, mode=filemode)
    fhandler.setLevel(fhandler_level)
    fhandler.setFormatter(fileformat)
    logger.addHandler(fhandler)
    
    # stream handler
    shandler = shandler or StreamHandler()
    shandler.setLevel(shandler_level)
    shandler.setFormatter(streamformat)
    logger.addHandler(shandler)
    
    return logger
                        
                    
UNAVAILABLE

なんでわざわざ関数なんか作んなあかんの?

今後各ファイルでロガーを作ることになるわけですが、いちいち「ロガーを宣言して、ハンドラーを宣言して、ロガーに加えて、...」っていうのはあまりにも面倒。そこで「いっそのことロガーを作る機能を全部まとめちゃおうじゃないか」という魂胆です。楽になりますよ。

~~~~~~~~~~~~~~~

デフォルトの引数を順に見ていきましょう。

内容デフォルト
logger_name新設するロガーの名前'default'
levelロガーのレベルDEBUG
fhandlerロガーファイルハンドラーNone
fhandler_levelファイルハンドラーに記載する最低レベルDEBUG
filenameログを出力するファイルの名前DEFAULT_LOG_FILE_NAME
filemodeファイルの書き込みモード(新規書き込み 'w' / 上書き 'a')'w'
fileformatファイルに記録するフォーマットDEFAULT_LOG_FORMAT
shandlerロガーストリームハンドラーNone
shandler_levelストリームハンドラーで記録する最低レベルWARNING
streamformatストリームハンドラーで記録するフォーマットDEFAULT_LOG_FORMAT
UNAVAILABLE

さっぱり意味わかんねぇ...

所々にリンクを隠していますので、わからない言葉に当たったら都度解説をご覧ください。なんならここを飛ばして 2-2 に行ってしまうのもアリです。

引数の途中にアスタリスク * が一人でいるのは、後続の引数をキーワード引数にするためです。

                        
def logger_setup(logger_name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_FILE_NAME, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=WARNING, streamformat=DEFAULT_LOG_FORMAT):
                        
                    

実際に引数を入れるときは logger_setup(filemode='a') のように、キーワードと一緒に書く必要があります。

~~~~~~~~~~~~~~~

まずは getLogger でロガーを取得。getLogger の引数はロガーにつける名前ですから、logger_setup の引数にした logger_name を入れるんですね。

                        
def logger_setup(logger_name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_FILE_NAME, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=WARNING, streamformat=DEFAULT_LOG_FORMAT):
    logger = getLogger(logger_name)
    logger.setLevel(level)
                        
                    

直下で logger に setLevel を使ってレベルを設定しています。

今度はファイルハンドラーの設定です。

                        
    # file handler
    fhandler = fhandler or FileHandler(filename, mode=filemode)
    fhandler.setLevel(fhandler_level)
    fhandler.setFormatter(fileformat)
    logger.addHandler(fhandler)
                        
                    

もし引数の時点で使うファイルハンドラーが指定されていれば、それをそのまま fhandler に代入します。一方引数に何も入れられていなければ fhandler は None の状態ですから、FileHandler メソッドファイルハンドラーを新設するという仕組みになっています。

その下でフォーマットを設定します。

                        
    fhandler.setLevel(fhandler_level)
    fhandler.setFormatter(fileformat)
    logger.addHandler(fhandler)
                        
                    

設定が終われば addHandler で logger に適用しましょう。

~~~~~~~~~~~~~~~

ラストはストリームハンドラーを設定しますよ。

UNAVAILABLE

ターミナルとかに出力されるやつやろ

よく勉強してらっしゃるじゃないですか。その通りです。

UNAVAILABLE

イヤミかクソ

                        
    # stream handler
    shandler = shandler or StreamHandler()
    shandler.setLevel(shandler_level)
    shandler.setFormatter(streamformat)
    logger.addHandler(shandler)
                        
                    

やることはファイルハンドラーでやったのと同じです。引数でストリームハンドラーが指定されていればそれを使いますし、指定されていなければ shandler は None ですから、StreamHandler メソッドで新設します。

レベルとフォーマットを設定したら、やはり logger に適用しましょう。

                        
    shandler.setFormatter(streamformat)
    logger.addHandler(shandler)
                        
                    

~~~~~~~~~~~~~~~

これで logger は出来上がりました。この setLogger メソッドを使ってロガーを作りたいわけですから、リターンするのはロガーがいいですね。

UNAVAILABLE

logger = setLogger(...) みたいにしたいってことやろ?

その通りです。ということで、メソッドの最後には

                        
    return logger
                        
                    

を入れておきます。

NEXT 2-2 定数を設定する