是否可以在SQLalchemy中使用namedtuple?

时间:2011-10-03 11:06:56

标签: python sqlalchemy namedtuple

我一直试图让一个名字小组与SQLalchemy合作,但无济于事。网络搜索并不是很有启发性,我是Python和SQLalchemy的新手,所以我不确定我是不是追逐风车:(基本的想法是我有一个名字小组,即:

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

如果我是正确的话,它基本上会创建一个Point(元组)类。这首先工作正常,我可以创建像:

这样的对象
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)

为什么会这样?有谁知道如何使用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 ..我将在创建数据库之前设置它。我希望我有意义..

2 个答案:

答案 0 :(得分:3)

Namedtuples有很多行为使它们不适合用sqlalchemy进行映射:最重要的是,命名元素在创建后无法更改。这意味着在执行插入操作后,您无法使用namedtuple来跟踪数据库中行的状态。你通常会想要做这样的事情:

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

mapper(MyDataHolder, MyDataMeta)

...

newRow = MyDataHolder(None, 'AAA')

...

session.add(newRow)

当会话代码执行SQL以将新数据添加到数据库时,它将要更新 newRow ,以便 newRow.id 对应于数据库的id分配给你的行。但是因为 newRow 是一个不可变的元组,所以不能更改id来保存从数据库返回的主键。这使得命名元组很大程度上不适合于映射器。

__init__问题的发生是因为在__new__中初始化了namedtuple,然后预计不会改变。在创建对象后调用__init __(),因此无效。因此,named_uple的__init__只定义了一个参数:self。我猜测映射器假定__init __()正在处理类初始化,并且不知道__new__和不可变类型。看起来他们正在使用在创建时传递的args调用 classname .__ init __()。您可以通过指定自己的初始值设定项来“修复”此问题:__ init __(self,* args)但最终会出现弱点问题。

发生weakref错误是因为namedtuples使用__slots__来存储它们的值而不是可变的__dict__s。我知道__slots__的使用是一种内存优化,因此您可以有效地存储多个命名元组。我假设一个命名元组在创建之后不会改变。这包括添加属性,因此使用__slots__是值得的内存优化。但是,我并没有声称理解为什么他们的namedtuple类的作者不支持弱引用。这不会特别困难,但可能有一个很好的理由让我失踪。

我今天早上遇到了这个问题,并通过定义我自己的数据映射类来解决它,该数据映射是用_fieldset属性初始化的。这指定了我有兴趣看到映射的(子)字段集。然后我有一些时间阅读文档,了解如何实现namedtuples以及其他一些python内部。我认为我得到的大部分原因都不起作用,但我确信那里有蟒蛇专家比我更了解这一点。

- 克里斯

答案 1 :(得分:1)

映射器似乎添加了一个_ init _method。因此,在mapper语句使其再次起作用后执行以下操作:

del Point.__init__

我不确定使用mapper来做这类事情是正确的想法。映射器很可能需要主键('no')才能正常工作,你的名字组目前没有空间。