2018/04/02

Python Tips:組み込みの名前を上書きしてしまったのを元に戻したい

Python で組み込み関数等の名前を上書きしてしまったときに元に戻す方法をご紹介します。

Python には、モジュールを import しなくても利用できる組み込みの関数があります。
例えば、 Python 3.6 の場合だと、その数は 60 以上にもなります。

abs() all() any() ascii() bin() bool() bytearray() bytes() callable() chr() classmethod() compile() complex() delattr()
dict() dir() divmod() enumerate() eval() exec() filter() float() format() frozenset() getattr() globals() hasattr() hash()
help() hex() id() input() int() isinstance() issubclass() iter() len() list() locals() map() max() memoryview()
min() next() object() oct() open() ord() pow() print() property() range() repr() reversed() round() set()
setattr() slice() sorted() staticmethod() str() sum() super() tuple() type() vars() zip() __import__()


これらの関数の名前を誤って上書きしてしまうと、元々あった関数の機能を使うことができません。多くの場合は変数名を変えればそれで済むのですが、 REPL や Jupyter を使っていると、 Python プロセスを起動し直す手間が大きく、稀に困ることがあります。

私の場合は、 idmapmax 等のシンプルな名前を変数名として使いたくなって、うっかり上書きしてしまうことがよくあります(他の言語のコードと Python コードを並行で触っているときによくやります)。

max = 10
max(10, 20, 5)
# => TypeError: 'int' object is not callable

結論としては、キーワード del を使って変数を削除すれば OK です。 del した後は元々の名前をまたそのまま利用できるようになります。

max = 10

del max

max(10, 20, 5)
# => 20

組み込みの名前は上書きしないように気をつけることが第一ですが、万が一上書きしてしまったときにはこういう方法があるということを覚えておくと便利です。

参考


2018/03/16

お知らせ:「 Life with Python Books 」を公開しました

サブドメイン books に Life with Python Books という名前でサイトを公開しました。


サイトは、日本語の Python 関連書籍の情報を集めたシンプルなサイトです。

一昔前 Python 本といえば、ひとりですべての本を把握できるぐらいの数だったような気がしますが、近年は Python 需要・ Python 人気の高まりを受けてか Python 関連書籍が多く出版されるようになりました。

最近の状況を見ると、 2017 年は Python 関連書籍が毎月少なくとも 1 冊はコンスタントに出版され、年間通してざっと 40 冊以上出版されたようです。そして 2018 年は 1 〜 3 月の 3 ヶ月間に私が把握しているだけでも 15 冊以上出版される(された)ので、この調子が続くと 2018 年は年間 60 冊以上、去年の 1.5 倍となる見込みです。ここ数年の勢いはすごいものがありますが、海外の Python の人気ぶりなども見るとこの流れは今後少なくとも数年は続きそうな感じがします。

(お断り: ここで「 Python 関連書籍」とは、タイトルに「 Python 」が入っているものだけでなく、何らかの形で Python に関係している「広い意味での Python 関連本」を指しています)

どんどん出版される Python 関連書籍について情報を一箇所にまとめたサイトを作りたいと思い今回作ってみました。

既存の EC サイトや出版社サイトに対する私の印象ですが、 EC サイトは Python 本だけを自分のニーズに合った切り口で検索するということがやりづらく、出版社のサイトは当然その出版社の書籍の情報しか見れず不十分、という印象です。いずれも「 Python 縛りで本を探したい」ときにいまいち使い勝手がよくないイメージでした。

このサイトを使って、私と同じように Python 本に興味がある方が、よりかんたん・スムーズに書籍を探せるようになればいいなと思います。

ただ、サイトの状態としては、まだ、 Python 関連書籍がようやく一通り登録でき(日本で出版されている主要な Python 本は 90% 以上網羅できているはずです)、各書籍の目次がひとまず揃った、というぐらいの状態です。改善の余地はまだまだあるので、タイトルの末尾に「 β 」と付けています。最初から完璧を求めるといつまでも公開できずにそのままお蔵入りになる可能性があったので、 β 版レベルで公開してしまおう、ということになりました。今後継続的に少しずつ改善をはかっていくつもりです。

Python 本に興味のある方はよろしければ使ってみてください。


永遠に β 版のままになる可能性もなきにしもあらず・・・ですが、今の時点では継続的に改善していきたいと思っているので、ご要望やお気づきの点がありましたらコメントや次のフォーム等でお気軽にご連絡ください :)

2018/03/06

Python Tips:関数全体をコンテキストマネージャでかんたんにラップしたい

Python には with 構文で使える「コンテキストマネージャ」という種類のオブジェクトがあります。

with open('log.txt', 'w') as f:
    f.write('これはログです。')

この open() の戻り値の f がコンテキストマネージャです。

このコンテキストマネージャを使って関数をまるごとラップする方法を今回はご紹介してみたいと思います。

結論は「デコレータを使う」です。以下に具体例をあげてご説明していきます。

例として、 Fabric3 ( Python 3 に対応した Fabric のフォーク版)を使った、次のようなケースを考えてみましょう。

fabfile.py:

from fabric.api import cd, run, task

PROJECT_DIR = '/path/to/the/project/root'


@task
def git_status():
    '''`git status` を実行する'''
    with cd(PROJECT_DIR):
        run('git status')


@task
def git_pull():
    '''`git pull` を `master` ブランチに対して実行する'''
    with cd(PROJECT_DIR):
        run('git pull origin master')

ここでは git_status()git_pull() という 2 つの Fabric タスクが定義されています。いずれのタスクも、 PROJECT_DIR で表されたプロジェクトのルートディレクトリで処理を実行する前提となっています。そのために、 fabric.api.cd() を使って with cd(PROJECT_DIR) でコンテキストを作ってその中で実際の処理を実行する形になっています。

この with cd(PROJECT_DIR) の部分は共通です。タスクが 2 つの場合はそれほど気になりませんが、同じ with を使うタスクが増えてくるとこれを共通化したくなることでしょう。後々タスクが増えてきたときのためにコンテキストマネージャを共通化することを考えてみましょう。

結論としては上述のとおり、「関数全体をコンテキストマネージャでラップするデコレータを作る」形がよいかと思います。

実際にデコレータを導入してみた場合のコードは次のとおりとなります。

from functools import wraps
from fabric.api import cd, run, task

PROJECT_DIR = '/path/to/the/project/root'


def decorate_cd(directory):
    '''`fabric.api.cd()` で関数をラップするデコレータを生成する'''
    def decorator(func):
        '''関数をラップするデコレータ'''
        @wraps(func)
        def wrapper(*args, **kwargs):
            with cd(directory):
                func(*args, **kwargs)

        return wrapper

    return decorator


@task
@decorate_cd(PROJECT_DIR)
def git_status():
    '''`git status` を実行する'''
    run('git status')


@task
@decorate_cd(PROJECT_DIR)
def git_pull():
    '''`git pull` を `master` ブランチに対して実行する'''
    run('git pull origin master')

ポイントはもちろん、デコレータを生成する decorate_cd() 関数です。関数が 3 重に入れ子になっていてなんだかややこしそうに見えるので、外側の関数から順にご説明します。

decorate_cd() これは、ディレクトリのパスを受け取ってデコレータとなる関数を返す関数です。この関数自体はデコレータではなく、あくまでも「デコレータ関数を生成する関数」です。


decorator() decorate_cd() によって生成され、実際に関数に対してデコレート処理を行う関数です。関数に対するデコレータは「ただひとつの引数として関数を受け取って戻り値として関数を返す」ものである必要がありますが、この関数は func を引数として受け取り、 wrapper を戻りとして返しています。

wrapper() これは、実際に fabric.api.cd() のコンテキストマネージャで関数をラップする実際にラッパー関数です。

この構造で作られた decorate_cd() を使うことで、関数をかんたんにコンテキストマネージャでラップすることができるようになります。

デコレータで装飾された関数

@decorate_cd(PROJECT_DIR)
def git_status():
    '''`git status` を実行する'''
    run('git status')

は Python では

def git_status():
    '''`git status` を実行する'''
    run('git status')

git_status = decorate_cd(PROJECT_DIR)(git_status)

と同じ意味になるので、 git_status() 関数が無事に fabric.api.cd() でラップできていることになります。

うまく使えると便利かと思います。以上、関数全体をコンテキストマネージャでかんたんにラップする方法についてでした。


参考