7

我一直试图让一个命名元组与 SQLalchemy 一起工作,但无济于事.. 网络搜索并不是很有启发性,而且我是 Python 和 SQLalchemy 的新手,所以我不确定我是否在追逐风车: (基本思想是我有一个命名元组,即:

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)

任何想法为什么会发生这种情况?有谁知道如何使命名元组与 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 类,但我不会事先知道我想在我的集合中存储多少个整数。我会在创建数据库之前设置它。我希望我是有道理的..

4

2 回答 2

4

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

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__ 问题是因为 namedtuple 在 __new__ 中初始化,然后预计不会更改。__init__() 在对象创建后被调用,因此无效。因此,namedtuple 的 __init__ 只定义了一个参数:self。我猜映射器假设 __init__() 正在处理类初始化并且不知道 __new__ 和不可变类型。看起来他们正在使用在创建时传递的参数调用classname .__init__() 。您可以通过指定自己的初始化程序来“解决”这个问题: __init__(self, *args) 但最终会遇到弱引用问题。

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

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

- 克里斯

于 2012-07-10T23:03:16.983 回答
1

The mapper seems to add a _init_method. So doing the following after the mapper statement makes it work again:

del Point.__init__

I'm not sure that using a mapper for this type of thing is the right idea. The mapper will most likely need the primary key ('no') in order to work correctly, which your nametuple currently does not have space for.

于 2011-10-03T12:28:38.063 回答