2015/11/30

Python Tips:引数のデフォルト値を活用したい

Python では関数の引数としてデフォルトの値を設定することができます。

今回はそのデフォルト値についての注意点をまとめてみます。

まず、デフォルトの値が与えられた引数については省略できるようになります。

import time

def take_nap(duration = 60):
    print('Will sleep for {} seconds...'.format(duration))
    time.sleep(duration)

take_nap()
# => Will sleep for 60 seconds...

デフォルト値が与えられた引数の後にデフォルト値のない引数を置くことはできません。宣言時に SyntaxError が発生します。デフォルト値にない引数の後にデフォルト値のある引数を持ってくるようにしましょう。

def take_nap(duration = 60, callback):
    print('Will sleep for {} seconds...'.format(duration))
    time.sleep(duration)
    callback()

# => SyntaxError: non-default argument follows default argument

デフォルト値にリストや辞書などを指定するのはやめましょう。デフォルト値は関数宣言時に一度だけ評価され、関数の呼び出し時には再利用されるようになっているため、誤って使うと以下のような挙動が発生します。

def print_lines(messages = []):
    prefix_message = 'Messages are:'
    messages.insert(0, prefix_message)
    for m in messages:
        print(m)

messages = ['Ciao', 'Hello', 'KONICHIWA']

print_lines(messages)
# => Messages are:
#    Ciao
#    Hello
#    KONICHIWA
print_lines(messages)
# => Messages are:
#    Messages are:
#    Ciao
#    Hello
#    KONICHIWA

# 'Messages are:' が messages に追加されたままとなり
# 呼び出される度に積み上がっていく

「デフォルト値は関数宣言時に一度だけ評価され、関数の呼び出し時には再利用される」というところがポイントです。 mutable な値だけが問題になるわけではありません。以下のような場合にも思わぬ挙動が発生します。

import datetime
import time

def get_timestamp(target_datetime = datetime.datetime.now()):
    """datetime 型の変数から timestamp を取得
    """
    return target_datetime.timestamp()

print(get_timestamp())
time.sleep(3)
print(get_timestamp())
# 2 回ともまったく同じタイムスタンプがかえってくる

これは datetime.datetime.now() が関数の宣言時に一度だけ呼び出されるために発生します。これが意図した挙動の場合には問題ありませんが、関数の呼び出し時の時刻がかえってくることを期待していると想定外の結果が返ってくるので要注意です。

この動きを防ぐには、デフォルト引数にはあくまでも None を渡しておいて関数の内部で初期化してしまうことが必要になります。

def get_timestamp(target_datetime = None):
    """datetime 型の変数から timestamp を取得
    """
    if not target_datetime:
        target_datetime = datetime.datetime.now()
    return target_datetime.timestamp()

この形で見ると、関数のデフォルト引数もクロージャと同じような挙動をするものとして覚えておくのがいいかもしれません。

2015/11/24

Python のイテレータ生成クラスの使い方

今回は Python のイテレータ生成クラスを使う方法についてご紹介します。

Python でイテレータを生成する関数のことを「ジェネレータ」と呼びますが、関数と同様にクラスを使う形でもイテレータを生成することができます。

具体的には __iter__() メソッドを定義してこれがイテレータを返すようにすれば OK です。
yield を使わない場合は __iter__() とは別に __next__() ( Python 2 の場合は next() )メソッドを定義する必要がありますが、シンプルなケースでは __iter__() と yield だけで十分かと思います。

与えられた整数の因数を返すジェネレータクラスを書いてみます。

# coding: utf-8

# 因数を返すイテレータクラス
class PrimeFactor(object):
   
    // number で対象の数を、 max_count で返す因数の数を指定する
    def __init__(self, number, max_count):
        self.number = number
        self.max_count = max_count

    // クラスをジェネレータ化する
    def __iter__(self):
        count = 0
        for i in range(2, self.number):
            if self.number % i == 0:
                count += 1
                // 指定された数だけ因数が返されたらイテレータ処理を終了するために StopIteration() 例外をあげる
                if count > self.max_count:
                    raise StopIteration()
                yield i

ここで定義した PrimeFactor は以下の形で使うことができます。

pf = PrimeFactor(number = 100, max_count = 5)

# 1 周目
for n in pf:
    print(n, end=", ")
print()
# => 2, 4, 5, 10, 20,

# 2 周目
for n in pf:
    print(n, end=", ")
print()
# => 2, 4, 5, 10, 20,

実装方法の問題でもあるかと思いますが、イテレータを返す関数とのちがいとして、クラスの場合はいったんループが回りきった後にもう一度ループを開始できるという特徴があります。

// PrimeFactor の関数版
def prime_factor(number, max_count):
    count = 0
    for i in range(2, number):
        if number % i == 0:
            count += 1
            if count > max_count:
                raise StopIteration()
            yield i

// こちらはいったんループを回したらその後は StopIteration 例外を出し続けるため
// 2 周目のループは実行されない
pf2 = prime_factor(number = 100, max_count = 5)

# 1 周目
for n in pf2:
    print(n, end=", ")
print()
# => 2, 4, 5, 10, 20,

# 2 周目
for n in pf2:
    print(n, end=", ")
print()
# => 出力なし

ちなみにこのイテレータを生成するクラスが持つインタフェースのことを「イテレータプロトコル( iterator protocol )」と呼んだりもするそうです。

以上です。

参考:

- 3.5 イテレータ型
- python - How to make class iterable? - Stack Overflow

2015/11/17

Python Tips:現在の関数の名前や引数を取得したい

Python で現在の関数の名前を取得する方法をご紹介します。 PHP でいうところの __FUNCTION__ に相当する情報を取得するイメージです。

結論からいうと inspect モジュールの inspect.currentframe() の戻り値の f_code.co_name というプロパティで確認することができます。

import inspect

def my_special_function(*args):
    print("Now in " + inspect.currentframe().f_code.co_name)

if __name__ == '__main__':
    my_special_function()  # => Now in my_special_function

上の例では my_special_function という名称が取得できていることが確認できます。

以上です。

2015/11/11

Python Tips:特定の文字コードの CSV ファイルを読み込みたい

今回は Python で特定の文字コードの CSV をデコードしながら読み込む方法をご紹介したいと思います。

標準ライブラリの csv を使う場合は、セル単位に切り離したときに decode するとよいかと思います。ファイル名と文字コードを指定すると内容を抽出してくれるヘルパー関数を書いてみます。

import csv

def csv_read(filename, encoding):
    """CSV ファイルを文字コードを指定して読み込む
    """
    with open(filename, "rb") as f:
        csvfile = csv.reader(f)
        rows = [[c.decode(encoding) for c in r] for r in csvfile]
        return rows


if __name__ == "__main__":
    # 確認
    csv_filename = "target.csv"
    encoding = 'sjis'
    csv_rows = csv_read(csv_filename, encoding)

ファイルサイズがものすごく大きな場合などにはそれなりの工夫が必要かと思いますが、通常のサイズの CSV ならこれで十分かと思います。

2015/10/29

Python Tips:カレントユーザのホームディレクトリのパスを取得したい

Python でカレントユーザのホームディレクトリのパスを取得する方法をご紹介します。

Python でカレントユーザのホームディレクトリを取得するには os.path.expanduser() 関数を使うのが便利です。

import os.path

print(os.path.expanduser('~'))  
# => /Users/ユーザー名

print(os.path.expanduser('~/.bashrc'))
# => /Users/ユーザー名/.bashrc

公式のドキュメントには以下のように説明されています。

On Unix, an initial ~ is replaced by the environment variable HOME if it is set; otherwise the current user’s home directory is looked up in the password directory through the built-in module pwd. An initial ~user is looked up directly in the password directory.

On Windows, HOME and USERPROFILE will be used if set, otherwise a combination of HOMEPATH and HOMEDRIVE will be used. An initial ~user is handled by stripping the last directory component from the created user path derived above.

Unix では、先頭の ~ は、環境変数 HOME が設定されているならその値に置き換えられます。そうでなければ、現在のユーザのホームディレクトリをビルトインモジュール pwd を使ってパスワードディレクトリから探して置き換えます。先頭の ~user については、直接パスワードディレクトリから探します。

Windows では ~ だけがサポートされ、環境変数 HOME または HOMEDRIVE と HOMEPATH の組み合わせで置き換えられます。

便利ですねー。


参考
linux - How to find the real user home directory using python? - Stack Overflow
10.1. os.path — Common pathname manipulations — Python 2.7.10 documentation
10.1. os.path — 共通のパス名操作 — Python 2.7ja1 documentation

2015/10/26

Python Tips:Python でゼロパディングしたい

今回は Python でゼロパディングする方法をご紹介します。

  • 1. int の場合
  • 2. str の場合


1. int の場合


整数型の場合は string 型の format() メソッドや % 演算子を使う方法が最もシンプルかと思います。

format() メソッド:

number = 50
number_padded = '{0:04d}'.format(number)
print(number_padded)  # => '0050'

% 演算子:

number = 50
number_padded = '%04d' % number
print(number_padded)  # => '0050'

いずれも 04d で「 4 桁よりも短い場合はゼロパディングで 4 桁にする」という意味になります。もともとの数値が 4 桁よりも長い場合にはゼロパディングやトリムなどは行われません。

いずれの場合も 0 ではなく半角空白で詰めたい場合には 04d のところを 4d に変えれば OK です。

2. str の場合


文字列の場合にはそのままずばりなメソッド zfill() というものが用意されています。名前の zfill は「ゼロフィル」の略かと思います。

number_string = '50'  
number_padded = number_string.zfill(4)
print(number_padded)  # => '0050'

文字列の場合は zfill() の他にも同等のメソッドがいくつか用意されています。

# rjust() は元の文字列を右づめで入れて引数で指定された長さまで文字で埋める
number_string = '50'
print(number_string.rjust(8, 'V'))  # => 'VVVVVV50'

# rjust() は元の文字列を左づめで入れて引数で指定された長さまで文字で埋める
number_string = '50'
print(number_string.ljust(8, 'V'))  # => '50VVVVVV'

# rjust() は元の文字列を左づめで入れて引数で指定された長さまで文字で埋める
number_string = '50'
print(number_string.ljust(8, 'V'))  # => '50VVVVVV'

# center() は元の文字列を中央寄せにして、引数で指定された長さになるよう指定された文字を両端に入れる
number_string = '50'
print(number_string.center(8, 'V'))  # => 'VVV50VVV'

以上です。

直感的でわかりやすい API で気持ちがいいですね。

参考
python - Nicest way to pad zeroes to string - Stack Overflow

2015/10/14

Python Tips:関数の定義を調べたい

Python の関数がどのように宣言・定義されているのかを調べる方法をご紹介します。

名前そのままの inspect.getsource 関数を使うと、関数のソースコードを確認することができます。

import inspect

# requests の get 関数を調べる
import requests
inspect.getsource(requests.get)
# =>
#   def get(url, **kwargs):
#       """Sends a GET request.
# 
#       :param url: URL for the new :class:`Request` object.
#       :param \*\*kwargs: Optional arguments that ``request`` takes.
#       :return: :class:`Response ` object
#       :rtype: requests.Response
#       """
# 
#       kwargs.setdefault('allow_redirects', True)
#       return request('get', url, **kwargs)
# 


ドキュメントがいまいちわかりづらい場合なんかには、直接ソースを見にいくよりはこちらの方法で確認した方が手軽で素早いのではないでしょうか。

2015/10/06

Python Tips:zip ファイルをインターネットからダウンロードして利用したい

今回は Pyhton で zip ファイルをインターネットからダウンロードして利用する方法をご紹介したいと思います。

ダウンロードの部分は Python を代表する requests ライブラリ、 zip ファイルの取り扱いの部分は標準ライブラリの zipfile を使う方法がおすすめです。

import requests
import zipfile

まずはダウンロードから見てみましょう。

import requests

# @see http://stackoverflow.com/questions/16694907/how-to-download-large-file-in-python-with-requests-py

def download_file(url):
    """URL を指定してカレントディレクトリにファイルをダウンロードする
    """
    filename = url.split('/')[-1]
    r = requests.get(url, stream=True)
    with open(filename, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024):
            if chunk:
                f.write(chunk)
                f.flush()
        return filename
    
    # ファイルが開けなかった場合は False を返す
    return False

ファイルサイズが大きくてもいいようにするには requests.get の steam オプションや iter_content などを使います。このコードはほぼ丸々上記 URL の Stack Overflow のところのものを使っています。

以下のようなコードで動作確認することができます。

if __name__ == "__main__":
    # 郵便局の郵便番号データをダウンロード
    url = 'http://www.post.japanpost.jp/zipcode/dl/kogaki/zip/ken_all.zip'
    filename = download_file(url)
    if filename:
        print('{} is downloaded.'.format(filename))

つづいて zip ファイルを扱う部分を。こちらは zipfile が提供するクラスと関数を使えばわずか数行です。

import zipfile

def zip_extract(filename):
    """ファイル名を指定して zip ファイルをカレントディレクトリに展開する
    """
    target_directory = '.'
    zfile = zipfile.ZipFile(filename)
    zfile.extractall(target_directory)

こちらも確認してみましょう。

if __name__ == "__main__":
    # 郵便局の郵便番号データをダウンロード
    filename = 'ken_all.zip'
    zip_extract(filename)

問題なく動作することが確認できたら、これらをつなぐとよいでしょう。

あとは展開されたファイルを好きなように利用すれば OK です。便利ですねー。

2015/09/23

Python Tips:- や . などの文字が入ったファイルを import で読み込みたい

Python でファイル名に - や . 、スペースなどが含まれるファイルを読み込む方法をご紹介します。

通常 Python では PEP8 に則るかぎりファイル名に - やスペースなどを使うことはありません。ですのでふだんこのような処理をする必要はありませんが、万が一のときのために - やスペースが含まれるファイルを読み込む方法を知っておくと便利です。

たとえば my-gorgeous-script.py というファイルを読み込みたい場合。通常ライブラリやスクリプトは import で読み込むのですが、 - がマイナスとして認識されてしまうため次のようにエラーが発生してしまいます。

import my-gorgeous-script
# => SyntaxError: invalid syntax

import の代わりに __import__ を使えば OK です。

mgs = __import__('my-gorgeous-script')

このあとは import my-gorgeous-script as mgs という行を実行したときのように mgs という名前をライブラリ名として使うことができます。

ただし参考リンクのリンク先で説明されているように、この方法はできるかぎり使うべきではありません。「こういうことができる」ことを知っておくにとどめて、実際に使うのは避けた方がよいでしょう。


Package and Module Names Modules should have short, all-lowercase names. Underscores can be used in the module name if it improves readability. Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.

Since module names are mapped to file names, and some file systems are case insensitive and truncate long names, it is important that module names be chosen to be fairly short -- this won't be a problem on Unix, but it may be a problem when the code is transported to older Mac or Windows versions, or DOS.


意訳:

パッケージとモジュールの名前はすべて小文字の短い名前にすべきです。読みやすさが向上するなら、アンダースコアを使ってもかまいません。 Python パッケージも同じくすべて小文字の短い名前にすべきですが、アンダースコアの使用は非推奨となっていみあす。

モジュール名はファイル名にマップされますし、ファイルシステムによっては大文字小文字の区別を行わず長い名前を断ち切ってしまうので、モジュール名にはかなり短いものを選ぶということが重要です。 --- これは Unix では問題になりませんが、コードが古いバージョンの Mac や Windows 、 DOS に移されたときに問題になることがあります。

以上です。

import 文に興味のある方には次のページも参考になるかもしれません。

import の使い方


参考
naming - Is it ok to use dashes in Python files when trying to import them? - Stack Overflow
Package and Module Names - PEP 0008 -- Style Guide for Python Code | Python.org

2015/09/16

Python Tips:数値リテラルに対して直接メソッドを呼び出したい

Python で数値リテラルから直接メソッドを呼び出す方法をご紹介します。

Python では数値リテラルにかぎらずリテラルから直接メソッドを呼び出す機会(=必要性)はあまりありませんが、言語としてはそれが可能なように作られています。

'hello'.upper()  # => 'HELLO'
'hello world'.title()  # => 'Hello World'

str 型の join() メソッドは比較的よく使いますよね。

message = ','.join(['a', 'b', 'c'])  # => 'a,b,c'

あまり見る/使う機会はありませんが、数値リテラルでも直接メソッド呼び出しが可能です。ただし数字の場合は . が小数点のドットとみなされてしまうため、 () で囲う必要があります。

3.real
# => SyntaxError: invalid syntax

(3).real  # => 3
(3 + 4j).imag  # => 4.0

その必要がある場合はなかなかありませんが、ここぞというときに使えるようにしておくと便利かもしれません。

2015/09/03

Python Tips:pip で最新版でないパッケージを確認したい

Python のパッケージ管理ツール pip で、ローカルにインストールされているパッケージのうち最新版でないものを確認する方法をご紹介します。最新版でないものは `pip list` に `--outdated` オプションを渡すと確認することができます。

$ pip list --outdated

逆に最新版のもののみをリストにしたい場合は `--uptodate` オプションを使いましょう。

$ pip list --uptodate

参考
pip list — pip 公式ドキュメント

2015/08/25

Python Tips:複数の辞書を統合してひとつにしたい

Python でふたつ以上の辞書をひとつにまとめる方法をご紹介します。「ふたつにまとめる」というときに、重複するキーがあった場合の挙動によっていくつかのパターンに分けることができます。

ひとつめの辞書を優先する
ふたつめの辞書で上書きする
何らかの方法でまとめる

以下順番にやり方を見ていきましょう。

ひとつめの辞書を優先する


まずはひとつめの「ひとつめの辞書を優先する」方法から。これは単純に重複チェックをする形になりそうです。

d1 = {'a':3, 'b': 4}
d2 = {'a':1, 'b': 2, 'c': 3}

def merge_dict1(d1, d2):
    d_merged = d1.copy()
    for k, v in d2.items():
        if not k in d_merged:
            d_merged[k] = v
    return d_merged

d3 = merge_dict1(d1, d2)
print(d3)
# => {'c': 3, 'b': 4, 'a': 3}

ふたつめの辞書で上書きする


つづいてふたつめの「ふたつめの辞書で上書きする」方法。こちらには dict 型の update メソッドが便利です。

d1 = {'a':3, 'b': 4}
d2 = {'a':1, 'b': 2, 'c': 3}

def merge_dict2(d1, d2):
    d_merged = d1.copy()
    d_merged.update(d2)
    return d_merged

d3 = merge_dict2(d1, d2)
print(d3)
# => {'c': 3, 'b': 2, 'a': 1}

何らかの方法でまとめる


最後に、どちらかの要素だけを使うのではなく両方の要素を何らかの方法でまとめる方法を見てみます。たとえば、同じ要素が存在する場合はそれらの合計値を取る場合は次のようにします。

from operator import add

d1 = {'a':3, 'b': 4}
d2 = {'a':1, 'b': 2, 'c': 3}

def merge_dict3(func, d1, d2):
    d_merged = d1.copy()
    for k, v in d2.items():
        if k in d_merged:
            d_merged[k] = func(d_merged[k], v)
        else:
            d_merged[k] = v
    return d_merged

d3 = merge_dict3(add, d1, d2)
print(d3)
# => {'c': 3, 'b': 6, 'a': 4}

ここで func や operator.add を使う必要性はありませんが、この関数定義にしておくことで汎用性を持たせることができます。たとえば merge_dict3 は次のように使いまわせます。

d4 = merge_dict3(lambda a, b: a + b, d1, d2)
print(d4)
# => {'c': 3, 'b': 6, 'a': 4}

d5 = merge_dict3(lambda a, b: a * b, d1, d2)
print(d5)
# => {'a': 3, 'b': 8, 'c': 3}

別のアプローチとして、加算の場合には collections.Counter クラスを経由してシンプルに記述することもできます。

from collections import Counter

d1 = {'a':3, 'b': 4}
d2 = {'a':1, 'b': 2, 'c': 3}

def merge_dict4(d1, d2):
    c1 = Counter(d1)
    c2 = Counter(d2)
    return dict(c1 + c2)

d3 = merge_dict4(d1, d2)
print(d3)
# => {'b': 6, 'a': 4, 'c': 3}

参考
python - Is there any pythonic way to combine two dicts (adding values for keys that appear in both)? - Stack Overflow

2015/08/20

Python Tips:インタラクティブシェルの内容を保存したい

Python でインタラクティブシェルの内容を保存する方法をご紹介します。

引数をつけずに `python` コマンドを実行すると起動するデフォルトのインタラクティブシェルにはそのような機能がないため、 IPython を使うのがよいでしょう。 IPython にはシェルの内容を保存する機能が備わっています。

$ ipython
In [1]: a = 2
In [2]: b = 3
In [3]: c = a + b
In [4]: %save saved_session.py 1-3
The following commands were written to file `saved_session.py`:
a = 2
b = 3
c = a + b

IPython のシェルの中で %save にふたつの引数を渡します。ひとつめは保存先のファイルの名前、ふたつめには対象行を指定します。対象行は - で範囲を決めて指定したり空白で区切って複数個与えたりすることができます。ですので、上記の %save の行は次のように書いても同じ結果となります。

In [4]: %save saved_session.py 1 2 3

ipython は Python のパッケージ(ライブラリ)なので pip などでインストールすることができます。 ipython が入っていない場合は以下のコマンドでインストールしてみてください。

$ pip install ipython

以上です。

2015/08/12

Python Tips:PHP でシリアライズされたデータを Python で扱いたい

今回は PHP でシリアライズされたデータを Python で扱う方法をご紹介します。

PHP でシリアライズされたデータを Python で扱うときはその名も phpserialize というライブラリを使うのが便利です。

phpserialize : Python Package Index

基本的な使い方を見ていきましょう。まずは最も基本となる「 PHP 側でシリアライズされたデータを読み込む方法」から。

文字列の場合はこんな感じになります。

# ライブラリの読み込み
from phpserialize import loads

# PHP でシリアライズされたデータの文字列を読み込む
serialized1 = 's:11:"Hello world";'
loaded1 = loads(serialized1)
print(loaded1)  
# => Hello world
# Python の文字列型として取得できる

つづいて連想配列の場合はこちら。

serialized2 = 'a:2:{s:1:"a";b:1;s:1:"b";i:345;}'
loaded2 = loads(serialized2)
print(loaded2)  # => {'a': True, 'b': 345}

次はインデックス配列の場合。 PHP ではインデックス配列と連想配列が統一的に扱われるので、インデックス配列の場合でも Python 側では統一的に dict として解釈する仕様になっています。この場合、元のデータがインデックス配列とわかっている場合には dict_to_list 関数を使って list に変換することができます。

from phpserialize import loads, dict_to_list

serialized3 = 'a:3:{i:0;i:3;i:1;i:7;i:2;i:5;}'
loaded3 = loads(serialized3)
print(loaded3)  
# => {0: 3, 1: 7, 2: 5}
# 基本的に辞書が返される

loaded3_list = dict_to_list(loaded3)
print(loaded3_list)  
# => [3, 7, 5]
# dict_to_list でリストに変換可
# タプルに変換したい場合は同様の関数 dict_to_tuple が使えます

つづいて、 loads とは逆に PHP 形式にシリアライズする関数も見てみましょう。 loads の逆の操作を行う関数として dumps というものが用意されています。

from phpserialize import dumps

print(dumps('Hello world'))  # => 'a:2:{s:1:"a";b:1;s:1:"b";i:345;}'
print(dumps({'a': True, 'b': 345}))  # => 's:11:"Hello world";'

さらにもうひとつだけ見てみましょう。
loads と dumps に似た機能を持つ関数として load 、 dump という 2 つの関数が用意されています。これらは文字列ではなくファイルのようなストリームオブジェクトを扱う関数です。

from phpserialize import load, dump

# ファイル php.txt から PHP でシリアライズされたデータを読み込み
loaded3 = load(open('php.txt'))

# Python データを PHP のシリアライズフォーマットでファイルに書き込み
dump(open('php2.txt', 'w'), loaded3)

以上です。

PHP と Python 間でデータの受け渡しをする場合には JSON 形式などが便利かと思いますが、 PHP でシリアライズされたデータをどうにかして Python で扱いたいという場合にはこちらの phpserialize が有用かと思います。


参考
How to extract data from a post meta serialized array? - WordPress Development Stack Exchange
phpserialize : Python Package Index

2015/07/29

Python Tips:pip そのものをアップデートしたい

Python のパッケージ管理コマンドである pip 自身をアップデートする方法をご紹介します。

pip は pip そのものが PyPI パッケージとなっているので pip コマンドでアップデートすることができます。 PyPI パッケージをアップデートするには `pip install` に `--upgrade` オプションを付けて実行します。

$ pip install --upgrade pip

しばらく気づきませんでしたが、 2014 年の年末頃のアップデートから pip のバージョンが 1.5 から 6.0 となり、最初の 1 を取った形でのバージョン表記になったようです。引き続き活発に開発が進められているようで頼もしいです。

参考
python - How do I update pip itself from inside my virtual environment? - Stack Overflow
pip 公式ドキュメント

2015/07/16

Python Tips:Requests ライブラリのレスポンスボディの文字化けを解消したい

シンプルで使いやすい Python の HTTP リクエスト用ライブラリ Requests を使っているときに文字化けを解消する方法をご紹介します。

import requests

URL = 'http://www.1101.com/home.html'

r = requests.get(URL)

if r.status_code == 200:
    print(r.text.encode('utf-8'))  # => 文字化けしたテキスト

    # 文字化けの原因はエンコーディング
    print(r.encoding)  # => 'ISO-8859-1'

    # エンコーディングを変更すると
    # 自動で文字化けが解消されたテキストが取得できるようになる 
    # エンコード utf-8 の部分は状況に応じて変更すれば OK
    r.encoding = 'Shift_JIS'
    print(r.text.encode('utf-8'))

気をつけるべきポイントとしては、 requests の Response オブジェクトのプロパティ text は unicode にデコードされている形がデフォルトという点です。
Python2.7 の場合( Python3 の場合は未確認です )、 HTTP レスポンスの生のボディを格納した content プロパティは str 型であるため、このあたりのちがいに注意が必要です。

import requests

URL = 'http://www.1101.com/home.html'

if r.status_code == 200:
    r = requests.get(URL)
    print(type(r.text))  # => unicode
    print(type(r.content))  # => str


参考
Response Content — Requests 公式ドキュメント

2015/07/08

Python で基本アルゴリズム:バブルソート

Python でバブルソートを行うコードをご紹介します。

# coding: utf-8

def bubble_sort(li):
    """バブルソートでソートしたリストを返す
    """
    li_sorted = li[:]

    for i in range(0, len(li_sorted)):
        for j in range(1, len(li_sorted) - i):
            if li_sorted[j - 1] > li_sorted[j]:
                swap(li_sorted, j)
                # 確認用に途中経過を表示する場合は次の行のコメントを外す
                # print(li_sorted)

    return li_sorted

def swap(li, index):
    """リストの隣り合った要素 index -1 & index の値を入れ替える
    """
    li[index], li[index - 1] = li[index - 1: index + 1]


if __name__ == '__main__':
    # テスト
    print(bubble_sort([8, 5, 2, 1]))  # => [1, 2, 5, 8]
    print(bubble_sort([1, 5, 3]))     # => [1, 3, 5]

swap の部分は必ずしも切り出す必要はないかと思います。


参考
バブルソート - Wikipedia

2015/07/07

Python Tips: Web サービスの JSON を処理したい

HTTP リクエストに対して JSON 形式でデータや処理の結果を返すタイプのサービスを Python で利用する方法をご紹介します。

私が知るかぎり、最もかんたんな方法は requests ライブラリを使う方法です。

import requests

URL_JSON = 'http://www.lifewithpython.com/feeds/posts/default?alt=json'

r = requests.get(URL_JSON)

# 無事レスポンスが返ってきたら中身を出力
if r.status_code == 200:
    # json を取得
    j = r.json()

    # キーを表示
    print(j.keys())  # => [u'feed', u'version', u'encoding']

    # 各エントリのタイトルを一覧で表示
    entries = j['feed']['entry']
    for e in entries:
        print(e['title']['$t'])

requests ライブラリの get 関数などが返す requests.models.Response オブジェクトには json() というメソッドがあり、これは JSON の中身をパース済みの値となっています。具体的には list 型や dict 型です。

2015/06/24

Python Tips:Python のバージョンによって処理を変えたい

Python において、 Python のバージョンによって処理を変更する方法をご紹介します。

結論としては sys.version オブジェクトを利用する方法が最も Pythonic でシンプルかと思います。

# Python 2.x でなければ現在の Python のバージョンを表示して処理を終了する

import sys

if sys.version_info.major != 2:
    sys.stderr.write('Please use python 2.7. Your python version is as following:\n')
    sys.stderr.write('{}\n'.format(sys.version))
    exit()

# 以下メインの処理

上のコード内でも使用していますが、 sys.version にはバージョン情報を表す文字列が入っています。また、 sys.version_info にはバージョン情報に関係するさまざまな情報が含まれています。

import sys

# 2.7.6 の場合
prtin(sys.version_info)
# => '2.7.6 (default, Sep  9 2014, 15:04:36) \n[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)]'

print(sys.version.major)  # => 2
print(sys.version.minor)  # => 7
print(sys.version.micro)  # => 6

以上です。

2015/06/17

ライブラリ:Fabric

Python のパッケージである Fabric をご紹介したいと思います。

import fabric

Fabric とは


Fabric はいわゆる「デプロイツール」と言われるもののひとつで、 ssh での一連の処理を自動化するためのライブラリです。
Python らしいかんたんなインタフェースとなっており、比較的スムーズに使いはじめることができるのが特徴です。
同種のツールとしては Ruby の Capistrano があります。

公式ページには次のように紹介されています。

Fabric is a Python (2.5-2.7) library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks.

It provides a basic suite of operations for executing local or remote shell commands (normally or via sudo) and uploading/downloading files, as well as auxiliary functionality such as prompting the running user for input, or aborting execution.

意訳すると次のような感じになるでしょうか。

Fabric は Python (2.5-2.7) のライブラリで SSH を使いやすくするしてくれるコマンドラインツールです。アプリケーションのデプロイやシステム管理のために使うものです。

ローカル・リモートでのシェルコマンドの実行(通常のものと sudo 付きのもの)やファイルのアップロード・ダウンロードのための基本的な処理のセットを提供します。ユーザからの入力受付のためのプロンプトや処理の中断などの補助的な機能もあわせて提供します。

私が思う Fabric を使うことのメリットは次のとおりです。

- 基本的なシェルコマンドを知っていれば全体の流れは Python 上で書ける
- 分岐やループ処理がかんたん
- 例外・エラーの処理がかんたん
- 準備がかんたん: ローカルで fabric をインストールすればすぐに使い始められる

では基本的な使い方をかんたんにご紹介していきます。

インストール


まずはパッケージをインストールする必要があります。記事執筆時点の私の Mac 環境では pip だけでかんたんにインストールできました。

$ pip install fabric

Fabric で Hello World


では最初の Hello World をしてみましょう。
基本的な流れとしては、まずは fabfile.py をカレントディレクトリに作成して、その中の処理を fab コマンド呼び出す、という形になります。

まずは以下のコードを fabfile.py に保存しましょう。

# coding: utf-8
from fabric.api import local, run

# ホスト名を指定 
# 複数個渡せばそのそれぞれに対して同じ処理が実行される
env.hosts = ['your_host_name']

def hello_world():
    # ローカル環境で echo を実行
    local('echo "Hello, world"')
    # リモート環境で echo を実行
    run('echo "Hello, world"')

つづいて fab コマンドでこちらを実行します。

$ fab hello_world

fab コマンドはこの fabric が提供するコマンドで、カレントディレクトリの fabfile.py の中の関数をコマンドラインから呼び出す機能を提供します。

上記のコマンドを実行するとまず最初にローカル環境で Hello, world がエコーされ、つづいてリモートホスト your_host_name 上で Hello, world がエコーされます。リモートに接続する際には通常の SSH が開始されるときの処理が自動的に走ります。パスワードで入る設定の場合にはパスワード入力のプロンプトが現れ
公開鍵で入る設定の場合にはそのまま SSH 接続が確立されます。

fabfile.py 内の関数は引数を受け取ることもできます。

def hello(name):
    # ローカル環境で echo を実行
    local('echo "Hello, {}"'.format(name))
    # リモート環境で echo を実行
    run('echo "Hello, {}"'.format(name))

引数に渡す値(文字列)を指定するには fab コマンドで関数名の後に「 : 」を付けてその後に渡します。

$ fab hello:japan

Fabric の基本的な関数


基本的な関数を覚えておけば、大体の処理は行うことができるかと思います。

- local
- run
- lcd
- cd
- get
- put

from fabric.api import local, run, lcd, cd, get, put

# ローカルでコマンドを実行
local('コマンド')  

# リモートでコマンドを実行
result = run('コマンド')  

# ローカルの特定のディレクトリでコマンドを実行
with lcd('ディレクトリ'):  
    local('コマンド')

# リモートの特定のディレクトリでコマンドを実行
with cd('ディレクトリ'):  
    result = run('コマンド')

# SFTP でリモートからローカルにファイルをコピー
get('リモートのパス', 'ローカルのパス')

# SFTP でローカルからリモートにファイルをコピー
put('リモートのパス', 'ローカルのパス')

## 処理を終了
abort('これ以上何もできません')

ファイルの存在チェックなども行えます。

from fabric.contrib import files
from fabric.api import abort

# filename ファイルがすでに存在すれば処理を中断して終了する
if files.exists(filename):
    abort('File {} already exists'.format(filename))

以上です。


参考
Welcome to Fabric! — Fabric documentation

2015/06/11

Python Tips:複数の辞書を統合したい

Python で 2 つの辞書を統合する方法をご紹介します。

基本的には 辞書型の update メソッドを使う形が最もシンプルかつかんたんな方法です。

land_animal = {'uma': 3, 'ushi': 5, 'kame': 7}
sea_animal = {'kujira': 10, 'kame': 12}

land_animal.update(sea_animal)

print(land_animal) # => {'kame': 12, 'kujira': 10, 'uma': 3, 'ushi': 5}

update は Ruby でいうところの破壊的メソッドで、レシーバそのものを変更してしまう点に注意が必要です。上の例では land_animal が書き換えられます。 land_animal を残しておきたい場合は copy してから update するのがよいでしょう。

all_animal = land_animal.copy()
all_animal.update(sea_animal)

ちなみに update 関数は他の辞書を受け取る形だけでなくキーワード引数を渡す形で使うことも可能です。

land_animal = {'uma': 3, 'ushi': 5, 'kame': 7}

land_animal.update(uma=10)
print(land_animal)  # => land_animal = {'uma': 10, 'ushi': 5, 'kame': 7}

辞書型を同じキーを持つ要素の値を足し合わせる形で使うには collections.Counter を使うのが便利です。 collections.Counter クラスのインスタンスでは辞書型にはない「 + 」演算子がサポートされています。

from collections import Counter    

land_animal = {'uma': 3, 'ushi': 5, 'kame': 7}
sea_animal = {'kujira': 10, 'kame': 12}

land_animal_counter = Counter(land_animal)
sea_animal_counter = Counter(sea_animal)
all_animal_counter = land_animal_counter + sea_animal_counter

all_animal = dict(all_animal_counter)


collections — High-performance container datatypes — Python 公式ドキュメント

2015/06/03

Python Tips:type() と isinstance() をうまく使い分けたい

Python には type() と isinstance() というよく似た関数が存在します。今回はそれらの使い分け方法についてご紹介します。

結論からいうと、次のようなちがいがあります。

- type() そのインスタンスを生成したクラスを返す
- isinstance() ふたつの引数がオブジェクトとクラスまたはそのスーパークラスの関係にあれば True を返す

class Onitsuka(object):
    pass

class Puma(Onitsuka):
    pass


p1 = Puma()

# type は直接そのインスタンスのクラスを返す
print(type(p1) == Puma)      # => True
print(type(p1) == Onitsuka)  # => False

# isinstance 継承元も含めてチェックする
print(isinstance(p1, Puma))      # => True
print(isinstance(p1, Onitsuka))  # => True

以上です。

ちなみに、クラス間の継承関係をチェックするには issubclass() 関数やクラスメソッド mro() を使うことができます。

Python Tips:クラスの継承関係をチェックしたい

Differences between isinstance() and type() in python
- StackOverflow

2015/05/31

Python Tips:変数が定義されたかどうかを調べたい

Python で変数が定義されているかどうかをチェックする方法をご紹介します。

最もシンプルなのは例外をキャッチする方法かと思います。

# 変数 abc があるかどうかをチェック
try:
    abc
except NameError:
    # なかった場合の処理

ただ変数がそのスコープにあるかどうかだけでなく、もう少し厳密にチェックしたい場合には locals() globals() が使えます。 locals() はローカルスコープの名前を、 globals() はグローバルスコープの名前一覧を格納したリストです。

# ローカルスコープでチェック
if 'abc' in locals():
    # あった場合の処理
else:
    # なかった場合の処理

# グローバルスコープでチェック
if 'abc' in globals():
    # あった場合の処理
else:
    # なかった場合の処理

ちなみに、変数名ではなく、クラスが特定の名前のプロパティやメソッドを持つかどうかをチェックしたい場合には hasattr() や dir() が利用できます。

class C(object):
    def __init__(self):
        self.abc = 0

c = C()

# c に abc というメンバがあるかどうかをチェック
if hasattr(c, 'abc'):
    print(c.abc)

# 同上
if 'abc' in dir(c):
    print(c.abc)

以上です。

2015/05/26

Python Tips:文字列の中の文字をカウントしたい

Python で文字列中の各文字の出現回数をカウントする方法をご紹介します。

dict でカウント

まずは、最もシンプルな dict 型を使った方法から。

string = 'supercalifragilisticexpialidocious'

counter = {}
for c in string:
    if not c in counter:
        counter[c] = 1
    else:
        counter[c] += 1

print(counter)  # => {'a': 3, 'c': 3, 'e': 2, ...

defaultdict でカウント

つづいて、 collections ライブラリの defaultdict を使った方法。デフォルト値を決められる分、よりシンプルに書くことができます。

from collections import defaultdict

string = 'supercalifragilisticexpialidocious'

# デフォルト値が 0 の辞書を定義してカウンタとして使う
counter = defaultdict(lambda: 0)
for c in string:
    counter[c] += 1

print(dict(counter))  # => {'a': 3, 'c': 3, 'e': 2, ...

Counter でカウント

さらに Python 2 系では Python 2.7 から導入された Counter クラスを使った方法もあります。
こちらは Counter の名前のとおり、カウンタとしてそのまま利用できる機能を備えたクラスなので、さらにシンプルにカウンタを実現することができます。

from collections import Counter

string = 'supercalifragilisticexpialidocious'

# string を渡して Counter インスタンスを生成
counter = Counter(string)

print(counter['a'])  # => 3
print(dict(counter))  # => {'a': 3, 'c': 3, 'e': 2, ...

以上です。


参考
Python Tips:辞書にアイテムが存在しない場合のデフォルト値を設定したい
defaultdict — Python 公式ドキュメント
Counter — Python 公式ドキュメント
Counting repeated characters in a string in Python - Stack Overflow

2015/05/20

Python Tips:標準入力からパスワード入力を受け付けたい

Python で標準入力でパスワードを入力してもらう方法をご紹介します。

パスワードを入力させるときには、通常標準入力を受け付けるときとは異なり、入力内容を画面に表示しないようにしたいものです。

パスワードを入力してもらうときには標準添付の getpass ライブラリを使うのが便利です。

[getpass — Portable password input — Python 公式ドキュメント](https://docs.python.org/2/library/getpass.html)

getpass ライブラリには raw_input() 関数と同じような形で使える getpass() が入っています。

# coding: utf-8
from getpass import getpass

# 名前とパスワードの入力を受け付ける
name = raw_input('your name: ')
password = getpass('your password: ')

以上です。

余談ですが、 raw_input() は Python 2 の関数です。
Python 3 で同様のことをするには input() 関数を使うとよいでしょう。

2015/05/07

Python Tips:辞書にアイテムが存在しない場合のデフォルト値を設定したい

Python で辞書にアイテムが存在しない場合のデフォルト値の設定方法をご紹介します。

Python の辞書を使っていると、次のような処理をしたくなるときがあるかと思います。

# 各朝食メニューの回数を調べる
breakfast = ['kome', 'pan', 'kome', 'kome', 'cereal', 'pan', 'kome']

numbers = dict()
for b in breakfast:
    if b in numbers:
        numbers[b] += 1
    else:
        numbers[b] = 1

print(numbers)
# => {'pan': 2, 'kome': 4, 'cereal': 1}

この処理はもちろん間違っていませんが、もっとシンプルにわかりやすく書きたい場合はデフォルト値を使うのが便利です。デフォルト値を使うにはいくつかの方法があります。 2 つの方法を以下にご紹介します。

方法その 1. get メソッドを使う方法


辞書の get メソッドを使うと上記の for ループは次のようにコンパクトに書き換えることができます。

for b in breakfast:
    numbers[b] = numbers.get(b, 0) + 1

get メソッドは、第 1 引数に取得したいアイテムのキーを、第 2 引数にそのアイテムが見つからなかった場合のデフォルト値を受け取ります。戻り値は [] と同じです。

方法その 2. defaultdict


もうひとつの方法は、その名も defaultdict というデフォルト値つきの辞書型を利用する方法です。 defaultdict は組み込みの collections ライブラリに含まれています。 defaultdict を使うと上記のコードは次のように書き換えることができます。

# 各朝食メニューの回数を調べる
from collections import defaultdict

breakfast = ['kome', 'pan', 'kome', 'kome', 'cereal', 'pan', 'kome']

numbers = defaultdict(lambda: 0)
for b in breakfast:
    numbers[b] += 1

print(dict(numbers))
# => {'pan': 2, 'kome': 4, 'cereal': 1}

defaultdict を生成する際に関数やクラスなど callable なオブジェクトを渡すと、キーが見つからなかった場合にそのオブジェクトが呼び出されます。 numbers = defaultdict(lambda: 0) のところは次のように書き換えることも可能です。

numbers = defaultdict()
numbers.default_factory = lambda: 0

参考
dict.get — Python 公式ドキュメント
collections.defaultdict — Python 公式ドキュメント

2015/05/02

import の使い方

Python の import 文の使い方をご紹介します。

Python で他のモジュールやパッケージを読み込む方法といえば import ですが、その import にもいろいろな使い方があります。以下、いくつか代表的な使い方を見てみましょう。

ライブラリ全体を読み込む


ライブラリ全体を読み込みたい場合は import ライブラリ名 と書きます。

import xyz

xyz.a_func()  # xyz の中で定義された関数 a_func() を呼び出す

ライブラリのうち一部の関数やクラス、定数だけを読み込む


ライブラリのうち使用する関数などが限られている場合は、 from ライブラリ名 import 関数名 という形で一部のみ取得することができます。

from xyz import a_func

a_func()  # xyz の中で定義された関数 a_func() を呼び出す

ライブラリの中にある名前をすべて読み込む


from ライブラリ名 import * と書けば、そのライブラリの中のすべての名前をインポートすることができます。ただしアンダースコア 2 つで囲われた __abc__ という名前のものはプライベート扱いになるためインポートされません。

from xyz import *

a_func()  # xyz の中で定義された関数 a_func() を呼び出す

また、この方法は言語仕様として可能は可能なのですが、 Python PEP20 の「 explicit rather than implicit 」(「暗黙」よりも「明示」)というポリシーを尊重するなら使うべきではありません。

独自の名前をつけて読み込む


import xxx の後に as エイリアス とつけると、インポートしてくる名前に別名をつけることができます。名前の衝突を防ぎたい場合や短縮形を使いたい場合に便利です。

from xyz import a_func as f

f()  # xyz の中で定義された関数 a_func() に別名 f() をつけて呼び出す

その他の方法


使用頻度は比較的低そうですが、その他の使い方も見ておきましょう。

相対パスを使って読み込む

Python 2.5 からは相対パスを使うこともできます。

次のようなディレクトリ構造で、 main から b.py を呼び出し、その b.py の中から a.py を利用したい場合を考えます。

main.py
lib/
  __init__.py
  a.py
  subdir/
    __init__.py
    b.py

この場合、 main の中で b.py を読み込む部分と b.py の中で a.py を読み込む部分は次のように書くことができます。

# main.py
# b.py を読み込む
from lib.subdir import b

# b.py
# a.py を読み込む
from .. import a

ただしこの方法は __init__.py を使ってパッケージ化されている場合でないと使えないようです。

普通のスクリプトの中でどうしてもひとつ上の階層の Python ファイルを読み込みたい場合にはパスを追加する形で実現することができます。

import os
import sys

path = os.path.join(os.path.dirname(__file__), '../')
sys.path.append(path)

ただしこの方法は推奨されていないようなので、「やむなく」な場合のみとするのがよさそうです。

import キーワードではなく関数を使って読み込む

import キーワードと同等の働きをする import.import_module という関数を使うこともできます。こちらは、読み込むライブラリ名が動的に決まるとき(?)なんかに便利でしょうか。

import importlib

# 3 つのライブラリを読み込む
libs = ['sys', 'os', 'math']
for lib in libs:
    importlib.import_module(lib)

以上です。

import に興味のある方は次のページも参考になるかもしれません。

Python Tips:- や . などの文字が入ったファイルを import で読み込みたい


参考
- 6. Simple statements — Python 2.7.8 documentation
- 30.2. importlib – Convenience wrappers for __import__() — Python 公式ドキュメント
- Python - how to refer to relative paths of resources when working with code repository - Stack Overflow
- Relative paths in Python - Stack Overflow
- Python Packages and You - _habnablog

2015/04/28

Python Tips: Python で簡易サーバを走らせる

Python で簡易サーバを走らせる方法をご紹介します。

ターミナルからの python コマンドでサーバを立ち上げることができます。
Python 2 と Python 3 では組み込みのライブラリの構成が異なっているためコマンドが若干異なります。

Python 2


python2.7 -m SimpleHTTPServer 8080


Python 3


python -m http.server 8080



いずれも末尾の 8080 の部分はポート番号です。このサンプルでは 8080 としていますが特に 8080 である必要はなく、利用したいポート番号を指定することができます。ポート番号を省略した場合はいずれも 8000 がデフォルトで選択されるようになっています。

2015/04/24

Python Tips:string の format 内で {} を出力したい

Python の format で {} を出力する方法をご紹介します。

まず python の format とは何のことを言っているかといいますと、次の文字列の format メソッドのことを指しています。

print('hello, {}'.format('world'))  # => hello world
print('hello, {}'.format('goodbye'))  # => hello goodbye

フォーマットのテンプレートとなる文字列内では、変数のプレースホルダとして {} を使うことになっています。ですので、次のような形で {} を使おうと思ってもエラーが発生してしまいます。

print('I like ruby block syntax like: {:d}.times { |i| puts i }'.format(5))
# => KeyError Traceback (most recent call last)
# KeyError: ' |i| puts i '

プレースホルダではなく通常の文字として {} を使いたい場合には、記号をふたつ重ねて {{ か }} を使います。

print('I like ruby block syntax like: {:d}.times {{ |i| puts i }}'.format(5))
# => I like ruby block syntax like: 5.times { |i| puts i }

以上です。

その他 format にはパッと見では把握しきれないくらい豊富なオプションが用意されているので、詳しく知りたい方は公式ドキュメントの方にあたってみてください。


参考
- フォーマットのシンタックス - Python 公式ドキュメント
- フォーマットの例 - Python 公式ドキュメント
- Python format throws KeyError - Stack Overflow

2015/04/15

Python Tips:バイセクションサーチを行いたい

Python でバイセクションサーチ(二分探索法)を行う方法をご紹介します。

Python にはそのままずばり「 bisect 」――バイセクションサーチを行うためのライブラリが用意されています。

実際の使い方を見てみましょう。

from bisect import bisect_left, bisect_right

li = [1, 3, 5, 6, 10, 14, 17, 22, 30, 35]

index_left = bisect_left(li, 6)  # => 3
index_right = bisect_right(li, 6)  # => 4

bisect_left bisect_right はソート済みのリストに新たな値を入れようとしたときに、入れるべき場所を返してくれる関数です。新たな値がすでにリストに存在する場合には、 bisect_left はなるべく左側に、 bisect_right はなるべく右側に入れようとします。

上記の例では 6 を追加するなら、入れるべき場所は次のふたつのうちのどちらかです。

[1, 3, 5, __here__, 6, 10]
[1, 3, 5, 6, __here__, 10]

上の場合のインデックスは 3 、下の場合のインデックスは 4 で、それぞれ bisect_left 、 bisect_right の戻り値が示すものと一致します。

さらに bisect_left bisect_right には範囲を制限するための引数を渡すこともできます。こんな感じ。

bisect_left(対象リスト, 探したい値, lo=左側のインデックス, hi=右側のインデックス)

実際に二分探索の結果、値を挿入したい場合には insort_left insort_right を使います。

from bisect import insort_left

bisect.insort_left(li, new_element)

insort_left insoft_right はいずれも引数に与えたリストを変更する(破壊的な)メソッドです。


参考
bisect — Array bisection algorithm - Python 公式ドキュメント

2015/04/09

Python Tips:文字列を検索したい

Python で文字列を検索する方法をご紹介してみます。

ここでは 2 つの方法をご紹介します。ひとつは「文字列型」のメソッドを使う最もシンプルな方法、もうひとつは「正規表現」を使う方法です。

  1. 文字列型のメソッドを使う方法
  2. 正規表現を使う方法

1. 文字列型のメソッドを使う方法


まずは文字列型のメソッドを使う方法を見てみましょう。

対象パターンの場所(インデックス)を取得したい場合には find() メソッドを、単純に対象パターンが存在するかどうかだけを知りたい場合には in キーワードを使います。

find() メソッドを使う

s = 'hello kyoto'
print(s.find('kyo'))    # => 6
print(s.find('tokyo'))  # => -1

文字列型のメソッド find() は、引数に渡されたパターンが最初に現れるインデックスを返します。マッチしなかった場合には -1 を返してくれます。

ちなみに、文字列の末尾の方から検索して一致箇所を探したい場合には rfind() メソッドを使うとよいでしょう。

s = "hello kyoto"
print(s.find('o'))   # => 4
print(s.rfind('o'))  # => 10

find() は第 2 引数、第 3 引数を指定することで検索範囲を絞り込むことができたりもするので、詳しい使い方に興味のある方は公式ページをご覧になってみてください。

string.find - Python 公式ドキュメント

一致箇所までは知る必要がなくて、単純に対象のパターンが存在するかどうかだけをチェックしたい場合には in キーワードを使うとよいでしょう。

print('hell' in 'hello kyoto')  # => True
print('heaven' in 'hello kyoto')  # => False

これは文章としてなんだか自然な感じで可読性も高いので、 in で事足りるケースではできるだけ in を使うのがよいでしょう。

正規表現を使う必要がないシンプルなケースではこの find()in を使うのがよいかと思います。


2. 正規表現を使う方法


もうひとつ、「正規表現」を使う方法を見てみましょう。

Python で正規表現を使うには標準ライブラリの re を使用します。マッチした文字列をそのまま取得したい場合は関数 findall() を使うとよいでしょう。

import re

print(re.findall('o.', 'hello world'))  # => ['o ', 'or']
print(re.findall('ohayo', 'hello world'))  # => []

マッチしたパターンが存在しない場合は空のリストが返ってきます。ちなみに、 findall() は結果をリストとして返しますが、 finditer() はリストではなくイテレータとして結果を返してくれます。

また、マッチした結果に対して何らかの処理や判定をしたい場合には、 search()match() などの関数が便利な「正規表現マッチオブジェクト」を返してくれるので、それらを使うようにするとよいでしょう。

import re

result = re.search('l+', 'hello world')
print(result.span())   # => (2, 4)
print(result.start())  # => 2
print(result.end())    # => 4

find()in を使った通常の文字列検索では物足りない場合には、こちらの正規表現を使うとよいかと思います。

以上です。


参考
ライブラリ:re

2015/03/30

Python Tips:virtualenvwrapper で virtualenv の名前を変更したい

Python の環境管理ツールである virtualenv の便利ラッパー、 virtualenvwrapper で Python 環境(以下 virtualenv )の名前を変更する方法をご消化します。

結論からいうと、本記事執筆時点で virtualenvwrapper には既存の virtualenv に名前を変更する機能は用意されていないようです。 virtualenv をコピーする cpvirtualenv というコマンドが用意されてはいるのですが、「十分にサポートされていないので注意して使ってください」といった旨の注意書きが公式ページになされており、あまりおすすめでないようです。

ということで virtualenv の名前を変更すればどうすればよいか、なのですが、名前を変更するのではなく「同じ設定で作り直す」のがシンプルで確実だと思います。

具体的には、 Python のバージョンとパッケージを移行すれば大丈夫かと思いますので、そのとおりにコマンドを実行します。


$ # virtualenv で使われている Python のバージョンとパッケージのリストを取得
$ workon old_env
(old_env) $ which python > old_python_version.txt
(old_env) $ pip freeze > old_requirements.txt
(old_env) $ deactivate
$
$ # 新しい名前で環境を作成
$ mkvirtualenv new_env -p $(cat old_python_version.txt)
(new_env) $ pip install -r old_requirements.txt
(new_env) $ deactivate
$
$ # 古い環境を削除
$ rmvirtualenv old_env


old_env 、 new_env は実際の変更前後の virtualenv 名を指定すれば OK です。途中に生成するファイルは無事移行が済んだら削除しておきましょう。

以上です。

2015/03/27

Python Tips:Python で 16/256 階調 RGB カラーをランダムに出力したい

Python を使って CSS に使うような 16 階調の RGB カラーをランダムに生成する方法をご紹介します。

ランダム化の部分には組み込みの random ライブラリ、 16 進数にする部分には format の書式指定を使う方法がシンプルでやりやすいかと思います。以下のコードは Stack Overflow で紹介されていた方法を少し改変したものです。

import random

# 一度きりならこの方法で
rc = lambda: random.randint(0, 255)
print('#{:x}{:x}{:x}'.format(rc(), rc(), rc()))  # => #bd9f7b など(英字は小文字)
print('#{:X}{:X}{:X}'.format(rc(), rc(), rc()))  # => #BD9F7B など(英字は大文字)

# 何度も色を生成したいなら関数化
def generate_random_color():
    return '#{:X}{:X}{:X}'.format(*[random.randint(0, 255) for _ in range(3)])

generate_random_color()  # => #EF2F5B など

Python2 であれば Stack Overflow に紹介されたいたように str 型の % 演算子を使ってもよいのですが、 2015 年現在であれば Python3 互換の format() メソッドを使う方法がおすすめです。

2015/03/03

Python Tips:関数の合成をしたい

Python で関数の合成を行う方法をご紹介します。

「関数の合成」とは、数学でいう関数合成と同じような形でふたつの関数を合体させるような処理のことです。

数学での表記はこんな感じだったでしょうか。
f.g(x) = f(g(x))

以下、これを Python で行う方法を見ていきます。

まずは、ひとつめの関数からふたつめの関数へと受け渡しされる値がひとつの場合から。

引数がひとつだけの場合

# ふたつの関数を合成するための関数を定義する
def compose1(outer_func, inner_func):
    return lambda x: outer_func(inner_func(x))

# abs と int を合成して新たな関数 abs_int を作成する
abs_int = compose1(abs, int)

# 実際に使ってみる
print(abs_int("-12"))  # => 12 
print(abs_int(-4.3))   # => 4
compose1 という関数を使って新たに定義した abs_int は、引数を整数に変換してからさらに絶対値に変換して返す関数となりました。

ただし、この関数では次のように複数の引数を取るケースに対応できません。
abs_sum = compose1(abs, sum)
print(abs_sum(-5, -10))  # => TypeError

もう少し汎用的なものを次に見てみます。

引数が複数の場合

次の compose2 は引数が複数の場合にも、キーワード引数がある場合にも対応しています。
# ふたつの関数を合成するための関数を定義する
# こちらは引数が複数個ある場合でも大丈夫
def compose2(outer_func, inner_func):
    def composed(*args, **kwds):
        return outer_func(inner_func(*args, **kwds))
    return composed

# max の結果に abs を適用する関数を作る
abs_max = compose2(abs, max)
print(abs_max(-5, -10))  # => 5

これを使えば、たとえば標準入力から立て続けに整数を取得したい場合なんかに次のように書くことも可能です。
# input の結果に int を適用する関数を作る
questions = ["age: ", "height: ", "weight: "]
ans = map(compose2(int, input), questions)
qa = {q: a for q, a in zip(questions, ans)}
print(qa)  # => age height weight のキーと値を持った辞書

使いどころは決して多くはないかもしれませんが、ここぞというときに使えるようにしておくと便利かなと思います。

以上です。


参考
python - compose function and functional module - Stack Overflow
Function Composition in Python - Mathieu Larose

2015/02/24

Python Tips:文章中の単語を集計したい

Python で英語文章中の単語を集計する方法をご紹介します。

大まかな流れは次のとおりになるでしょうか。

  1. 文章を単語に分けて
  2. 集計して
  3. 表示する

collectons の Counter というクラスを使うときれいに書くことができます。

# ライブラリの読み込み
import re
from collections import Counter

# 00 テキストの取得
target_text = """
Python is a remarkably powerful dynamic programming language that is used in a wide variety of application domains. Python is often compared to Tcl, Perl, Ruby, Scheme or Java. Some of its key distinguishing features include:

very clear, readable syntax
strong introspection capabilities
intuitive object orientation
natural expression of procedural code
full modularity, supporting hierarchical packages
exception-based error handling
very high level dynamic data types
extensive standard libraries and third party modules for virtually every task
extensions and modules easily written in C, C++ (or Java for Jython, or .NET languages for IronPython)
embeddable within applications as a scripting interface
"""

# 01 文章を単語に分ける
# 複数の区切り文字を指定するため re.split を使う
words = re.split(r'\s|\,|\.|\(|\)', target_text.lower())

# 02 集計する
counter = Counter(words)

# 03 表示する
# Counter#most_common を使って出現頻度の降順に csv 形式で表示する
for word, count in counter.most_common():
    if len(word) > 0:
        print("%s,%d" % (word, count))
# => csv 形式の単語出現回数


以上です。少し変更して標準入力を受け付けるようにすると、ちょっとした英単語カウンタとして使えます。


参考

Python: Split string with multiple delimiters - Stack Overflow

2015/02/13

Python Tips:標準入力を 1 行ずつ処理したい

Python で標準入力から入力を得て 1 行ずつ処理する方法をご紹介します。

ちょっとしたコマンドラインツールを作るときに 1 行ずつ処理できれば便利かと思います。

やり方はいくつか考えられるかと思いますが、ひとつカンタンな方法は fileinput ライブラリを使う方法です。

# ライブラリを読み込み
import fileinput

# 入力の各行に対して処理を行う
for line in fileinput.input():
# some process...
pass

たとえば、単語の数を行ごとに数えてみたいなら次のようにすれば OK です。

# word_count.py
import fileinput

for line in fileinput.input():
    print(len(line.split()))

次の内容のファイル sample.txt があったとすると

Freude, schöner Götterfunken
Tochter aus Elysium,
Wir betreten feuertrunken,
Himmlische, dein Heiligtum!
Deine Zauber binden wieder
Was die Mode streng geteilt;
Alle Menschen werden Brüder,
Wo dein sanfter Flügel weilt.


以下のコマンドで行ごとの単語数を返してくれます。

$ cat sample.txt | python word_count.py
3
3
3
3
4
5
4
5

上記のスクリプトは次のような形で使うこともできます。

$ python word_count.py sample.txt

こちらの場合も返ってくる結果は同じです。

こうなるのは、 fileinput.input() は引数が与えられればそれらを cat したような形でまとめて順番に 1 行する処理するようになっているからです。

fileinput にはこの他にもコマンドライン引数にファイルを渡したい場合に便利な関数がもろもろ用意されています。興味があれば公式ドキュメントの方も見てみてください。


参考
fileinput - Python 公式ドキュメント

2015/02/10

Python Tips:複数のリストに対してループを回したい

Pythonで複数のリストに対してループを同時に回す方法をご紹介します。

最もシンプルなのは zip 関数を使う方法です。

さっそく例を見てみます。まず最初は同じ長さのふたつのリストの場合。
li1 = [1, 2, 3]
li2 = [11, 12, 13]
zip(li1, li2)
# => [(1, 11), (2, 12), (3, 13)]
戻り値は各要素をペアにしたタプルのリストとなります。

リストの長さが異なる場合は短い方のリストに合わせられます。
li1 = [1, 2, 3, 4, 5]
li2 = [11, 12, 13]
zip(li1, li2)
# => [(1, 11), (2, 12), (3, 13)]

みっつ以上のリストの場合もふたつの場合と同じようにまとめてくれます。
li1 = [1, 2, 3]
li2 = [11, 12, 13]
li3 = ['a', 'b', 'c']
zip(li1, li2, li3)
# => [(1, 11, 'a'), (2, 12, 'b'), (3, 13, 'c')]

逆にリストがひとつの場合も例外をあげることなく動作してくれます。
li1 = [1, 2, 3]
zip(li1)
# => [(1,), (2,), (3,)]


生成される合成リストの長さを最短のリストではなく最長のリストに合わせたい場合は itertools.zip_longest を使うのが便利です。
import itertools

li1 = [1, 2, 3, 4, 5]
li2 = [11, 12, 13]
print([x for x in itertools.zip_longest(li1, li2)])
# => [(1, 11), (2, 12), (3, 13), (None, 14), (None, 15)]


以上です。

ちなみに組み込みの zip と同じような処理を自分で書くとすると次のような形になるかと思います。
def myzip(*lists):
    """組み込みの zip と同じ処理をする関数 myzip """
    i = 0
    combined = []
    while True:
        try:
            # 各リストの同じインデックスの要素をタプルにまとめて追加
            # インデックスがどれかひとつの要素の長さを越えれば break
            e = []
            for li in lists:
                e.append(li[i])
            combined.append(tuple(e))
            i += 1
        except IndexError:
            break
    return combined

またまたちなみに、Python3では zip 関数はリストではなくイテレータを返す形に変更されています。


参考
iterator - how can I iterate through two lists in parallel in Python? - Stack Overflow


2015/02/03

Python Tips:IPython の起動時に特定の処理を走らせたい

IPython で起動時に特定の処理を走らせる方法をご紹介します。

IPython で起動時の処理を設定するには「プロファイル」というものを利用する形となります( IPython 1.1 の場合)。

ちなみに、 IPython の 1.1 ではプロファイルを利用する形となっていますが、以前のバージョンでは .ipythonrc を使うなどその他の方法が採用されていたようです。

以下、具体的なやり方を見ていきます。対象となる IPython のバージョンは 1.1 です。


01 プロファイルの作成

まずは IPython の「プロファイル」というものを作成します。
$ ipython profile create some_profile_name
some_profile_name のところを省略すれば、ホームディレクトリ以下に .ipython/profile_default ディレクトリが作られ、その中にデフォルトの設定ファイルが作成されます。今回はそこを省略したものとして以下進めていきます。

.ipython/profile_default の中をのぞくと ipython_config.py というファイルが入っています。これが IPython 起動時の設定を書き込むためのものです。


02 設定ファイルの編集

ipython_config.py の中にはプロファイルを作成時にすでにひな形が書き込まれているので、その中に適宜設定を追加します。

たとえば、特定のライブラリを読み込みたいなら、 ipython_config.py の中の次の一文を探します。
c = get_config()

これを以下の内容に変更します。
c = get_config()

c.InteractiveShellApp.exec_lines = [
    'import numpy',
    'import scipy'
]
こうすることで、 numpy と scipy が IPython の起動時に読み込まれる形となります。

特定の読み込みパスを追加しておきたい場合は次のようにします。
c.InteractiveShellApp.exec_lines = [
    'import sys',
    'sys.path.append(some_path_you_want_to_add)'
]

IPython を立ち上げたらほぼ毎回使うような使用頻度が高いライブラリやパスなんかはここで読み込んでおくとようにすると便利です。

以上です。


ライブラリの読み込み以外にもさまざまな設定ができるようになっているので、詳しくは公式ドキュメントをご参照ください。


参考
Introduction to IPython configuration#profiles — IPython公式ドキュメント

2015/01/27

Python Tips:ワンライナーが書きたい

Pythonでワンライナーコマンドを書く方法をご紹介します。

Pythonでワンライナーを書くには c オプションを使います。
python -c "print("hello world")"
# hello world と出力される
ヘルプドキュメントによると、 c は cmd の c だとか。個人的にはc というのはあまり直感的でなくて覚えにくい気が、します。。

引数を扱いたい場合には sys.argv を使います。
python -c "import sys; print(sys.argv)" a b c def
# ['-c', 'a', 'b', 'c', 'def'] と出力される

sys.argv を使えば、たとえば、引数の合計値を計算することなんかもできます。
python -c "import sys; print(sum([int(x) for x in sys.argv[1:]]))" 3 4 5
# 合計の 12 が出力される

また、標準入力を扱いたい場合には input() もしくは sys.stdin.read() を使います。
hayato:Desktop$ pwd | python -c "import sys; print(input().split('/'))"
# カレントディレクトリを構成するディレクトリが配列に分割されて表示される

以上です。


・・・ということで、ほんの数例を見てみただけでも、Pythonのワンライナーというのはあまり使い勝手がよい感じではないことがよくわかります笑

他のスクリプト言語のワンライナーや他のコマンドなんかも比べてみても、あえてPythonでワンライナーを書きたくなる場面というのは、あまり多くはなさそうです。

ちなみに、ワンライナーといっても、上で見たようなコードを直接渡すものではなく、モジュールを利用する形のものであれば、使い途はたくさんあるようです。こちらのページに良い例がいくつか紹介されていますのでよろしければ覗いてみてください。
# jsonを整形するなら
$ echo '{"foo": "lorem", "bar": "ipsum"}' | python -mjson.tool
# 簡易ウェブサーバを走らせるなら
$ python -m SimpleHTTPServer


参考
Powerful Python One-Liners - Python Wiki
Python command line oneliners

2015/01/20

ライブラリ:selenium

Python の「 selenium 」というライブラリをご紹介します。

import selenium

selenium ライブラリは、同名の Selenium というブラウザ操作用のツールがあるのですが、その Python バインディング、いわゆる「 Selenium の Python 版」です。

selenium ライブラリを使うことで Firefox などのブラウザを Python からかんたんに操作できるようになります。

以下、使い方をかんたんに見ていきます。まずはインストールから。


インストール

pip が入っていれば、他のライブラリと同じように pip install でインストールすることができます。
pip install selenium

Mac 環境だとこの形でインストールできますが、 Windows 環境では少し異なるかもしれません。 Python 以外の情報も含む Selenium 全体の公式ダウンロードページはこちらになります。
- Downloads - Selenium


使い方

基本的な使い方を見ていきます。

最小単位は次のような形になるでしょうか。
# ライブラリの読み込み
from selenium import webdriver

# ドライバの生成
# 問題なければFirefoxブラウザが自動的に起動する
driver = webdriver.Firefox()

# Firefox上でURLを指定してGETリクエスト
driver.get("http://www.google.co.jp/")

# ドライバを閉じる
# 問題なければFirefoxも終了する
driver.close()

ブラウザを開いて、特定のページを開いて、ブラウザを閉じる、という一連の流れがこれだけのコードで実現できます。

ちなみに、 selenium ではブラウザを操作するインスタンスを「ドライバ」と読んでいます。デフォルトで使えるのは Firefox ですが、Firefox のほかにも Chrome Safari PhantomJS IE など主要なブラウザを操作するドライバがサードパーティ製で公開されているので、適宜インストールすれば使うことができるようになります。

上の例ではページを開いて閉じただけであまりおもしろみがないので、キーボード操作をするような例についても見てみたいと思います。

こちらは公式ドキュメントの例にコメントをつけたものになります。
# ライブラリの読み込み
# Keys はキーボード操作に相当するモジュール
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

# ドライバの生成
# Firefoxを起動させる
driver = webdriver.Firefox()

# FirefoxでGETリクエスト
driver.get("http://www.python.org")

# 期待どおりのページが開かれたことを確認するアサーション
assert "Python" in driver.title

# ページ内のHTML要素をname属性を使って取得
# name属性の値が q である要素が選択される
elem = driver.find_element_by_name("q")

# 選択した要素に対してキーボード入力
# selenium という文字を入力し、リターンキーを押す
elem.send_keys("selenium")
elem.send_keys(Keys.RETURN)

# 期待どおりのページが開かれたことを確認するアサーション
assert "Google" in driver.title

# ドライバを閉じる
# Firefoxも終了する
driver.close()

コードの各文の意味合いはコメントに書いてあるとおりです。

このコードを走らせてみると、ブラウザがひとりでにウニョウニョ動く様子を確認することができます。

以上です。


一見こむずかしく思えますが、 Python らしいシンプルでわかりやすいインターフェイスになっているので意外とかんたんに使えます。

この他にも、ページ内のテキストを取得したり、マウスを操作したり、画面キャプチャを撮ったりと、通常ウェブブラウジングを行う上での基本操作が意外とシンプルなコードでできるようになっているので、興味がある方はぜひ公式ドキュメントをのぞいてみてください。

ちなみに、「ヘッドレスブラウザ」と呼ばれる PhantomJS などをインストールすればブラウザウィンドウを開かずに各種処理を行うことも可能となります。

もひとつちなみに、 Python バインディングではないおおもとの Selenium の方には、数多くのさまざまな「 Selenium ◯◯」が存在していて、 Selenium シリーズのようになっています。現在最も広く使われるものは次の2つになるでしょうか。

  • Selenium IDE
  • Selenium WebDriver


前者のIDEというのはFirefoxのプラグインとして動くもの、 WebDriverは今回ご紹介したPythonバインディングなどのようなコードによる操作を可能とするものです。


参考
Python Selenium ライブラリの公式ページ
- Selenium with Python — Selenium Python Bindings公式ドキュメント

同じくPyPIのページ
- selenium - Python Package Index

Seleniumのページ
- Selenium - Web Browser Automation

英語Wikipediaのページ
- Selenium (software) - Wikipedia

Seleniumの歴史も含めて解説されているページです。わかりやすいです。
- Selenium何とかっていうツールがやたら色々あるのはどういうわけなのか | 品質向上ブログ

2015/01/15

Python Tips:virtualenv で Python のバージョンを指定したい

Pythonの virtualenv で Python のインタプリタを指定する方法をご紹介します。

方法はかんたんで、 virtualenv コマンド実行時に python オプションでパスを指定するだけです。

$ virtualenv my_env --python=/usr/bin/python2.6

問題なく環境が生成できたら、「 ...done.」で終わるメッセージが表示されるかと思います。

環境ができあがれば通常どおり仮想環境に入ることができます。
$ cd my_env
$ source bin/activate  # Linux / OS X の場合
$ Scripts\activate  # Windows の場合
$ python -V
python 2.6.8
Pythonのバージョンを調べると指定したバージョンのものになっているはずです。

そして、環境から出ると、元のシステムのPythonに戻ることも確認できます。
$ deactivate
$ python -V
python 2.7.5

以上です。


ちなみに、 virtualenvwrapper にも同様のはたらきをする --python オプションが用意されています。
$ mkvirtualenv my_env --python=/usr/bin/python2.6

virtualenv コマンドにはこの他にも便利なオプションが豊富に用意されています。たとえばこんなもの。
  • --always-copy 仮想環境のPythonインタプリタをシンボリックリンクで用意するのではなくまるごとコピーして用意する
  • --system-site-packages システムのPythonライブラリに仮想環境からアクセスできるようにする
  • --no-pip デフォルトでインストールされる pip をインストールしないようにする
  • --prompt=MYPROMPT 仮想環境に入ったときにプロンプトの先頭に付加される文字を変更する

詳しくはvirtualenvコマンドのヘルプや公式ドキュメントをご覧ください。


参考
virtualenv — virtualenv公式ドキュメント

2015/01/08

サンプルコード:クイックソート

サンプルコード「クイックソート」をご紹介します。

クイックソートは、リストの中から要素をひとつ「ピボット」として選び、ピボット未満の要素とピボットよりも大きな要素とに残りの要素を分けていく再帰的アルゴリズムです。

Pythonのように内包表記を持った言語だととてもシンプル・エレガントに書けるのが特徴です。

def qsort(li):
    """クイックソートを行う

    @param li ソート可能な要素を持ったリスト
    @return li をソートした結果のリスト
    """

    # base case
    # 要素が空もしくは1つのリストはそのまま返す
    if len(li) < 2:
        return li

    # recursive case
    # 最初の要素をpivotとして、残りの要素をpivot未満と以上に分ける
    pivot = li[0]
    li_rest =  li[1:]
    smaller = [x for x in li_rest if x < pivot]
    larger = [x for x in li_rest if x >= pivot]

 # 分けたグループを再度 qsort に渡してから小さい順に結合する
    return  qsort(smaller) + [pivot] + qsort(larger)

リストを渡して使います。
qsort([])  # => []
qsort([1, 5, 3, 2])  # => [1, 2, 3, 5]
qsort(["tan", "pon", "am"])  # => ['am', 'pon', 'tan']


以上です。