如何在Python中实现数据库风格的表格
我正在实现一个类,类似于一个典型的数据库表:
- 有命名的列和没有命名的行
- 有一个主键,可以用来引用这些行
- 支持通过主键和列名来获取和赋值
- 可以为任何列添加唯一或非唯一索引,这样可以快速找到某列中有特定值的行(或一组行)
- 删除一行的操作很快,采用“软删除”的方式:行数据仍然保留,但会被标记为删除,之后的查询中不会显示出来
- 添加列的操作也很快
- 行的添加很少发生
- 列的删除也很少发生
我决定直接实现这个类,而不是使用sqlite的封装。
那么,使用什么样的数据结构比较好呢?
举个例子,我考虑的一种方法是使用字典。它的键是表中主键列的值;它的值是以以下方式实现的行:
作为列表。列号映射到列名(一个方向用列表,另一个方向用映射)。在这里,获取操作会先把列名转换为列号,然后找到列表中对应的元素。
作为字典。列名是这个字典的键。
我不太确定这两种方法的优缺点。
我想自己写代码的原因是:
- 我需要跟踪行的删除情况。也就是说,我希望随时能够报告哪些行被删除了,以及删除的“原因”(这个“原因”会传递给我的删除方法)。
- 在建立索引时,我需要一些报告(例如,在构建非唯一索引时,我想检查某些条件,并报告是否违反了这些条件)。
3 个回答
你真的应该使用SQLite。
关于你提到的第一个理由(跟踪删除原因),你可以很简单地通过创建一个第二个表来实现,当你删除数据时,就把这些数据“移动”到这个新表里。在这个新表中,你可以增加一个额外的列来记录删除原因,或者再创建一个表来关联这些信息。如果删除原因不是每次都需要的,你甚至可以在原始表上使用触发器,这样在删除之前就可以把要删除的行复制到新表中,或者使用用户自定义的函数来获取删除原因。
至于索引的原因,虽然有一些约束等可以解决,但如果没有更多的细节,我无法直接给出具体的建议。
我建议可以用元组或列表来做字典的键。比如说,my_dict(("col_2", "row_24"))
这样就能获取到某个元素。从这个基础上出发,写一些方法,比如 'get_col' 和 'get_row',还有 'get_row_slice' 和 'get_col_slice',就会变得相对简单(虽然对于非常大的数据库来说,速度可能会慢一些)。
用这种方式构建字典有两个好处。第一,获取单个元素的速度会比你之前提到的两种方法快;第二,如果你想在列中有不同数量的元素(或者有些元素缺失),这样做会非常简单,而且节省内存。
这只是我的一个想法 :) 我很想知道大家会推荐什么包!
祝好
你可以考虑创建一个类,里面使用一个内存中的sqlite表来存储数据:
import sqlite3
class MyTable(object):
def __init__(self):
self.conn=sqlite3.connect(':memory:')
self.cursor=self.conn.cursor()
sql='''\
CREATE TABLE foo ...
'''
self.execute(sql)
def execute(self,sql,args):
self.cursor.execute(sql,args)
def delete(self,id,reason):
sql='UPDATE table SET softdelete = 1, reason = %s where tableid = %s'
self.cursor.execute(sql,(reason,id,))
def verify(self):
# Check that certain conditions are true
# Report (or raise exception?) if violated
def build_index(self):
self.verify()
...
软删除可以通过添加一个名为softdelete
的布尔类型列来实现。
同样,你也可以添加一个列来存储删除的原因。
如果想要恢复被删除的数据,只需更新这一行并把softdelete
的值改回来就行了。
要选择那些没有被删除的行,可以用SQL条件WHERE softdelete != 1
来实现。
你可以写一个verify
方法来检查你的数据是否满足某些条件。然后可以在build_index
方法中调用这个方法。
另一种选择是使用numpy的结构化掩码数组。
很难说哪种方法最快。也许唯一确定的方法就是为每种方法写代码,然后用timeit在真实数据上进行性能测试。