我有一个这样的数据结构,
"ID NAME BIRTH AGE SEX"
=================================
1 Joe 01011980 30 M
2 Rose 12111986 24 F
3 Tom 31121965 35 M
4 Joe 15091990 20 M
我想使用 python + sqlite 以简单的方式存储和查询数据。我正在尝试设计一个像对象这样的dict来存储和检索这些信息,也可以通过简单的方式与其他应用程序共享数据库。(just a plain database table for other application
,then the pickle and ySerial like object should not fit for it
。)
例如:
d = mysqlitedict.open('student_table')
d['1'] = ["Joe","01011980","30","M"]
d['2'] = ["Rose","12111986","24","F"]
这可能是合理的,因为我可以使用__setitem__()
来获取该值,如果“ID”作为键,其余部分作为该dict类似对象的值。
问题是如果我想在语义上使用其他字段作为键,例如使用“NAME”:
d['Joe'] = ["1","01011980","30","M"]
这将是一个问题,因为类似对象的dict应该在语义上具有键/值对,因为现在“ID”是键,“NAME”不能在此处覆盖键。
然后我的问题是,我可以设计我的课程然后我可以这样做吗?
d[key="NAME", "Joe"] = ["1","01011980","30","M"]
d[key="ID",'1'] = ["Joe","01011980","30","M"]
d.update(key = "ID", {'1':["Joe","01011980","30","M"]})
>>>d[key="NAME", 'Joe']
["1","Joe","01011980","30","M"]
["1","Joe","15091990","20","M"]
>>>d.has_key(key="NAME", 'Joe']
True
我将不胜感激任何回复!
KC
答案 0 :(得分:3)
sqlite
是一个SQL数据库,在使用时效果最好(包含在SQLAlchemy或其他任何内容,如果你真的坚持; - )。
d[key="NAME", 'Joe']
之类的语法只是非法的Python,无论你做多少包装和喘气和喘气。围绕数据库连接的简单类包装很容易,但是它永远不会给你那种语法 - 像d.fetch('Joe', key='Name')
这样的东西很容易实现,但是索引与函数调用的语法非常不同,甚至在后面命名参数必须在位置之后。
如果你愿意放弃你雄心勃勃的语法梦想,支持合理的Python语法,并需要帮助设计一个类来实现后者,当然可以随意问(我很快就会上床睡觉,但是我相信其他的,后来的睡眠者会渴望得到帮助; - )。
编辑:给出OP的澄清(在评论中),看起来set_key
方法可以接受维护Python可接受的语法(尽管语义当然仍然是tad off,因为OP想要一个“dict-like”对象,它可能有非唯一键 - 在Python中没有这样的东西,真的......但是,我们可以近似一点,至少)。
所以,这是第一个草图(需要Python 2.6或更高版本 - 只是因为我使用collections.MutableMapping
来获取其他类似dict的方法,并使用.format
来格式化字符串;如果你是卡在2.5,%-formatting字符串和UserDict.DictMixin将改为工作):
import collections
import sqlite3
class SqliteDict(collections.MutableMapping):
@classmethod
def create(cls, path, columns):
conn = sqlite3.connect(path)
conn.execute('DROP TABLE IF EXISTS SqliteDict')
conn.execute('CREATE TABLE SqliteDict ({0})'.format(','.join(columns.split())))
conn.commit()
return cls(conn)
@classmethod
def open(cls, path):
conn = sqlite3.connect(path)
return cls(conn)
def __init__(self, conn):
# looks like for sime weird reason you want str, not unicode, when feasible, so...:
conn.text_factory = sqlite3.OptimizedUnicode
c = conn.cursor()
c.execute('SELECT * FROM SqliteDict LIMIT 0')
self.cols = [x[0] for x in c.description]
self.conn = conn
# start with a keyname (==column name) of `ID`
self.set_key('ID')
def set_key(self, key):
self.i = self.cols.index(key)
self.kn = key
def __len__(self):
c = self.conn.cursor()
c.execute('SELECT COUNT(*) FROM SqliteDict')
return c.fetchone()[0]
def __iter__(self):
c = self.conn.cursor()
c.execute('SELECT * FROM SqliteDict')
while True:
result = c.fetchone()
if result is None: break
k = result.pop(self.i)
return k, result
def __getitem__(self, k):
c = self.conn.cursor()
# print 'doing:', 'SELECT * FROM SqliteDict WHERE {0}=?'.format(self.kn)
# print ' with:', repr(k)
c.execute('SELECT * FROM SqliteDict WHERE {0}=?'.format(self.kn), (k,))
result = [list(r) for r in c.fetchall()]
# print ' resu:', repr(result)
for r in result: del r[self.i]
return result
def __contains__(self, k):
c = self.conn.cursor()
c.execute('SELECT * FROM SqliteDict WHERE {0}=?'.format(self.kn), (k,))
return c.fetchone() is not None
def __delitem__(self, k):
c = self.conn.cursor()
c.execute('DELETE FROM SqliteDict WHERE {0}=?'.format(self.kn), (k,))
self.conn.commit()
def __setitem__(self, k, v):
r = list(v)
r.insert(self.i, k)
if len(r) != len(self.cols):
raise ValueError, 'len({0}) is {1}, must be {2} instead'.format(r, len(r), len(self.cols))
c = self.conn.cursor()
# print 'doing:', 'REPLACE INTO SqliteDict VALUES({0})'.format(','.join(['?']*len(r)))
# print ' with:', r
c.execute('REPLACE INTO SqliteDict VALUES({0})'.format(','.join(['?']*len(r))), r)
self.conn.commit()
def close(self):
self.conn.close()
def main():
d = SqliteDict.create('student_table', 'ID NAME BIRTH AGE SEX')
d['1'] = ["Joe", "01011980", "30", "M"]
d['2'] = ["Rose", "12111986", "24", "F"]
print len(d), 'items in table created.'
print d['2']
print d['1']
d.close()
d = SqliteDict.open('student_table')
d.set_key('NAME')
print len(d), 'items in table opened.'
print d['Joe']
if __name__ == '__main__':
main()
该类不是要直接实例化(尽管可以通过将开放的sqlite3连接传递给具有适当SqliteDict
表的数据库来实现),但是通过两个类方法create
(创建一个新的数据库或消灭现有的数据库)和open
,它似乎比OP的更好地匹配OP的愿望(让__init__
获取数据库文件路径一个描述如何打开的选项字符串它就像gdbm
等模块一样 - 'r'
打开只读,'c'
创建或消除,'w'
打开读写 - 简单当然要调整)。在传递(作为空格分隔的字符串)到create
的列中,必须是一个名为ID
的列(我没有太在意提高“正确”在构建和使用此类实例时可能发生的许多用户错误中的任何错误;在所有不正确的用法上都会出现错误,但对用户来说不一定是错误的。)
一旦打开(或创建)实例,它的行为尽可能接近dict,除了所有值集必须是完全正确长度的列表,而值返回是列表列表(由于奇怪的“非唯一键”问题)。例如,上面的代码在运行时打印
2 items in table created.
[['Rose', '12111986', '24', 'F']]
[['Joe', '01011980', '30', 'M']]
2 items in table opened.
[['1', '01011980', '30', 'M']]
“Python-absurd”行为是d[x] = d[x]
会失败 - 因为右侧是一个列表,例如使用单个项目(列值列表),而项目分配绝对需要一个列表,例如四个项目(列值)。这种荒谬是在OP请求的语义中,并且只能通过再次彻底改变这种荒谬的必需语义来改变(例如,强制项目分配在RHS上有一个列表列表,并使用executemany
代替普通{ {1}})。
键的非唯一性也使得无法猜测execute
是否对应于某些数字d[x] = v
表条目的键k
是否意味着替换一个({1}}如果是,哪个一个?!)或所有这些条目,或者添加另一个新条目。在上面的代码中,我采用了“添加另一个条目”的解释,但是使用SQL语句n
,如果REPLACE
被更改为指定一些唯一性约束,则会更改“add”中的一些语义条目“to”替换条目“如果和否则会违反唯一性约束。
我会让大家玩这个代码,并反映Python映射和关系表之间的语义差距有多大,OP非常渴望桥接(显然是他对“使用”的冲击的副作用更好的语法“比SQL提供的 - 我想知道他是否已经按照我的建议查看了SqlAlchemy。”
我认为,最后,重要的一课是我在开始时所说的,在我昨天写的答案部分的第一段,我自我引用......:
CREATE TABLE
是一个SQL数据库并且有效 使用时最好(包裹 如果你在SQLAlchemy或其他什么 真的坚持; - )。