python - 是否可以将 namedtuple 与 SQLalchemy 一起使用?

标签 python sqlalchemy namedtuple

我一直在尝试让 namedtuple 与 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)

知道为什么会这样吗?有谁知道如何使 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 类,但我不会事先知道我想在我的集合中存储多少个整数。我会在创建我的数据库之前设置它。我希望我说得有道理..

最佳答案

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

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

mapper(MyDataHolder, MyDataMeta)

...

newRow = MyDataHolder(None, 'AAA')

...

session.add(newRow)

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

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

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

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

-- 克里斯

关于python - 是否可以将 namedtuple 与 SQLalchemy 一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7634177/

相关文章:

python - 基于带有 sqlalchemy 声明性基础的另一个字段的 onupdate

python - 命名元组错误

python - 如何将特殊方法 'Mixin' 应用于 typing.NamedTuple

python - PostgreSQL 如何追加执行查询的多个结果?

python - 假设 - 在测试之间重用@given

python - "De-instrument"来自 sqlalchemy ORM 的实例化对象

python - Python 中的 Namedtuple 格式化/ pretty-print

python - Python 中 matplotlib 的 (x,y) 绘图替代方案是什么?

python - PySImpleGui 错误代码 AttributeError : module 'PySimpleGUI' has no attribute 'Popup'

mysql - 带有 ModWSGI 和 flask 的 SQLAlchemy 中的 "Unknown MySQL Server"