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__() メソッドの中で super(UltraDog, self).__init__(name) という書き方を使ってスーパークラス Dog__init__() メソッドを呼び出しています。

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

( Python 2 での) super() 関数の使い方は super(クラス, インスタンス自身).メソッド名() です。わざわざ親クラスとインスタンス自身を super() に渡すのは、書き方としてちょっと気持ち悪い気もしますが、「 Explicit is better than implicit 」のポリシーを優先した感じでしょうか。

これを使う際の注意点は、親クラス(上の例では Dog )で object クラスを継承する必要がある、という点です。これを忘れて次のように宣言してしまうと super() がきちんと動いてくれず「 TypeError: must be type, not classobj 」と怒られてしまいます。

class Dog:
   ...


以上です。

ちなみに Python 3 では super() の引数を省略して次のように書くことができます。

super().__init__(name)


参考

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)

以上です。


参考

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


参考