2014/01/29

Python Tips:多重リストをフラットにしたい

Pythonで多重リストをフラットにする(平滑化する)方法をご紹介します。

多重リストの階層が必ず2階層と決まっている場合には、リスト内包表記で次のように書くことができます。
a = [[1, 3], [5, 7], [9]]

def flatten(nested_list):
    """2重のリストをフラットにする関数"""
    return [e for inner_list in nested_list for e in inner_list]

flatten(a) 
# => [1, 3, 5, 7, 9]

リスト内包表記に頼らない方法としては extend 関数を使う方法があります。
def flatten_by_extend(nested_list):
    flat_list = []
    for e in nested_list:
        flat_list.extend(e)
    return flat_list

flatten_by_extend(a)
# => [1, 3, 5, 7, 9]

ただ、このextendを使ったやり方は内包表記を使ったやり方よりも遅いようです。そのあたりの議論がStack Overflowでなされています

深さがまちまちな多重リスト、たとえば次のようなリストがあるものとします。
b = [[1, 3], [[5]], [[7], 9]]
このようなリストをフラット化するには、深さ優先探索の要領で処理を行うとよいかと思います。

次の flatten_with_any_depth は、そんな任意の深さの多重リストをフラットにする関数です。
def flatten_with_any_depth(nested_list):
    """深さ優先探索の要領で入れ子のリストをフラットにする関数"""
    # フラットなリストとフリンジを用意
    flat_list = []
    fringe = [nested_list]

    while len(fringe) > 0:
        node = fringe.pop(0)
        # ノードがリストであれば子要素をフリンジに追加
        # リストでなければそのままフラットリストに追加
        if isinstance(node, list):
            fringe = node + fringe
        else:
            flat_list.append(node)

    return flat_list

flatten_with_any_depth([[1, 3], [[5]], [[7], 9]])


参考
Making a flat list out of list of lists in Python - Stack Overflow

2014/01/23

Pythonの super() 関数の使い方

Pythonでスーパークラスのインスタンスメソッドを呼び出すときに使える super() 関数の使い方をご紹介します。

まずは例から。
class Dog(object):
    def __init__(self, name):
       self.name = name

class UltraDog(Dog):
    def __init__(self, name, type):
        super(UltraDog, self).__init__(name)
        self.type = type

ud1 = UltraDog("taro", "akita")
print(ud1.name)

ここでは、サブクラスのインスタンスメソッドの中で、スーパークラスのインスタンスメソッドを呼び出しています。具体的には、Dog のサブクラス UltraDog の __init__ メソッドの中でスーパークラスである Dog の __init__ メソッドを呼び出します。そのときに super() 関数を使っています。

意図としては、スーパークラスの __init__ メソッドの中に name を初期化する処理がすでに存在するので、それをサブクラス UltraDog の中でも再利用しよう、というものです。

super() 関数の使い方は super(クラス名, self).メソッド名 です。この形で呼び出されたスーパークラスのインスタンスメソッドは self をレシーバとして実行される形となります。

書き方としてちょっと気持ち悪い気もしますが、「Explicit is better than implicit」というところを優先した感じでしょうか。

1点、使う際の注意点としては、親クラスの Dog の方で object クラスを継承するのを忘れないことです。これを忘れて
class Dog:
   ...
という形で宣言してしまうと super() がきちんと動いてくれず「TypeError: must be type, not classobj」と怒られてしまいます。

ちなみにPython 3では super() の引数を省略することができて
super().__init__(name)
と書くことができます。


参考
inheritance - Understanding Python super() and init methods - Stack Overflow
python super() raises TypeError ! Why? - Stack Overflow
python - super() fails with error: TypeError "argument 1 must be type, not classobj" - Stack Overflow

2014/01/21

Python Tips:文字列を評価したい

Pythonで文字列を評価する方法をご紹介します。

Pythonコードとして文字列を評価(eval)する方法としては大きく「eval」と「exec」の2つがあります。

ざっくり区別すると

  • evalは単一の式を評価(evaluate an expression)
  • execは複数の文を実行(execute statements)

するためのものです。

順に見ていきます。

eval

result = eval("5")  # 5
result = eval("3 + 4")  # 7
result = eval("'%s is a %s' % ('tama', 'cat')")  # tama is a cat

evalは式を評価して、評価した値を返してくれます。基本は「名前 = eval(式)」という形で使うことになるかと思います。

次にexecを見てみます。

exec

exec("a = 15")
print a  # 15

exec("if True: print 'it is True'")

exec("""
total = 0
for i in range(5):
    print i
    total += i

print total 
""")

evalが単一の式を評価するのに対し、execは式を含む複数の文を実行します。上の例のように、複数行にわたるコードでも問題なく実行することができます。execの中で宣言された値(totalなど)はexecの外でも有効となります。

式か文かの区別はちょっとわかりづらいのですが、たとえば

  • 対話型モードで入力しEnterを押したときに値が返ってくるなら式
  • if文に if expression: という形で渡して構文エラーとならないなら式

と言えるでしょうか。

以上です。


参考
Why is Python's eval() rejecting this multiline string, and how can I fix it? - Stack Overflow
dynamic - What's the difference between eval, exec, and compile in Python? - Stack Overflow
In Python, why doesn't exec work in a function with a subfunction? - Stack Overflow

2014/01/18

Python Tips:ライブラリ読み込み対象ディレクトリを追加したい

Pythonで特定のディレクトリをライブラリ読み込み対象パスに追加する方法をご紹介します。

Pythonでライブラリを読み込むディレクトリは sys.path の中にリストアップされています。
import sys
print sys.path  # => パスの一覧を格納したリスト

このリストに、通常のリストと同じやり方でディレクトリを追加すると、そのディレクトリが読み込み対象に追加されます。

たとえば。
import sys

# /Users/username/Desktop ディレクトリを import 先に追加
sys.path.append("/Users/username/Desktop")

# /Users/username/Desktop/mylib.py が読み込める
import mylib

末尾に追加する append でもいいですし、 insert でも大丈夫です。


この方法を使って実行スクリプトが入っているディレクトリを追加したい場合は次の方法でよいかと思います。
import sys
import os

sd = os.path.dirname(__file__)
sys.path.append(sd)


以上です。


参考
Appending to Your Python Path

2014/01/09

Pythonのイテレータ生成関数の使い方

Pythonでイテレータを生成する関数の使い方についてご紹介します。

このイテレータを生成する関数のことをPythonでは「ジェネレータ」と呼んだりします。

具体的には、通常関数が戻り値を返す return 文の代わりに yield 文を使うような関数を定義します。

たとえば。
# フィボナッチ数列を生成するイテレータを返す関数を定義
def fib_gen():
    f1 = 0
    f2 = 1
    while True:
        yield f2
        f1, f2 = f2, f1 + f2

# イテレータを生成
fib = fib_gen()

# for文でイテレータを回す
for n in fib:
    print n,
    if n > 10: break
# => 1 1 2 3 5 8 13
ここで fib はフィボナッチ数列を返すイテレータとなっています。

for文はイテレータを自動的に回してくれるのでこのような挙動になりますが、組み込みの next 関数で1つずつ回すことも可能です。

上のコードのあとにつづけて次のコードを書くと次のような結果が出てきます。
next(fib)  # => 21
next(fib)  # => 34


参考
Functional Programming HOWTO — Python v2.7.6 documentation