言語別 YAML用ライブラリ徹底解説

第3回Python編

今回は、Python用のライブラリとして、PyYAML、libyaml for Pythonの2つのライブラリを紹介します。

PyYAML

PyYAMLは、Pythonで実装されたYAMLライブラリです。特徴としては、YAMLの最新仕様に厳密に準拠していることです。そのため、現在ではYAMLにおけるリファレンス実装だとみなされています。

PyYAMLについて
Webサイトhttp://pyyaml.org/wiki/PyYAML
ドキュメントhttp://pyyaml.org/wiki/
PyYAMLDocumentation
ダウンロードhttp://pyyaml.org/download/pyyaml/
バージョン3.05
作者Kirill Simonov

インストール

インストールは、LinuxやMac OS Xではリスト1の手順で行ってください。Windowsの場合は、http://pyyaml.org/download/pyyaml/にあるWindows installerを使ってください。

リスト1 PyYAMLのインストール
$ wget http://pyyaml.org/download/pyyaml/PyYAML-3.05.tar.gz
$ tar xzf PyYAML-3.05.tar.gz
$ cd PyYAML-3.05/
$ sudo python setup.py install

使い方

PyYAMLの使い方はリスト2のようになります。日本語を含む場合は必ずUnicodeにデコードしなければならない点に注意してください。詳細はリファレンスマニュアルを参照してください。

リスト2 PyYAMLの使い方(ex-pyyaml1.py)
import yaml

## YAMLドキュメントを読み込む
string = open('example.yaml').read()
string = string.decode('utf8')      # 日本語を含む場合はデコードする
data = yaml.load(string)
print repr(data)
## または
##   data = yaml.load(open('example.yaml'))
##   print repr(data)

## YAMLストリームを読み込む
for data in yaml.load_all(string):
    print repr(data)
## または
##   for data in yaml.load_all(open('example.yaml')):
##       print repr(data)

## 任意のデータをYAML文字列に変換する
## (allow_unicode=Trueを指定すると、日本語がエンコードされない)
data = [ {'x':10, 'y':20}, {'x':15, 'y':25} ]
print yaml.dump(data, encoding='utf8', allow_unicode=True)
## またはファイルに出力する場合:
##   f = open('dump.yaml', 'w')
##   yaml.dump(data, f, encoding='utf8', allow_unicode=True)

またYAMLでは任意のオブジェクトを変換/復元できます。しかしそのために、たとえば信用できない相手から受け取ったYAML文字列をそのままyaml.load()でロードすると、セキュリティ的に問題のあるデータに変換される可能性があります。これを防ぐために、PyYAMLでは次の関数が用意されています。

yaml.safe_load()
yaml.load()と同じですが、⁠!!python/object:__main__.Hello」のようなタグがあるとエラーになります。
yaml.safe_dump()
yaml.dump()と同じですが、listやmapやスカラー以外のオブジェクトがあるとエラーになります。

これらを使うと、安全なデータのみを相手と受け渡しできます。

タグを変更する

yaml.dump()でインスタンスオブジェクトをYAML文字列に変換した場合、たとえば「!!python/object:__main__.Color」のようなタグがつきます。しかしこれだとPython限定になり、ほかの言語のYAMLパーサで読み込むとエラーになるため、データ交換する場合には都合が悪いです。

これを回避するには、以下の関数を使ってクラスに対応するタグを指定しますリスト3⁠。

yaml.add_representer(klass, func)
オブジェクトをノード(yaml.nodes.Node)に変換する関数を登録します。
yaml.add_constructor(ytag, func)
ノード(yaml.nodes.Node)からオブジェクトを復元する関数を登録します。
リスト3 クラスに対応するタグを指定する(ex-pyyaml2.py)
# -*- coding: utf-8 -*-
import yaml

## クラスに対応するタグを登録するユーティリティ
def yaml_register_class(klass, ytag):
    suffix = '%s.%s' % (klass.__module__, klass.__name__)
    def representer(dumper, instance):
        node = dumper.represent_mapping(ytag, instance.__dict__)
        return node
    def constructor(loader, node):
        instance = loader.construct_python_object(suffix, node)
        return instance
    yaml.add_representer(klass, representer)
    yaml.add_constructor(ytag, constructor)
    ## または
    #suffix = '%s.%s' % (klass.__module__, klass.__name__)
    #f1 = lambda dumper, obj: dumper.represent_mapping(ytag, obj.__dict__)
    #f2 = lambda loader, node: loader.construct_python_object(suffix, node)
    #yaml.add_representer(klass, f1)
    #yaml.add_constructor(ytag, f2)

## クラス定義
class Color(object):
    def __init__(self, r, g, b):
        self.r = r
        self.g = g
        self.b = b

## クラスに対応するタグを登録する
yaml_register_class(Color, '!color')

## 実行例
c = Color(255, 128, 0)
print yaml.dump(c)   #=> !color {b: 0, g: 128, r: 255}
s = '!color {b: 0, g: 128, r: 255}'
c = yaml.load(s)
print c              #=> <__main__.Color object at 0x5be510>
print c.__dict__     #=> {'r': 255, 'b': 0, 'g': 128}
.--------------------

またオブジェクトをマッピング以外のデータで表現することもできます。リスト4は、文字列またはシーケンスでデータを表現した例です。

リスト4 オブジェクトを文字列またはシーケンスで表現する(ex-pyyaml3.py)
# -*- coding: utf-8 -*-
import yaml

## クラス定義
class Color(object):
    def __init__(self, r, g, b):
        self.r = r
        self.g = g
        self.b = b

## Colorインスタンスをノードに変換する関数
def color_representer(dumper, color):
    ## 文字列に変換する場合
    s = '#%02x%02x%02x' % (color.r, color.g, color.b)
    node = dumper.represent_scalar(u'!color', s)
    return node
    ## シーケンスに変換する場合
    #L = [color.r, color.g, color.b]
    #node = dumper.represent_mapping(u'!color', L)
    #return node

yaml.add_representer(Color, color_representer)

## 実行例
print yaml.dump(Color(255, 0, 0))
    #=> !color '#ff0000'     (スカラーに変換した場合)
    #=> !color [255, 0, 0]   (シーケンスに変換した場合)

## ノードからColorインスタンスを復元する関数
def color_constructor(loader, node):
    ## 文字列を表すノードから変換する場合
    s = loader.construct_scalar(node)
    import re
    pat = '#' + '([a-fA-F0-9][a-fA-F0-9])' * 3
    m = re.match(pat, s)
    if not m:
        msg = '%s: invalid color' % s
        raise yaml.constructor.ConstructorError(msg)
    r, g, b = m.groups()
    return Color(int(r, 16), int(g, 16), int(b, 16))
    ## シーケンスを表すノードから変換する場合
    #L = loader.construct_sequence(node)
    #return Color(L[0], L[1], L[2])

yaml.add_constructor(u'!color', color_constructor)

## 実行例(文字列から変換した場合)
color = yaml.load("!color '#FF8000'")
print "color: r=%s, g=%s, b=%s" % (color.r, color.g, color.b)
    #=> color: r=255, g=128, b=0
## 実行例(シーケンスから変換した場合)
#color = yaml.load('!color [255, 128, 0]')
#print "color: r=%s, g=%s, b=%s" % (color.r, color.g, color.b)
#   #=> color: r=255, g=128, b=0

日本語の扱い

yaml.load()では、入力となる文字列をデコードしてunicodeに変換しておけば、特に問題はありません。

yaml.dump()では、デフォルトでは日本語文字列がたとえば「\u3044」のようにエンコードされますが、allow_unicode=Trueを指定するとエンコードされません。またUnicodeを変換するためのencodingを指定できます。

その他

Pythonのlistオブジェクトやdictオブジェクトは、次のようなルールでYAMLのコンテナに変換されますリスト5⁠。

  • ほかのコンテナを含んでいればブロックスタイル
  • ほかのコンテナを含んでいなければフロースタイル

フロースタイルを使わずすべてをブロックスタイルにしたい場合は、yaml.dump()においてdefault_flow_style=Falseを指定してください。

リスト5 yaml.dump()におけるフロースタイルとブロックスタイル(ex-pyyaml4.py)
## point1とpoint2はコンテナを含んでいないのでフロースタイル、
## pointsはコンテナを含んでいるのでブロックスタイルになる。
point1 = {'x':10, 'y':20}
point2 = {'x':15, 'y':25}
points = [ point1, point2 ]
print yaml.dump(points),
## 実行結果:
##  - {x: 10, y: 20}
##  - {x: 15, y: 25}

## default_flow_style=Falseを指定すると、すべてブロックスタイルになる。
print yaml.dump(points, default_flow_style=False),
## 実行結果:
##  - x: 10
##    y: 20
##  - x: 15
##    y: 25

LibYAML for Python

LibYAMLは、Cで書かれた高速なライブラリです。LibYAML自体では動作せず、各プログラミング言語用のバインディングが必要です。今のところ、Python用のバインディングが用意されています。

LibYAML for Pythonについて
Webサイトhttp://pyyaml.org/wiki/PyYAML
ドキュメントhttp://pyyaml.org/wiki/LibYAML
ダウンロードhttp://pyyaml.org/download/libyaml
バージョン0.0.1
作者Kirill Simonov

インストール

LibYAMLのインストールは、リスト6の手順で行ってください。

リスト6 LibYAMLのインストール
$ wget http://pyyaml.org/download/libyaml/yaml-0.0.1.tar.gz
$ tar xzf yaml-0.0.1.tar.gz
$ cd yaml-0.0.1/
$ ./configure
$ make
$ sudo make install

またLibYAMLをPythonから使うには、Pythonバインディングが必要です。

インストールには、ほかにPyrexとPyYAMLが必要ですリスト7⁠。

リスト7 Pythonバインディングのインストール
$ wget http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/Pyrex-0.9.6.4.tar.gz
$ tar xzf Pyrex-0.9.6.4.tar.gz
$ cd Pyrex-0.9.6.4/
$ sudo python setup.py install
$ cd ..
$ wget http://pyyaml.org/download/pyyaml/PyYAML-3.05.tar.gz
$ tar xzf PyYAML-3.05.tar.gz
$ cd PyYAML-3.05/
$ sudo python setup_with_libyaml.py install

使い方

LibYAMLをPythonから使う方法は、以下の点を除いてPyYAMLと同じです。

  • yaml.load()において、引数に「Loader=CLoader」を指定する
  • yaml.dump()において、引数に「Dumper=CDumper」を指定する

つまりPyYAML標準のLoaderクラスとDumperクラスの代わりに、LibYAML'を使ったCLoaderクラスとCDumperクラスを指定するわけです。

リスト8は使い方のサンプルです。ここではLibYAML'がインストールされていてもいなくても動作するようにしています。

リスト8 LibYAMLの使い方(ex-libyaml1.py)
# -*- coding: utf-8 -*-
import yaml
try:
    ## LibYAMLがインストールされている場合はそれを使う
    from yaml import CLoader as Loader
    from yaml import CDumper as Dumper
except ImportError:
    ## インストールされていない場合はPyYAMLを使う
    from yaml import Loader, Dumper

## YAMLドキュメントを読み込む
import sys
string = open('example.yaml').read()
string = string.decode('utf8')      # 日本語を含む場合はデコードする
data = yaml.load(string, Loader=Loader)
## または
##   data = yaml.load(open('example.yaml'), Loader=Loader)

## 任意のデータをYAML文字列に変換する
data = [ {'x':10, 'y':20}, {'x':15, 'y':25} ]
print yaml.dump(data, Dumper=Dumper)
## またはファイルに出力する場合:
##   f = open('dump.yaml', 'w')
##   print yaml.dump(data, f, Dumper=Dumper)

日本語の扱い

日本語の扱いについては、PyYAMLと同じです。

タグを変更する

タグを変更する方法は、PyYAMLと同じです。

不具合

筆者が試した範囲では、特に不具合は見つかりませんでした。

その他

当然ですが、LibYAMLはC言語から使うことができます。詳細はlibyamlのWebサイトを参照してください。

おすすめ記事

記事・ニュース一覧

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy