Skip to content.

kagome.lab.tkikuchi.net

Sections
Personal tools
You are here: Home » Members » tkikuchi's Home » メモ » オブジェクトデータベース
Views

オブジェクトデータベース

Document Actions
あれこれ

Python でオブジェクトデータベース

  • そもそもオブジェクトデータベースって何だ?
  • オブジェクト=データ+メソッド(プログラム)
  • とりあえずクラスを定義してインスタンスを作る

データベース無し

  • # trivial counter class
    class Counter:
        def __init__(self):
            self.count = 1
        def inc(self):
            self.count += 1
        def read(self):
            return self.count
     
    c = Counter()
    print c.read()
    c.inc(); c.inc(); c.inc()
    print c.read()
    
  • やってみると 最初の print で 1, inc() を3回やった後は 4 になる。
  • 何回やっても結果は同じ。Counter をどこかに保存してないから。

Pickle に保存

  • Python には pickle (漬物?)というオブジェクト保存方法がある。
  • # simple counter ... pickle database
    # trivial counter class
    import pickle
    class Counter:
        def __init__(self):
            self.count = 1
        def inc(self):
            self.count += 1
        def read(self):
            return self.count
    try:
        counter = pickle.load(file('Counter.pck'))
    except:
        counter = {}
    for key in ['abc', 'def', 'xyz']:
        if not counter.has_key(key):
            counter[key] = Counter()
        else:
            counter[key].inc()
    for key in ['abc', 'def', 'xyz']:
        print key, ':', counter[key].read()
    pickle.dump(counter, file('Counter.pck', 'w'))
    
  • カウンタを3個生成して、ディクショナリに入れている。このディクショナリごと Counter.pck に保存しておく。やってみると、このプログラムを起動するたびにカウントが1ずつ増えていく。

ZODB を使う

  • Zope Object Database というくらいだから、オブジェクトデータベース
  • # simple counter ... zope object database
    import sys
    sys.path.insert(0, '/usr/local/zope29/lib/python')
    from persistent import Persistent
    from ZODB import DB
    from ZODB.FileStorage import FileStorage
    import transaction
    # trivial counter class
    class Counter(Persistent):
        def __init__(self):
            self.count = 1
        def inc(self):
            self.count += 1
        def read(self):
            return self.count
    storage = FileStorage('Counter.fs')
    db = DB(storage)
    conn = db.open()
    dbroot = conn.root()
    if not dbroot.has_key('counter'):
        from BTrees.OOBTree import OOBTree
        dbroot['counter'] = OOBTree()
    counter = dbroot['counter']
    for key in ['abc', 'def', 'xyz']:
        if not counter.has_key(key):
            counter[key] = Counter()
        else:
            counter[key].inc()
    for key in ['abc', 'def', 'xyz']:
        print key, ':', counter[key].read()
    transaction.commit()
    
  • データベースの初期化のあたりとか、(約束事が)やや複雑ではあるが、 基本的には pickle と同じ感覚で、オブジェクト(インスタンス)ごと ごっそりデータベースに保存できる(ように見える)。

ZEO を使う

  • ZODB の別の使い方で、ZEO (データベースサーバ)を動かしておいて それに接続しながら利用することもできる。
  • # simple counter ... zope object database
    import sys
    sys.path.insert(0, '/usr/local/zope29/lib/python')
    from persistent import Persistent
    from ZODB import DB
    from ZEO import ClientStorage
    import transaction
    # trivial counter class
    class Counter(Persistent):
        def __init__(self):
            self.count = 1
        def inc(self):
            self.count += 1
        def read(self):
            return self.count
    addr = '127.0.0.1', 8100
    storage = ClientStorage.ClientStorage(addr)
    db = DB(storage)
    conn = db.open()
    dbroot = conn.root()
    if not dbroot.has_key('counter'):
        from BTrees.OOBTree import OOBTree
        dbroot['counter'] = OOBTree()
    counter = dbroot['counter']
    for key in ['abc', 'def', 'xyz']:
        if not counter.has_key(key):
            counter[key] = Counter()
        else:
            counter[key].inc()
    for key in ['abc', 'def', 'xyz']:
        print key, ':', counter[key].read()
    transaction.commit()
    
  • これは、ZEO サーバが動いていないといけないので、 mkzeoinstance.py を使って、インスタンスを作成し、bin/zeoctl start しておく。
  • 上の2つのZODB を利用する例では、Zope をインストールしたディレクトリに あわせて、sys.path.insert() の引数を変えておく必要がある。

SQLObject を使う

  • SQL データベースというものが世の中ではよく利用されるので、最近は これを Object DB に利用する方法がいくつか出ている。
  • SQLObject と SQLAlchemy を取り上げるが、使ったことが無いので間違っているかもしれない。
  • # simple counter ... sqlobject / postgresql database
    from sqlobject import *
    scheme = 'postgres://localhost/sqlobjectcounter'
    # trivial counter class
    class Counter(SQLObject):
        key = StringCol(alternateID=True)
        count = IntCol()
        def inc(self):
            self.count += 1
        def read(self):
            return self.count
    sqlhub.processConnection = connectionForURI(scheme)
    try:
        Counter.createTable()
    except:
        pass
    for key in ['abc', 'def', 'ghi']:
        try:
            c = Counter.byKey(key)
            c.inc()
        except:
            Counter(key=key, count=1)
    for key in ['abc', 'def', 'ghi']:
        print key, ':', Counter.byKey(key).read()
    
  • とりあえず、postgresql が動いているので利用。createdb しておく必要あり。
  • Counter クラスの定義が大幅に異なる。初期化も違う。
  • commit する感じのところがないけどいいのかなぁ?

SQLAlchemy を使う

  • # simple counter ... sqlalchemy / postgresql database
    from sqlalchemy import *
    scheme = 'postgres://localhost/sqlalchemycounter'
    db = create_engine(scheme)
    metadata = BoundMetaData(db)
    try:
        # first time
        counter_table = Table('counter', metadata,
            Column('key', String(), primary_key=True),
            Column('count', Integer)
        )
        counter_table.create()
    except:
        counter_table = Table('counter', metadata, autoload=True)
    # trivial counter class
    class Counter(object):
        def __init__(self, key):
            self.key = key
            self.count = 1
        def inc(self):
            self.count += 1
        def read(self):
            return self.count
    countermapper = mapper(Counter, counter_table)
    session = create_session()
    query = session.query(Counter)
    for key in ['abc', 'def']:
        try:
            c = query.get_by(key=key)
            c.inc()
        except:
            c = Counter(key)
        session.save(c)
    session.flush()
    for key in ['abc', 'def']:
        print key, ':', query.get_by(key=key).read()
    
  • メソッドでないアトリビュート(key, count) をあらかじめ SQL のテーブルとして作っておくと、mapper() を使ってクラスの中で参照できるのか。
  • ディクショナリで個々のカウンタを参照するのでなく、query.get_by というのを使うらしい。
  • 初期化などのオーバヘッドはかなりあるが、クラスの書き方なんかは、本来のものに近いような気がする。
  • SQL* は、こんなんでいいのかまだ不明。

参考サイト

Created by tkikuchi
Last modified 2006-12-28 16:45
 

Powered by Plone

This site conforms to the following standards: