Python2.6変更点まとめ

  • 20081004: typo修正及び説明追加

Python2.6きましたね。ということで、自分用にも主な変更点メモ。なぐり書きなのでミス多いかも。個人的な注目部分は

  • with文
  • multiprocessing
  • itertoolsへのメソッド追加
  • ABCの導入
  • クラスデコレータの導入
  • ネットワーク系ライブラリ(http,ftp,telnet..etc)でタイムアウトが設定できるようになった。

あたりですかね。ではどうぞ。

Python 3.0由来の変更点

  • 複素数へオブジェクトを変換する__complex__メソッド。
  • 例外補足のためのもう一つ書き方:except TypeError as exc
  • build-inのreduce()に加え、functools.reduceの追加。(3.0ではreduceはfunctools経由でしか使えない)

3.0では他にもbuild-in関数に変更がある。3.0互換のコードを書きたいなら必要に応じてfrom future_builtins import hex,mapのようにimportすること。

また、-3コマンドラインスイッチにより3.0で削除される機能を使っている場合、警告を出せる。

PEP 343: 'with' statement

python code
  1. with open('/etc/passwd', 'r') as f:
  2.     for line in f:
  3.         print line
  4.         ... more processing code ...
  5.  
  • withに入るとオブジェクトの__enter__()、抜けるときに__exit__(type, value, traceback)が呼ばれる。例外発生しなかった場合、type, value,tracebackはNone。
  • contextlibを使うと簡単に書ける。

PEP 399: メインモジュールからの明示的相対的import

Pythonでは-mスイッチでモジュールをスクリプトとして動かせる。が、パッケージ内部のモジュールを動かそうとした場合、相対的importが上手く働かない。本修正で、__package__属性がモジュールに追加された。この属性がある場合、__name__の代わりにこの値からの相対importを行う。

PEP302スタイルのインポータは__package__を必要に応じてセットできる。runpyモジュールの-mスイッチはこれを行っているので、パッケージ内からスクリプトを実行しても相対的importは正しく動く。

PEP 371: multiprocessing module

プロセス間通信を実装したモジュール。並列計算などを実装できる。詳しくはAtsushiさんの記事参照

PEP3101: さらに高度な文字列フォーマット

3.0では%オペレータはさらに高機能になる。2.6ではこの%str.formatメソッドで実装している。

python code
  1. "User ID: {0}".format("root") -> "User ID: root"
  2.  
  3. 'User ID: {uid} Last seen: {last_login}'.format(
  4.       uid='root',
  5.       last_login = '5 Mar 2008 07:20') ->
  6.   'User ID: root Last seen: 5 Mar 2008 07:20'
  7.  
  8. 'Platform: {0.platform}\nPython version: {0.version}'.format(sys) ->
  9.     'Platform: darwin\n
  10.     Python version: 2.6a1+ (trunk:61261M, Mar 5 2008, 20:29:41) \n
  11.     [GCC 4.0.1 (Apple Computer, Inc. build 5367)]'
  12.  
  13. fmt = '{0:15} ${1:>6}'
  14. fmt.format('Registration', 35) ->
  15.   'Registration $ 35'
  16.  

くわしくはPEP3101参照。

PEP3105: 関数としてのprint

3.0でprint文はprint関数になる。2.6では__future__に定義されている。

python code
  1. from __future__ import print_function
  2. print('# of entries', len(dictionary), file=sys.stderr)
  3.  
  4. def print(*args, sep=' ', end='\n', file=None)
  5.  

PEP 3112: Byte リテラル

3.0では文字列はデフォルトでUnicodeになり、8bitリテラルは別に用意される。b'string'byteコンストラクタだ。2.6 ではstrとおなじbytes型を定義し、b''記法をサポートする。

また、__future__で文字列リテラルを全てUnicodeにすることができる。

python code
  1. from __future__ import unicode_literals
  2.  
  3. s = ('\u751f\u3080\u304e\u3000\u751f\u3054'
  4.     '\u3081\u3000\u751f\u305f\u307e\u3054')
  5.  
  6. print len(s) # 12 Unicode characters
  7.  

PEP 3116: 新しいI/Oライブラリ

Pythonではダックタイピングにより、ファイル-likeオブジェクトはread(),write()が定義されていればよかった。ただ、文字列を扱う場合readline()が定義されていたほうがよい。このようなケースに応じた基底クラスがPython3.0ではioモジュールに用意される。

  • RawIOBase
  • BufferedIOBase
  • TextIOBase

など。

くわしくはPEP 3116参照。

PEP 3119: Abstract Base Classes

Abstract Base Class(ABC)はJAVAのインターフェースのようなもの。ABCのサポートはABCMetaというメタクラスを含むabcモジュールで構成される。ABCMetaisinstanceissubclassのbuilt-in関数で特別に扱われる。

ABCは通常の継承のように

python code
  1. import collections
  2. class Storage(collections.MutableMapping):
  3.   ...
  4.  

ともABCのregister()メソッドをつかって

pytyhon code
  1. import collections
  2. class Storage:
  3.     ...
  4. collections.MutableMapping.register(Storage)
  5.  

ともできる。registerを使えば、組み込み型や他者の書いたクラスをABCとすることができる。

ABCはJAVAと違い、Pythonが自動でそのインターフェースをチェックしてくれるわけではない。チェックするためには

python code
  1. if not isinstance(d, collections.MutableMapping):
  2.     raise ValueError("Mapping object expected, not %r" % d)
  3.  

のように書く。

独自のABCは以下のように定義する

 code
  1. from abc import ABCMeta, abstractmethod, abstractproperty
  2.  
  3. class Drawable():
  4.     __metaclass__ = ABCMeta
  5.  
  6.     @abstractmethod
  7.     def draw(self, x, y, scale=1.0):
  8.         pass
  9.  
  10.     @abstractproperty
  11.     def readonly(self):
  12.       return self._x
  13.  
  14.     def draw_doubled(self, x, y):
  15.         self.draw(x, y, scale=2.0)
  16.  

これでDrawableから派生したクラスでdrawメソッド、readonlyプロパティが定義されていなければTypeErrorが出るようになる。

PEP 3127: 数値リテラル及び文法

3.0では8進数は0開始でなく0o or 0O開始になり、2進数リテラル0b or 0Bがサポートされる。2.6では0開始8進数表記も使えるが0o or 0O開始も使えるし、oct()関数は0開始も文字列を返す。ただし、future_builtins.octは0o開始の文字列を返す。また、int,long関数は0o0b開始文字列も扱えるようになった。

PEP 3129: クラスデコレータ

python code
  1. @foo
  2. @bar
  3. class A:
  4.   pass
  5.  

python code
  1. class A:
  2.   pass
  3.  
  4. A = foo(bar(A))
  5.  

と同義になる。

PEP 3141: 数値の型階層

3.0ではSchemeに習った数値の型階層が導入される。これらの型はnumbersモジュールを通して2.6にバックポートされた。

  • Number
    • Complex
      • Real : floor()trunc()を定義
        • Rational : numeratordenominatorを定義。実装はfractionsモジュールで。
          • Integral

また、math.trunc()も3.0からバックポートされた。fractionはこのように使う。

python code
  1. >>> from fractions import Fraction
  2. >>> a = Fraction(2, 3)
  3. >>> b = Fraction(2, 5)
  4. >>> float(a), float(b)
  5. (0.66666666666666663, 0.40000000000000002)
  6. >>> a+b
  7. Fraction(16, 15)
  8. >>> a/b
  9. Fraction(5, 3)
  10. >>> (2.5) .as_integer_ratio()
  11. (5, 2)
  12. >>> (3.1415) .as_integer_ratio()
  13. (7074029114692207L, 2251799813685248L)
  14. >>> (1./3) .as_integer_ratio()
  15. (6004799503160661L, 18014398509481984L)
  16.  

その他の言語の変更

  • **kw引数にPythonの辞書以外のUserDictのようなどんなマッピングでも使えるようになった。

  • next(iterator, [default])メソッド追加。見ての通りiteratorの次の要素を、なければdefaultを返す。

  • タプルにindex()count()が追加された(listと同じ)。

  • 組み込み型のsliceのサポートが向上。

  • プロパティがgetter, setter, deleterというデコレータをサポート。

    python code
    1. class C(object):
    2.     @property
    3.     def x(self):
    4.         return self._x
    5.  
    6.     @x.setter
    7.     def x(self, value):
    8.         self._x = value
    9.  
    10.     @x.deleter
    11.     def x(self):
    12.         del self._x
    13.  
    14. class D(C):
    15.     @C.x.getter
    16.     def x(self):
    17.         return self._x * 2
    18.  
    19.     @x.setter
    20.     def x(self, value):
    21.         self._x = value / 2
    22.  
  • setintersection(), intersection_update(), union(), update(), difference(), difference_update()が引数に複数のiterableを取れるようになった。

  • 浮動小数点に関する様々な改良が追加された。

  • 親クラスから__hash__()を継承したクラスで__hash__ = Noneとすることでhashableでないことを示せる。

  • Exceptionのインタフェースが変更。message属性がdeprecated。

  • GeneratorExitの親クラスがExceptionからBaseExceptionに変更

  • Generatorオブジェクトにgi_code属性を追加。

  • compile()関数でキーワード引数が取れるようになった。

  • complexコンストラクタでカッコにかこわれている数値がOKになった。complex('(3+4j)')

  • string.translate()に変換テーブルとしてNoneを渡せるようになった。Noneは恒等変換として扱われる。

  • dir()組み込み関数が、引数に渡されたオブジェクトの__dir__()メソッドを見るようになった。__dir__()は文字列のlistを返す必要がある。これにより__getattr__()__getattribute__()で、擬似属性みたいなのを実現しているクラスでもそのリストが返せる。

  • instance methodオブジェクトでim_self__self__im_func__func__でも参照できるようになった。

  • class文内でlocals()を使った場合、自由変数(クラスの属性でない変数)は含まれなくなった。

新規、改善、非推奨モジュール

  • 3.0警告モードで以下のモジュールは非推奨。

    • audiodev, bgenlocations, buildtools, bundlebuilder, Canvas, compiler, dircache, dl, fpformat, gensuitemodule, ihooks, imageop, imgfile, linuxaudiodev, mhlib, mimetools, multifile, new, pure, statvfs, sunaudiodev, test.testall, and toaiff.
  • asyncore,asynchatでメンテ再開。多くのパッチやバグフィックスが含まれた。

  • cgiでクエリ文字列付きPOSTリクエストにおいてクエリ文字列が解釈されるようになった。(/add.py?category=1へのPOSTなど)

  • parse_qsparse_qslcgiモジュールからurlparseモジュールへ。cgiでもまだ使えるがPendingDeprecationWarningが出る。

  • cmathモジュールが改良された。

  • collections.namedtupleが新しく定義。

    python code
    1. >>> var_type = collections.namedtuple('variable',
    2. ... 'id name type size')
    3. # Names are separated by spaces or commas.
    4. # 'id, name, type, size' would also work.
    5. >>> var_type._fields
    6. ('id', 'name', 'type', 'size')
    7.  
    8. >>> var = var_type(1, 'frequency', 'int', 4)
    9. >>> print var[0], var.id # Equivalent
    10. 1 1
    11. >>> print var[2], var.type # Equivalent
    12. int int
    13. >>> var._asdict()
    14. {'size': 4, 'type': 'int', 'id': 1, 'name': 'frequency'}
    15. >>> v2 = var._replace(name='amplitude')
    16. >>> v2
    17. variable(id=1, name='amplitude', type='int', size=4)
    18.  
  • collections.dequemaxlen引数を取れるようになった。maxlenを超えて要素を追加すると自動で先頭が破棄される。

  • datetimeモジュールのstrftime()で'%f'が扱えるようになった。

  • decimalモジュールでexp(), log10()が追加、またas_tuple()sign,digits,exponentをフィールドとするnamed tupleを得られる。

  • difflib.SequenceMathcerクラスがa,b,size属性をもったnamed tupleを返すようになった。

  • ftplibでタイムアウトが設定できるようになった。

  • glob.glob()がunicodeファイル名を返すようになった。

  • gopherlib削除。

  • heapqモジュールにmerge(iter1, iter2, ...)heappushpop(heap, item)が追加。

  • httplib.HTTPConnectionでタイムアウトが設定できるようになった。

  • inspectモジュールの関数の多くがnamed tupleを返すようになった。

  • itertoolsに多数の関数を追加。

    • izip_longest(iter1, iter2, ...[, fillvalue])
    • product(iter1, iter2, ..., [repeat=N])
    • combinations(iterable, r)
    • permutations(iter[, r])
    • chain(*iterables)は新しくitertools.chain.from_iterable(iterable)とも書けるように。
  • mathモジュールに関数を追加。

    • isinf() ,isnan(): 無限とNaNを判別。
    • copysign() : IEEE 754のサインビットをコピー。math.copysign(1, -0.0)-1.0
    • factorial() :階乗を計算。
    • fsum() : iterableの数値を足すが、出来る限り精度を失うことを避ける。
    • acosh(), asinh() ,atanh()
    • log1p()
    • trunc() : 0の方向へ丸める。
  • MmeWritermimifyモジュールが非推奨に。emailパッケージを使うこと。

  • md5モジュールが非推奨に。hashlibを使うこと。

  • mmapオブジェクトで文字列検索を行うrfind(),find()メソッドを追加。

  • operatorモジュールにmethodcaller()関数追加。

    python code
    1. >>> # Equivalent to lambda s: s.replace('old', 'new')
    2. >>> replacer = operator.methodcaller('replace', 'old', 'new')
    3. >>> replacer('old wine in old bottles')
    4. 'new wine in new bottles'
    5.  
  • operator.attrgetterでドットを含む名前が取れるように。

    python code
    1. >>> inst_name = operator.attrgetter(
    2. ... '__class__.__name__')
    3. >>> inst_name('')
    4. 'str'
    5. >>> inst_name(help)
    6. '_Helper'
    7.  
  • os.walk()followlinks=Falseパラメータ追加。Trueにセットされるとシンボリックリンクをたどる。無限ループ注意。

  • os.path.splitext()でドット始まりのファイル名の扱いが変更に。('.ipython', '')から('', '.ipython')

  • os.path.relpath(path, start='.')が追加。startからの相対パスを返す。

  • pdb に新しいコマンドrunを追加。

  • posixfileモジュールが非推奨に。fcntl.lockf()を変わりに使うこと。

  • pickletoolsoptimize()関数追加。

  • popen2モジュールが非推奨に。subprocessモジュールを使うこと。

  • randomモジュールのRandomオブジェクトが32-bitと64-bit環境間でpickleできるようになった。またtriangular(low,hight,mode)関数追加。

  • reモジュールで長い正規表現を行うとき、シグナルをチェックするようにしたので、時間のかかる検索に割り込まれても大丈夫になった。また、正規表現は正規表現専用の仮想マシンコードの変換されるがベリファイアがなかったので、不正なコードを実行される可能性があった。今回ベリファイアを追加。

  • rgmimgモジュール削除。

  • sched.schedulerインスタンスにqueue属性追加。この属性は(time,priority,action,argument)から構成されるnamed tupleのリストを返す。

  • selectモジュールがepoll,kqueueシステムコールのラッパ関数を導入。

  • setsモジュールが非推奨に。組み込みset, frozensetを使うこと。

  • shaモジュール非推奨。hashlibモジュールを使うこと。

  • shutil.copytree()ignore=callable引数追加。ignoreでコピーしないファイルリストを返すことが出来る。またignore_patterns関数を使い、以下のようにもできる

    python code
    1. shutil.copytree('Doc/library', '/tmp/library',
    2.                 ignore=shutil.ignore_patterns('*~', '.svn'))
    3.  
  • smtplibでSMTP over SSL, LMTPをサポート。

  • subprocess.Popenオブジェクトにterminate(), kill(), send_signal()追加。

  • sysに様々な追加。

    • float.h由来の情報を持ったfloat_infoオブジェクト。
    • .pyc.pyoを作るか制御するdont_write_bytecode
    • オブジェクトの使用メモリを取得するsys.getsizeof()。組み込みオブジェクトは正しい数値を返す。
    • sys.getprofile(),sys.gettrace()
  • telnetlibでタイムアウトが設定できるようになった。

  • tempfile.NamedTemporaryFiledelete=Falseを指定することで自動削除しなくできる。

  • tempfile.SpooledTemporaryFileを追加。メモリ上に確保し、メモリにのらなくなるとファイルに書き出す。

  • test.test_supportwith文を使った関数を提供する。

  • textwrapモジュールで空白文字を保持するためdrop_whitespace=Falseを指定できるようになった。

  • timeitモジュールで文字列だけでなくcallableも受け取れるようになった。

  • タートルグラフィックを扱うturtleモジュールにかなりの改良。

  • urllibでタイムアウトを設定できるようになった。

  • zipfile.ZipFileextract()extractall(), open(), read()を追加。

  • ASTを扱うastモジュール追加

  • JSONを扱うjsonモジュール追加

  • Property Listを扱うplistlibモジュール追加。

  • OpenSSLの上に構築された、sslモジュール追加。socketモジュールのSSLサポートはまだ使えるが、3.0では削除される予定。このモジュールを使うためには、通常通りTCPコネクションを張り、ssl.wrap_socket()関数に渡す。

  • 3.0由来の組み込み関数を含むfuture_builtinsモジュールを追加。

    • ascii : repr()と同義。3.0ではrepr()はunicode,ascii()はASCII byte文字列を返す。
    • filter,map : 2.xではリストを, 3.0ではイテレータを返す。
    • hex, oct: __hex__()__oct__()ではなく、__index__()をよびその結果を変換する。

Python2.6への移行

  • ハッシュできないクラスは__hash__=Noneを行うべき。

  • collection.deque.__init__()は、iterableの要素を追加する前に自身の内容をクリアする。これはlist.__init__()と同じ。

    python code
    1. >>> from collections.deque
    2. >>> a = deque([1,2,3])
    3. >>> deque.__init__(a, [4,5,6])
    4. >>> a
    5. # python2.6 => deque([4,5,6])
    6. # python2.5 => deque([1,2,3,4,5,6])
    7.  
  • object.__init__()はこれまで任意の引数を渡すことができ、これらの引数は無視していた。2.6では余計な引数を渡すとTypeErrorがでる。

  • Decimalコンストラクタは前後に空白があっても大丈夫に。以前はInvalidOperation例外がでていた。一方、Context.create_decimal()は余分な空白があるとConversionSyntax例外が出るようになった。

  • __import__で間違ってファイルパスを渡すと、指定したファイルをimportしていたが、これは決してそのように意図したわけではない。今はこのようなケースをチェックし、ImportErrorを出すようになった。

  • C API: PyImport_ImportPyImport_ImportModuleのデフォルトが相対importでなく完全importになった。これは他のモジュールをimportするC拡張に影響する。

  • socket.error例外はIOErrorを継承するようになったので、StandardErrorのサブクラスになった。

  • xmlrpclibdatetime.datedatetime.timeを自動的にxmlrpclib.DateTimeに変換しなくなった。なぜならこの挙動は全てのアプリケーションにとって必ずしも有用とは限らないからだ。xmlrpclibを使っているコードはdatetimeインスタンスを変換すべきである。

  • 3.0警告モード: Exceptionクラスにスライシングおよびインデックスアクセスすると警告。

  • 3.0警告モード: 2つの辞書や、比較メソッドを実装していないオブジェクト間で比較演算子を適応すると警告。ただし、dict1 == dict2は動く。

10.05.08/12am

Python: S式パーサライブラリを作りました

前のエントリーで簡易S式パーサをre.Scannerで作ったのですが、まぁ個人的にまとめておいたほうが後々使えるだろう、ということでライブラリにまとめました。ダンパもついているので、S式の読み込みの他、PythonオブジェクトをS式で出力することができます。

実装には引き続きre.Scannerを活用しています。おかげで短い行数でキレイにかけているのではないかと。

ダウンロード

simplesexp.py

ソースはこんな感じ(テストのぞく)。

python code
  1. import re, sys
  2. from unicodedata import east_asian_width
  3.  
  4. try:
  5.   from re import Scanner
  6. except ImportError:
  7.   from sre import Scanner
  8.  
  9. class ParseError(StandardError): pass
  10.  
  11. class Ident(unicode):
  12.   def __repr__(self):
  13.     return "Ident(%s)"%unicode.__repr__(self)
  14.  
  15. class Symbol(unicode):
  16.   def __repr__(self):
  17.     return "Symbol(%s)"%unicode.__repr__(self)
  18.  
  19. class Pair(list):
  20.   def __repr__(self):
  21.     return "Pair(%s)"%list.__repr__(self)
  22.  
  23. class Token(object):
  24.   def __init__(self, value, pos):
  25.     self.value = value
  26.     self.pos = pos
  27.   def __repr__(self):
  28.     return repr(self.value)
  29.  
  30. class Binding(object):
  31.   def __init__(self, dct):
  32.     self.dct = dict(((k, k.__class__), v) for k,v in dct.iteritems())
  33.   __contains__ = lambda self, key: (key, key.__class__) in self.dct
  34.   __getitem__ = lambda self,key: self.dct[(key, key.__class__)]
  35.  
  36. default_binding = {"#t":True, "true":True, "#f":False, "false":False, "nil":None, "dict":Ident(u'alist->hash-table')}
  37.  
  38. class Reader(object):
  39.   PAREN = {"]":"[", ")":"("}
  40.   def __init__(self, binding=None, symbol_marker="'", use_dict=True):
  41.     self.binding = binding or default_binding
  42.     self.symbol_marker = symbol_marker
  43.     self.use_dict = use_dict
  44.  
  45.   def read(self, value):
  46.     self.result = []
  47.     self.paren_stack = []
  48.     self.source = value
  49.     self.pos = 0
  50.     self.scanner = Scanner([
  51.       (r"\s+", self("skip")),
  52.       (r";[^\n]*\n", self("skip")),
  53.       (r""""(((?<=\\)")|[^"])*((?<!\\)")""", self("str")),
  54.       (r"(\(|\[)", self("open")),
  55.       (r"(\)|\])", self("close")),
  56.       (r"(([\d]+|(((\d+)?\.[\d]+)|([\d]+\.)))e[\+\-]?[\d]+)|(((\d+)?\.[\d]+)|([\d]+\.))", self("number")),
  57.       (r"\-?((0x[\da-f]+)|(0[0-7]+)|([1-9][\d]*)|0)[l]?", self("number")),
  58.       (r"""%s([^\(\[\)\]\s"]+)"""%self.symbol_marker, self("symbol")),
  59.       (r"""([^\(\[\)\]\s"]+)""", self("ident")),
  60.       (r"""".*""", self("unterm_str")),
  61.       (r".*", self("unknown_token"))
  62.     ], re.M|re.S|re.I)
  63.     self.scanner.scan(self.source)
  64.     if self.paren_stack:
  65.       self.raise_error("missing closing parenthesis.")
  66.     return self.parse(self.result)
  67.  
  68.   def append(self, v):
  69.     self.last().append(Token(v, self.pos))
  70.  
  71.   def __call__(self, name):
  72.     def _(scanner, s):
  73.       self.pos += len(s)
  74.       return getattr(self, name)(s)
  75.     return _
  76.  
  77.   def unknown_token(self,s): self.raise_error("unknown token: %s"%s)
  78.   def skip(self, _): pass
  79.   def open(self, s):
  80.       new_lst = []
  81.       self.last().append(new_lst)
  82.       self.paren_stack.append([s, new_lst])
  83.   def close(self, s):
  84.       if not self.paren_stack:
  85.         self.raise_error("missing opening parenthesis.")
  86.       if self.PAREN[s] != self.paren_stack.pop()[0]:
  87.         self.raise_error("missing closing parenthesis.")
  88.   def str(self, s): self.append(eval('u""'+s+'""'))
  89.   def unterm_str(self, s): self.raise_error("unterminated string literal.")
  90.   def number(self, s): self.append(eval(s))
  91.   def symbol(self, s): self.append(Symbol(s[1:]))
  92.   def ident(self, s):
  93.     if s in self.binding:
  94.       self.append(self.binding[s])
  95.     else:
  96.       self.append(Ident(s))
  97.  
  98.   def last(self):
  99.     if self.paren_stack:
  100.       return self.paren_stack[-1][1]
  101.     else:
  102.       return self.result
  103.  
  104.   def parse(self, rs):
  105.     def is_ident(value, expected):
  106.       return getattr(value,"value", None) == Ident(expected)
  107.     def is_pair(rs):
  108.       return getattr(rs, "__len__", lambda :0)()==3 and is_ident(rs[1], u".")
  109.  
  110.     if isinstance(rs, list):
  111.       if not len(rs):
  112.         return []
  113.       elif self.use_dict and is_ident(rs[0], u"alist->hash-table"):
  114.         if len(rs) != 2:
  115.           self.raise_error("alist->hash-table: expected 1 arguments, got %d."%(len(rs)-1), rs[0].pos)
  116.         if not all(is_pair(a) for a in rs[1]):
  117.           self.raise_error("alist->hash-table: aruguments must be alist", rs[0].pos)
  118.         return dict((self.parse(i[0]), self.parse(i[2])) for i in rs[1])
  119.       elif len(rs)!=3 and any(is_ident(t, u".") for t in rs):
  120.         self.raise_error('illegal use of "."', rs[0].pos)
  121.       elif is_pair(rs):
  122.         parsed = self.parse(rs[2])
  123.         if not isinstance(rs[2], list):
  124.           return Pair([rs[0].value, parsed])
  125.         if isinstance(parsed, Pair):
  126.           return Pair([rs[0].value, parsed])
  127.         elif isinstance(parsed, list):
  128.           return [rs[0].value]+parsed
  129.         else:
  130.           return [rs[0].value, parsed]
  131.       else:
  132.         return map(self.parse, rs)
  133.     else:
  134.       return rs.value
  135.  
  136.   def raise_error(self, msg="parse error", pos=None, range=3):
  137.     pos = pos or self.pos
  138.     lines = self.source.split("\n")
  139.     curline = self.source[:pos].count("\n")
  140.     linepos = pos - len("\n".join(lines[:curline]))
  141.     buf = ["\n"]
  142.     for i in xrange(max(0, curline-range), curline+1):
  143.       buf.append("% 5d: %s"%(i+1, lines[i]))
  144.     width = 7 + sum(east_asian_width(c) == 'W' and 2 or 1 for c in unicode(lines[i]))
  145.     buf.append("%s~"%(" "*width))
  146.     buf.append("line %d, %d: %s"%(curline+1,linepos, msg))
  147.     raise ParseError(("\n".join(buf)).encode(sys.stderr.encoding))
  148.  
  149. class Dumper(object):
  150.   def __init__(self, binding=None ,symbol_marker="'"):
  151.     binding = binding or default_binding
  152.     self.binding = Binding(dict(zip(binding.values(), binding)))
  153.     self.symbol_marker = symbol_marker
  154.  
  155.   def dump(self, obj):
  156.     result = self.to_sexp(obj, [])
  157.     if isinstance(result, list) and len(result) and result[0]=="(":
  158.       result = result[1:-1]
  159.     return u" ".join(result)
  160.  
  161.   def to_sexp(self, obj, result):
  162.     ap = result.append
  163.     tos = lambda v: self.to_sexp(v, result)
  164.     if isinstance(obj, Pair):
  165.       ap("(")
  166.       tos(obj[0])
  167.       ap(" . ")
  168.       tos(obj[1])
  169.       ap(")")
  170.     elif isinstance(obj, (tuple, list)):
  171.       ap("(")
  172.       map(tos, obj)
  173.       ap(")")
  174.     else:
  175.       if isinstance(obj, dict):
  176.         ap("( alist->hash-table ")
  177.         tos([(k, Ident(u"."), v) for k,v in obj.items()])
  178.         ap(" ) ")
  179.       elif obj in self.binding:
  180.         ap(unicode(Ident(self.binding[obj])))
  181.       elif isinstance(obj, Symbol):
  182.         ap(u"'%s"%unicode(obj))
  183.       elif isinstance(obj, (Ident,int, float, long)):
  184.         ap(unicode(obj))
  185.       else:
  186.         s = unicode(repr(obj)).decode("unicode_escape")
  187.         m = re.match(r"""^[u|r]?["|'](.*)["|']$""", s, re.M|re.S)
  188.         if m:
  189.           s = m.group(1)
  190.         ap("\"%s\""%s.replace('"','\\"').replace("\\'","'"))
  191.     return result
  192.  
  193. dumper = Dumper()
  194. read = Reader().read
  195. dump = dumper.dump
  196.  

概要

特徴は

  • 辞書を定義できる
  • ドット対に対応
  • 識別子に対して、任意のバインディングを指定できる
  • わりとちゃんとエラー表示される
  • シンボル表記、数値表記(python表記)に対応

といった当たりでしょうか。具体的にはテストコードを見てもらうと分かるかと。

 code
  1. (あああ hoge->fuga123 (1 . (2 . 3)) "hoge\\"hoge" ;comment2
  2. foo "aaa" #t <= 'foo
  3. "hogehoge
  4. foo
  5. " (5 . (6 .()))
  6. )
  7. (dict (
  8.   ("いいい" .
  9.     (alist->hash-table (
  10.       ("a-1" . "vvv")
  11.       ("a-2" . (
  12.         hoge foo bar
  13.       ))
  14.     )))
  15. ))
  16. (10 1L -45 010 0x10 -10 -0x10 3.14 10. .001 1e100 3.14e-10 0e0)
  17. ; comment3 ()(
  18.  
  19. """)
  20.  

という感じのS式が

python code
  1.   [
  2.     [Ident(u'あああ'), Ident(u'hoge->fuga123'), Pair([1, Pair([2, 3])]), u'hoge"hoge',
  3.     Ident(u'foo'), u'aaa', True, Ident(u'<='), Symbol(u'foo'),
  4.     u'hogehoge\nfoo\n', [5,6]],
  5.     {u'いいい':
  6.       {u'a-1': u'vvv',
  7.       u'a-2': [Ident(u'hoge'), Ident(u'foo'), Ident(u'bar')]}},
  8.     [10, 1L, -45, 010, 0x10, -10, -0x10, 3.14, 10., .001, 1e100, 3.14e-10, 0e0]
  9.   ]
  10.  

となります。IdentSymbolはunicodeのサブクラス、Pairはリストのサブクラスになっているので、違和感なく使えると思います。

また、alist->hash-tableで辞書が作れます。デフォルトでdictalist->hash-tableをバインドしてますので、dictでも辞書が作れます。この機能はオンオフ切り替えも可能です。

その他、#tTrueなどSchemeっぽくデフォルトバインディングが用意してあります。もちろん、バインディングは変更可能ですのでCLっぽくもできます。


と、こんな感じです。一番便利なのはやっぱり辞書ですかねえ。なので、YAML,JSONで書いてる設定ファイルをS式で置き換え・・・なんてことができるかもしれません。

09.19.08/09pm

Python版Yahooテキスト解析 APIライブラリを日本語係り受け解析に対応させました

ついでに指定形容詞係り先検索にも。

ダウンロード

yahooapi

使い方

python code
  1. from yahooapi import *
  2. client = DAServiceAPI("your_appid")
  3. result = client.parse(sentence=u"うちの庭には二羽鶏がいます。")
  4. for morph in result.Result.ChunkList.Chunk[0].MorphemList.Morphem:
  5.   print morph.Reading
  6. # => うち
  7. # の
  8.  
  9. client = DAServiceSearchAPI("your_appid")
  10. result = client.search(mode=MODE_URESHII)
  11. for word in result.Result.WordList.Word:
  12.   print word.Surface, word.Frequency
  13.  
  14. #クリック 35
  15. #応援 30
  16. #気持ち 26
  17. #金メダル 24
  18. #ホームラン 22
  19. #ニュース 17
  20. #デス 16
  21. # .
  22. # .
  23. # .
  24.  

うむ。

08.22.08/07pm

About

Author:yuin(http://inforno.net/)

文学部文化学科卒という生粋の文系趣味プログラマ。

主にRuby、Javascript、PHP、JAVA,Python,C,Scala,Schemeなどを使っています。今はPythonな感じかもしれない。今後作曲活動なども復活するかもしれない。

Pages