2009-09-24 1 views
13

Скажем, у меня не более одного или двух десятков объектов с различными свойствами, такими как:Маленькие таблицы в Python?

UID, имя, значение, цвет, тип, расположение

Я хочу, чтобы иметь возможность вызовите все объекты с помощью Location = «Boston» или Type = «Primary». Классический материал типа запроса базы данных.

Большинство настольных решений (pytables, * sql) действительно переполняют такой небольшой набор данных. Должен ли я просто перебирать все объекты и создавать отдельный словарь для каждого столбца данных (добавление значений в словари при добавлении новых объектов)?

Это создало бы dicts как это:

{ 'Boston': [234, 654, 234], 'Чикаго': [324, 765, 342]} - где эти 3 цифры записи представляют собой такие вещи, как UID-х ,

Как вы можете видеть, запрос на это будет немного больно.

Любые мысли об альтернативе?

ответ

14

Для небольших проблем с реляцией я обожаю использование встроенного Python sets.

Для примера место = 'Boston' OR типа = '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 отображение Место для ID пользователя), чтобы ускорить процесс. Если это станет слишком медленным или громоздким, я бы, вероятно, переключился на sqlite, который теперь включен в стандартную библиотеку Python (2.5).

+0

Спасибо, я никогда раньше не использовал встроенные наборы. Это должно по крайней мере сделать более ясным, что происходит в коде. – akoumjian

2

Если это действительно небольшое количество данных, я бы не заморачиваться с индексом и, вероятно, просто написать вспомогательную функцию:

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") 
+0

Это тоже очень полезно. Эстетика этого кода больше зависит от того, что люди ожидают от Python. Тем не менее, мне нравится небольшая гибкость, которая, как представляется, предлагает с профсоюзами/пересечениями. – akoumjian

5

Я не думаю, что 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 и т. Д., Конечно.

Запросы особенно гибкий, легкий и сладкий, например, вы можете смешать вставки строк и подстановку параметров по желанию ...:

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"')] 

и т.д. S грубо - что не нравится?

+0

Преимущество, по-видимому, является еще большей гибкостью при запросе и возможностью легко перемещать db из памяти в файлы и из них, экспортировать и т. Д. Недостаток, как я вижу, заключается в том, что вам пришлось импортировать дополнительный модуль (а не огромная сделка), а кто-то другой, читающий код, должен узнать, что все эти методы объекта. Это прекрасное решение, но не самое простое imho. – akoumjian

+1

Хотите сделать ставку на то, что больше людей уже знают о API-интерфейсе Python, чем о наборах, genexps, '.union' и т. Д.?) –

+1

Я просто вижу гораздо более элегантность в использовании одного словаря, одного набора и цикла, чем при использовании объект базы данных, объект курсора и шесть методов. – akoumjian