Маленькие таблицы в Python?
Скажем, у меня не более одного или двух десятков объектов с разными свойствами, например:
UID, имя, значение, цвет, тип, расположение
Я хочу иметь возможность вызывать все объекты с помощью Location = "Boston", или Type = "Primary". Классический материал типа запроса базы данных.
Большинство табличных решений (pytables, * sql) действительно переполнены для такого небольшого набора данных. Должен ли я просто перебирать все объекты и создавать отдельный словарь для каждого столбца данных (добавление значений в словари при добавлении новых объектов)?
Это создало бы так:
{'Boston': [234, 654, 234], 'Chicago': [324, 765, 342]} - где эти трехзначные записи представляют такие вещи, как UID.
Как вы можете видеть, запрос на это будет немного больно.
Любые мысли об альтернативе?
Ответы
Ответ 1
Для небольших реляционных задач мне нравится использовать встроенный Python sets.
Для примера location = 'Boston' OR type = 'Primary', если у вас были эти данные:
users = {
1: dict(Name="Mr. Foo", Location="Boston", Type="Secondary"),
2: dict(Name="Mr. Bar", Location="New York", Type="Primary"),
3: dict(Name="Mr. Quux", Location="Chicago", Type="Secondary"),
#...
}
Вы можете сделать запрос WHERE ... OR ...
следующим образом:
set1 = set(u for u in users if users[u]['Location'] == 'Boston')
set2 = set(u for u in users if users[u]['Type'] == 'Primary')
result = set1.union(set2)
Или с одним выражением:
result = set(u for u in users if users[u]['Location'] == 'Boston'
or users[u]['Type'] == 'Primary')
Вы также можете использовать функции itertools для создания довольно эффективных запросов данных. Например, если вы хотите сделать что-то похожее на GROUP BY city
:
cities = ('Boston', 'New York', 'Chicago')
cities_users = dict(map(lambda city: (city, ifilter(lambda u: users[u]['Location'] == city, users)), cities))
Вы также можете создавать индексы вручную (построить сопоставление dict
Location to User ID), чтобы ускорить процесс. Если это станет слишком медленным или громоздким, я бы, вероятно, переключился на sqlite, который теперь включен в стандартную библиотеку Python (2.5).
Ответ 2
Я не думаю, что sqlite будет "overkill" - он поставляется со стандартным Python с 2,5, поэтому нет необходимости устанавливать материал, и он может создавать и обрабатывать базы данных в памяти или на локальных дисковых файлах. Действительно, как это может быть проще...? Если вы хотите, чтобы все в памяти, включая начальные значения, и хотите использовать dicts для выражения этих начальных значений, например...:
import sqlite3
db = sqlite3.connect(':memory:')
db.execute('Create table Users (Name, Location, Type)')
db.executemany('Insert into Users values(:Name, :Location, :Type)', [
dict(Name="Mr. Foo", Location="Boston", Type="Secondary"),
dict(Name="Mr. Bar", Location="New York", Type="Primary"),
dict(Name="Mr. Quux", Location="Chicago", Type="Secondary"),
])
db.commit()
db.row_factory = sqlite3.Row
и теперь ваш крошечный "db" в памяти готов к работе. Не сложнее сделать БД в файле диска и/или прочитать начальные значения из текстового файла, CSV и т.д., Конечно.
Querying особенно гибкий, легкий и приятный, например, вы можете смешать вставку строк и замену параметров по своему желанию...:
def where(w, *a):
c = db.cursor()
c.execute('Select * From Users where %s' % w, *a)
return c.fetchall()
print [r["Name"] for r in where('Type="Secondary"')]
испускает [u'Mr. Foo', u'Mr. Quux']
, как и более элегантный, но эквивалентный
print [r["Name"] for r in where('Type=?', ["Secondary"])]
и ваш желаемый запрос:
print [r["Name"] for r in where('Location="Boston" or Type="Primary"')]
и т.д.. Серьезно - что не нравится?
Ответ 3
Если это действительно небольшой объем данных, я бы не стал беспокоиться об индексе и, возможно, просто написал вспомогательную функцию:
users = [
dict(Name="Mr. Foo", Location="Boston", Type="Secondary"),
dict(Name="Mr. Bar", Location="New York", Type="Primary"),
dict(Name="Mr. Quux", Location="Chicago", Type="Secondary"),
]
def search(dictlist, **kwargs):
def match(d):
for k,v in kwargs.iteritems():
try:
if d[k] != v:
return False
except KeyError:
return False
return True
return [d for d in dictlist if match(d)]
Который разрешит подобные запросы:
result = search(users, Type="Secondary")