sqlite
— это база данных SQL, и она лучше всего работает, когда используется как таковая (обернутая в SQLAlchemy или что-то в этом роде, если вы действительно настаивать;-).
Такой синтаксис, как d[key="NAME", 'Joe']
, является просто недопустимым для Python, независимо от того, сколько вы будете оборачивать, пыхтеть и пыхтеть. Простая оболочка класса вокруг соединения с БД проста, но она никогда не даст вам такого синтаксиса — что-то вроде d.fetch('Joe', key='Name')
достаточно легко реализовать, но синтаксис индексирования сильно отличается от синтаксиса вызовов функций, и даже в последних именованных аргументах должны стоять < em>после позиционных.
Если вы готовы отказаться от своих честолюбивых мечтаний о синтаксисе в пользу разумного синтаксиса Python и вам нужна помощь в разработке класса для реализации последнего, не стесняйтесь спрашивать, конечно (я довольно скоро пойду спать, но я уверен, что другие, более поздние спящие, будут стремиться помочь ;-).
Редактировать: учитывая пояснения OP (в комментарии), похоже, что метод 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
(чтобы создать новую БД или стереть существующую ) и open
, который, кажется, лучше соответствует желаниям OP, чем альтернатива (укажите __init__
путь к файлу БД и строку параметров, описывающую, как его открыть, точно так же, как такие модули, как gdbm
take -- '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']]
«Питонически абсурдное» поведение заключается в том, что d[x] = d[x]
не будет терпеть неудачу, потому что правая часть представляет собой список, например. с одним элементом (который представляет собой список значений столбца), в то время как для назначения элемента абсолютно требуется список, например. четыре элемента (значения столбца). Эта абсурдность заключается в запрошенной семантике OP, и ее можно изменить только путем повторного радикального изменения такой абсурдной требуемой семантики (например, принудительное назначение элемента для списка списков в RHS и использование executemany
вместо простого execute
).
Неуникальность ключей также делает невозможным угадывание, предназначен ли d[x] = v
для ключа k
, который соответствует некоторому числу n
записей таблицы, заменять один (и если да, то какой?!) или все эти записи, или вместо этого добавьте другую новую запись. В приведенном выше коде я использовал интерпретацию «добавить еще одну запись», но с оператором SQL REPLACE
, который, если CREATE TABLE
изменить для указания некоторых ограничений уникальности, изменит некоторую семантику с «добавить запись» на «заменить записи», если и когда ограничения уникальности в противном случае были бы нарушены.
Я позволю вам всем поиграть с этим кодом и подумать, насколько огромен семантический разрыв между сопоставлениями Python и реляционными таблицами, который OP отчаянно стремится преодолеть (очевидно, как побочный эффект его стремления «использовать более приятный синтаксис» чем SQL — интересно, просмотрел ли он SqlAlchemy, как я рекомендовал).
Я думаю, в конце концов, важным уроком является то, что я изложил в самом начале, в первом абзаце той части ответа, которую я написал вчера, и я цитирую...:
sqlite
— это база данных SQL, и она лучше всего работает, когда используется как таковая (обернутая в SQLAlchemy или что-то в этом роде, если вы действительно настаивать;-).
person
Alex Martelli
schedule
12.08.2010