[Python] Python 2.5 Changes

Conditional Expression

 # Conditional expression 
 value = (B if A else C)

 # 問題点: Aが真でも B が 偽 と判断された場合 C を返す
 value = (A and B or C)

 # 利点: lambda式 中に条件分岐が書ける

PEP 343 - 'with' statement (./Lib/test/test_with.py, ./Lib/contextlib.py参照)

 from __future__ import with_statement
 from contextlib import closing, nexted

 # 変数のスコープはブロック内ローカル ではない ので注意
 f = None
 with closing(file('test.txt', 'r')) as f:
     print f.read()
 print f.closed # True


 class wrap_stream(object):
     def __init__(self, create_func, *args, **kwds):
         self.create = create_func
         self.args = args
         self.kwds = kwds

     def __enter__(self):
         self.thing = self.func(*self.args, **self.kwds)
         return self.thing

     def __exit__(self, *exc_info):
         self.thing.close()

 with wrap_stream(file, 'test.txt', 'r') as stream:
     print stream.read()

  • try/except/finally ... exceptとfinallyブロックが同時に記述できる。(try/finally/elseはまだ無理らしい)
  • 例外クラスがnew-style classesを利用、例外クラスの階層が少し変更された。


TODO

  • functools

[Python][Ruby] mini-pickle (solver for pythonchallenge level 5)

Few days, I was having fun with pythonchallenge.com as you do.
I like the idea of the challanges, I think any programmers could enjoy it.
but unfortunally. it requires some Python specific modules.
fmm...there are hints which hard to notice without python experience.
Here may be help for non-Python programers. ( but spoiler warning )
I've implemented those python's modules for several languages.

If you'd like to implement it by yourself. hints for the lv5: PEP 307

 class Unpickler
   def initialize
     @mark, @memo, @stack = Object.new, {}, []
   end
 
   def marker
     @stack.rindex @mark
   end
 
   def load(stream)
     loop do
       case stream.getc.chr
       when "I"
         @stack << stream.readline.chomp.to_i
       when "S"
         @stack << eval(stream.readline.chomp)
       when "t", "l" # e.g) ... [ 1,2,MARKER,3,4 ] => [ 1,2,[3,4] ]
         @stack[marker..-1] = [ @stack[marker+1..-1] ]
       when "("
         @stack << @mark
       when "a" # e.g) ... [ [ ],1,2 ] => [ [ 1, 2] ]
        v = @stack.pop; @stack.last << v
       when "p"
         @memo[stream.readline.chomp] = @stack.last
       when "g"
         @stack << @memo[stream.readline.chomp]
       when "."
         return @stack.pop
       end
     end
   end
 end
 
 if $0 == __FILE__ and ARGV.length == 1
   banner = ARGV.first
   puts Unpickler.new.load(open(banner)).map{|row|row.inject(""){|r,(k,v)|r+=k*v}}
 end

[Python] pickle でCODEオブジェクトをシリアライズ (yat another way)

 from new import *
 import pickle, marshal, struct

 CODE='C'

 # opcode(CODE)が重複していないか確認する
 if (not pickle.Pickler.dispatch.has_key(code) and
    not pickle.Unpickler.dispatch.has_key(CODE)):

   def save_code(self, obj):
     """保存用メソッド"""
     assert type(obj) == code, "argument must be code object"
     tmp = marshal.dumps(obj)
     self.write(CODE)
     self.write(struct.pack("

注意: CモジュールのcPickleの方では、同じ方法で拡張できない。copy_reg辺りを参照

ユーザー定義クラス等、他のオブジェクトのシリアライズについての正式な拡張方法は、
マニュアルか PEP307 を参照。

登録してみた. http://rubyforge.org/projects/rupy/

RuPy Marshal

 # marshal_dump.py ... データ作成の為のPython script
 import sys, marshal

 def main(filename="test.dump"):
   data = [
     10, 10.0, -10000, os.environ.get('USER',None),
     ['foo@example.org', 'bar@example.net'],
     10000000000000000000000000000000000000,
   ]

   output = open(filename, "wb")
   marshal.dump(data, output)
   output.close()

 if __name__ == "__main__":
   main(sys.argv[1])
 python marshal_dump.py a.dump
 # marshal_load.rb
 require "python/marshal"
 include Python::Marshal

 p load( File.new(ARGV[0],'rb').read )
 ruby marshal_load.rb foo.dump

# 注意) 辞書等、いくつかの型は未サポート.

RuPy interpreter

  1. ./utils/py2pyc.py ユーティリティで、pythonソースをpycファイルにコンパイル
 python ../utils/py2pyc.py ../samples/hello.py
 rupy hello.pyc

今のところ、
PRINT命令しかサポートしていない、Hello world 専用インタープリター。

RubyObject

classを宣言した時に、(スコープ内に)クラスが既に存在していれば、
既存のクラスを拡張しそれを返す。Rubyの様なclassの振舞を提供するメタクラス

import inspect

class RubyMetaClass(type):
    def __new__(self, classname, classbases, classdict):
        try:
            frame = inspect.currentframe()
            frame = frame.f_back
            if frame.f_locals.has_key(classname):
                old_class = frame.f_locals.get(classname)
                for name,func in classdict.items():
                    if inspect.isfunction(func):
                        setattr(old_class, name, func)
                return old_class
        finally:
            del frame
        return type.__new__(self, classname, classbases, classdict)

class RubyObject(object):
    __metaclass__ = RubyMetaClass

 >>> class C:
 ...   def foo(self): return "C.foo"
 ...
 >>> c = C()
 >>> print c.foo()
 C.foo
 >>> class C(RubyObject):
 ...   def bar(self): return "C.bar"
 ...
 >>> print c.bar()
 C.bar

[REBOL][Prototype]

 REBOL []

 person: make object! [
   name: none
   age: none
 ]

 ; プロトタイプを生成
 tea: make person [
   name: "Tea"
   age: 24
 ]
 probe tea ; probe はオブジェクトをdumpして表示する関数。

 ; プロトタイプなので更に派生する事も可能。
 foo: make tea [ name: "foo" ]
 probe foo 

 ; 定義済みのobjectを新たに拡張する事も出来る。
 person: make person! [
   ; コンストラクタに相当する関数を定義してみる。
   new: func [new-name new-age] [
     ; self はobject自身を指す。makeでクローンを生成して返す。
     make self [ name: new-name age: new-age ]
   ]
 ]
 hina: person/new "Hina" 18
 probe hina

REBOLの第一印象は、括弧なしLISP, コンマなしIo。

  • Prototype based Object Oriented
  • Email, Money, Date, URL が組み込み型。Quoteなしに、EmailやURLをスクリプト中に書ける。
  • Seriesと呼ばれるデータ構造を持ち、REBOLスクリプト自身もそのデータ構造で表す事が出来る。(自己記述性)
  • 評価順序
    • write %output.file read %input.file が期待通りに解釈される。
    • 数式を扱う場合に注意。>> 10 + 20 * 30 + 100 は 710でなく 1000 になる。
      • ()で明示的に評価順序を指定すれば問題なし。
  • 変数のスコープ。
    • 関数宣言内で局所化する場合は明示的に/localで宣言する必要あり。
    • object宣言内のブロックでは別スコープ。
  • 引数の数が曖昧。()を使う事も出来るが省略も出来る為。
  • メソッドを追加する場合にスコープの制限あり。(総称関数が実現できない...?)
    • これってプロトタイプベースの利点が活かし切れていないのでは?
  • ソースがない。(非オープンソース?、拡張したかったけどライセンスが...)