search
LoginSignup
2

More than 1 year has passed since last update.

posted at

Pythonの組み込みモジュールの効果的な使い方(1)【Effective Python】

はじめに

Twitterで一時期流行していた 100 Days Of Code なるものを先日知りました。本記事は、初学者である私が100日の学習を通してどの程度成長できるか記録を残すこと、アウトプットすることを目的とします。誤っている点、読みにくい点多々あると思います。ご指摘いただけると幸いです!

今回学習する教材

  • Effective Python

    • 8章構成
    • 本章216ページ
  • 第6章:組み込みモジュール

functiools.wrapsをつかって関数デコレータを定義する

デコレータは、ラップする関数への呼び出しの前後で機能を追加することができます。
例えば、デバッグ時に、関数呼び出しの引数と戻り値を確認したいとします。
そのようなデコレータを定義します。
この際に、functools のwrapsヘルパー関数をwrapper関数に適用することで、
内部関数についてのすべての重要なメタデータが外部関数にコピーできます。

from functools import wraps

def trace(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        print('%s)%r, %r) -> %r' %
                (func.__name__, args, kwargs, result))
        return result
    return wrapper

@trace
def fibonacci(n):
    """Return the n-th Fibonacci number"""
    if n in(0, 1):
        return n
    return (fibonacci(n - 2) + fibonacci(n - 1))
fibonacci(4)

実行結果

fibonacci)(0,), {}) -> 0
fibonacci)(1,), {}) -> 1
fibonacci)(2,), {}) -> 1
fibonacci)(1,), {}) -> 1
fibonacci)(0,), {}) -> 0
fibonacci)(1,), {}) -> 1
fibonacci)(2,), {}) -> 1
fibonacci)(3,), {}) -> 2
fibonacci)(4,), {}) -> 3
help(fibonacci)

実行結果

Help on function fibonacci in module __main__:

fibonacci(n)
    Return the n-th Fibonacci number

contextlibとwith文をtry/finallyの代わりに考える

with文はopenとclose、ロックとリリースなど、開始と終了が必ずセットで必要な処理を簡潔に書くことができます。
例えば、相互排他ロックをwith文と、try/finally文 で書いてみましょう。

from threading import Lock

### with文
lock = Lock()
with lock:
    print('Lock is held')

### with文と等価のtry/finally
lock.acquire()
try:
    print('Lock is held')
finally:
    lock.release()

このようにwithを使うことで、コードを簡潔に書けます。
with文では、contextilibを使うことで、オブジェクトや関数の利用が楽になります。
contextlibのcontextmanagerデコレータは、単純な関数をwith文で使えるようにできます。
例として、2つのレベルでロギングを行う関数を考えます。
プログラムのデフォルトのロギングレベルは、WARNINGなので、この関数を実行したとき、出力されるのは、logging.errorのメッセージのみです。

def my_function():
    logging.debug('Some debug data')       # ロギングレベルWARNINGでは出力されない
    logging.error('Error log here')        # ロギングレベルWARINIGで出力
    logging.debug('More debug data')       # ロギングレベルがWARNINGでは出力されない

my_function()
# Error log here

この関数のロギングレベルをコンテキストマネージャを定義することで、一時的に上げることができます。

@contextmanager
def debug_logging(level):
    logger = logging.getLogger()
    old_level = logger.getEffectiveLevel()
    logger.setLevel(level)
    try:
        yield                               # withブロックの内容が実行される
    finally:
        logger.setLevel(old_level)

with debug_logging(logging.DEBUG):          # ロギングレベルDEBUG
    print('Inside:') 
    my_function()                           # logging.debug, loggin.error ともに出力される
print('After:')
my_function()

実行結果

Inside:
DEBUG:root:Some debug data
ERROR:root:Error log here
DEBUG:root:More debug data
After:
ERROR:root:Error log here

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2