是否可以在SQLalchemy中使用namedtuple?

use*_*539 6 python sqlalchemy namedtuple

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

Point=namedtuple('Point',['x','y'])
Run Code Online (Sandbox Code Playgroud)

如果我是正确的,它基本上创建一个类Point(元组).这首先工作正常,我可以创建如下对象:

p=Point(3,4)
Run Code Online (Sandbox Code Playgroud)

但是在我创建引擎等并调用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)
Run Code Online (Sandbox Code Playgroud)

任何想法为什么会发生?有谁知道如何使用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)
Run Code Online (Sandbox Code Playgroud)

主要的想法是我想要一个可以轻松存储在数据库中的命名集合.所以这:

class Bunch:
    __init__ = lambda self, **kw: setattr(self, '__dict__', kw)
Run Code Online (Sandbox Code Playgroud)

不会与sqlalchemy合作(我认为).我可以创建一个Bunch类,但我事先不知道我要在我的集合中存储多少个int ..我将在创建数据库之前设置它.我希望我有意义..

小智 5

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

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

mapper(MyDataHolder, MyDataMeta)

...

newRow = MyDataHolder(None, 'AAA')

...

session.add(newRow)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

- 克里斯


Gar*_*rwe 1

映射器似乎添加了一个_init_方法。因此,在映射器语句之后执行以下操作可以使其再次工作:

del Point.__init__
Run Code Online (Sandbox Code Playgroud)

我不确定使用映射器来处理此类事情是否是正确的想法。映射器很可能需要主键(“否”)才能正常工作,而您的名称元组当前没有空间。