7

SQLalchemy で動作するように namedtuple を取得しようとしましたが、役に立ちませんでした.. Web 検索はあまり明るくなく、Python と SQLalchemy は初めてなので、風車を追いかけているかどうかはよくわかりません。 (基本的な考え方は、namedtuple があるということです。つまり、

Point=namedtuple('Point',['x','y'])

私が正しければ、基本的にクラス Point(tuple) を作成します。これは最初は問題なく動作し、次のようなオブジェクトを作成できます。

p=Point(3,4)

しかし、エンジンなどを作成して mapper を呼び出した後、このエラーが発生せずにこれ以上オブジェクトを作成することはできません:

Traceback (most recent call last):
  File "<pyshell#62>", line 1, in <module>
    f=Point(3,4)
TypeError: __init__() takes exactly 1 argument (3 given)

なぜそれが起こるのか?namedtuple を sqlalchemy で動作させる方法を知っている人はいますか? もちろん、独自の Point クラスを定義することもできますが、namedtuple を機能させることに夢中になっています。Python 2.7、SQLalchemy 0.6.6 (sqlite エンジン) を使用しています。

例:

私はこのようなことを試みています:

from sqlalchemy import *
from sqlalchemy.orm import *
from collections import namedtuple

Point=namedtuple('Point',['x','y'],verbose=True)
p=Point(3,4)


db=create_engine('sqlite:///pointtest.db')
metadata=MetaData()
pointxy=Table('pointxy',metadata,
              Column('no',Integer,primary_key=True),
              Column('x',Integer),
              Column('y',Integer),
              sqlite_autoincrement=True)
metadata.create_all(db)
m=mapper(Point, pointxy)
Session=sessionmaker(bind=db)
session=Session()
f=Point(3,4)

主なアイデアは、データベースに簡単に保存できる名前付きのコレクションが必要だということです。したがって、この:

class Bunch:
    __init__ = lambda self, **kw: setattr(self, '__dict__', kw)

sqlalchemy では動作しません (と思います)。Bunch クラスを作成できますが、コレクションに格納する int の数を事前に知りません。データベースを作成する前に設定します。私が理にかなっていることを願っています..

4

2 に答える 2

4

Namedtuple には、sqlalchemy でのマッピングに適さない一連の動作があります。最も重要なことは、namedtuple は作成後に変更できないことです。つまり、namedtuple を使用して、挿入操作後のデータベース内の行の状態を追跡することはできません。通常、次のようなことをしたいと思うでしょう:

class MyDataHolder(namedtuple('MyDataHolder', ('id', 'my_value')):
    pass

mapper(MyDataHolder, MyDataMeta)

...

newRow = MyDataHolder(None, 'AAA')

...

session.add(newRow)

セッション コードが SQL を実行して新しいデータをデータベースに追加すると、データベースが行に割り当てた ID に newRow.id が対応するようnewRowを更新する必要があります。ただし、newRowは不変のタプルであるため、id を変更して、データベースから返された主キーを保持することはできません。これにより、namedtuples はマッパーにはほとんど適していません。

__init__ の問題は、namedtuple が __new__ で初期化され、その後変更されることが期待されないために発生します。__init__() は、オブジェクトが作成された後に呼び出されるため、効果がありません。したがって、namedtuple の __init__ は、self という 1 つの引数のみを定義します。マッパーは、 __init__() がクラスの初期化を処理していて、 __new__ と不変の型について知らないと想定しています。作成時に渡される引数でclassname .__init__() を呼び出しているようです。独自のイニシャライザ: __init__(self, *args) を指定することでこれを「修正」できますが、最終的には weakref 問題が発生します。

ネームドタプルは変更可能な __dict__ ではなく __slots__ を使用して値を保存するため、weakref エラーが発生します。__slots__ の使用はメモリの最適化であるため、名前付きタプルを効率的に格納できます。名前付きタプルは、作成後に変更されないことが予想されると想定しています。これには属性の追加が含まれるため、 __slots__ を使用するとメモリを最適化する価値があります。ただし、namedtuple クラスの作成者が弱参照をサポートしなかった理由を理解しているとは言いません。それは特に難しいことではなかったでしょうが、おそらく私が行方不明になったのは本当に正当な理由があるでしょう.

私は今朝この問題に遭遇し、_fieldset 属性で初期化されたデータ マッピング用に独自のクラスを定義することで回避しました。これは、マップされていることを確認したいフィールドの (サブセット) セットを指定します。それから私はしばらくして、namedtuples が他の Python 内部の束と共にどのように実装されているかについてのドキュメントを読みました。これが機能しない理由のほとんどは理解できたと思いますが、私よりもこれをよく理解しているPythonの専門家が世の中にいると確信しています。

-- クリス

于 2012-07-10T23:03:16.983 に答える
1

マッパーは _init _method を追加しているようです。したがって、マッパーステートメントの後に次のことを行うと、再び機能します。

del Point.__init__

この種のものにマッパーを使用することが正しい考えかどうかはわかりません。マッパーは、正しく機能するために主キー ('no') を必要とする可能性が高く、nametuple には現在そのためのスペースがありません。

于 2011-10-03T12:28:38.063 に答える