sqlite
является базой данных SQL и работает намного лучше, когда используется как таковой (обернуто в SQLAlchemy или что-то еще, если вы действительно настаиваете; -).
Синтаксис, такой как d[key="NAME", 'Joe']
, является просто недопустимым Python, независимо от того, сколько вы можете использовать для переноса, пыхтения и пыхтения. Простая оболочка класса для соединения с БД проста, но она никогда не даст вам такой синтаксис - что-то вроде d.fetch('Joe', key='Name')
достаточно просто достичь, но индексирование имеет очень другой синтаксис от вызовов функций, и даже в последних именованных аргументах необходимо следуйте за позиционными.
Если вы готовы отказаться от своих амбициозных мечтаний о синтаксисе в пользу разумного синтаксиса Python и вам нужна помощь в разработке класса для реализации последнего, не стесняйтесь спрашивать, конечно (я скоро ухожу спать, но Я уверен, что другие, позже спящие будут готовы помочь; -).
Редактировать : учитывая пояснения ОП (в комментарии), похоже, что set_key
метод приемлем для поддержки синтаксиса, приемлемого для Python (хотя семантика, конечно, все равно будет немного, так как OP хочет «подобный диктату» объект, который может иметь неуникальные ключи - такого нет в Python, на самом деле ... но мы можем немного приблизить его, по крайней мере).
Итак, вот самый первый набросок (требуется Python 2.6 или выше - просто потому, что я использовал collections.MutableMapping
для получения других dict-подобных методов и .format
для форматирования строк; если вы застряли в 2.5, % -форматирование строк и 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
(для создания нового DB или стереть существующий) и open
, который, кажется, соответствует желаниям ОП лучше, чем альтернатива (пусть __init__
выберет путь к файлу БД и строку опции, описывающую, как ее открыть, точно так же как модули, такие как gdbm
дубль - 'r'
, чтобы открыть только для чтения, 'c'
, чтобы создать или уничтожить, 'w'
, чтобы открыть для чтения-записи - легко настроить, конечно). Среди столбцов, переданных (как строка, разделенная пробелами) в create
, должен быть именем с именем ID
(я не особо заботился о том, чтобы выдавать «правильные» ошибки для любой из много, много пользовательских ошибок, которые могут возникнуть при создании и использовании экземпляров этого класса; ошибки будут возникать при любом неправильном использовании, но не обязательно очевидные для пользователя).
Как только экземпляр открывается (или создается), он ведет себя как можно ближе к диктату, за исключением того, что все установленные значения должны быть списками точно правильной длины, в то время как значения , возвращаемые , являются списками списки (из-за странной проблемы «неуникального ключа»). Например, приведенный выше код при запуске печатает
2 items in table created.
[['Rose', '12111986', '24', 'F']]
[['Joe', '01011980', '30', 'M']]
2 items in table opened.
[['1', '01011980', '30', 'M']]
Поведение "Pythonically абсурд" состоит в том, что d[x] = d[x]
не сможет потерпеть неудачу - потому что правая часть списка, например. с одним элементом (который представляет собой список значений столбца), в то время как для назначения элемента абсолютно необходим список с, например, четыре элемента (значения столбца). Этот абсурд содержится в запрошенной семантике OP и может быть изменен только путем радикального изменения такой абсурдной требуемой семантики (например, принуждение присвоения элемента иметь список списков в RHS и использование executemany
вместо простого execute
).
Неединственность ключей также делает невозможным угадывание, если d[x] = v
для ключа k
, который соответствует некоторому числу n
записей таблицы, предназначен для замены одного (и если это так, который один ?!) или все эти записи, или вместо этого добавьте еще одну новую запись. В приведенном выше коде я принял интерпретацию «добавить еще одну запись», но с оператором SQL REPLACE
, который при изменении CREATE TABLE
для указания некоторых ограничений уникальности изменит семантику с «добавить запись» на « заменить записи ", если и когда ограничения уникальности были бы иначе нарушены.
Я позволю вам всем поиграть с этим кодом и показать, насколько огромен семантический разрыв между отображениями Python и реляционными таблицами, что OP отчаянно стремится к преодолению (очевидно, как побочный эффект от его желания "использовать"более приятный синтаксис, чем позволяет SQL - интересно, если бы он посмотрел на SqlAlchemy, как я рекомендовал).
Я думаю, в конце концов, важный урок - то, что я изложил прямо вначнем с того, что в первом абзаце той части ответа, которую я написал вчера, и я сам цитирую ...:
sqlite
- это база данных SQL, и она работает намного лучше, когда используется как таковая(завернутый в SQLAlchemy или что-то еще, если вы действительно настаиваете; -).