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