2009-09-24 10 views
13

Diciamo che non ho più di uno o due dozzine di oggetti con proprietà diverse, come ad esempio il seguente:Piccole tabelle in Python?

UID, nome, valore, colore, tipo, Location

Voglio essere in grado di richiama tutti gli oggetti con Location = "Boston", o Type = "Primary". Classico tipo di query del database.

La maggior parte delle soluzioni di tabella (pytables, * sql) sono davvero esagerate per un insieme di dati così piccolo. Dovrei semplicemente scorrere tutti gli oggetti e creare un dizionario separato per ogni colonna di dati (aggiungendo valori ai dizionari mentre aggiungo nuovi oggetti)?

Ciò creerebbe dicts come questo:

{ 'Boston': [234, 654, 234], 'Chicago': [324, 765, 342]} - dove quei 3 voci cifre rappresentano le cose come UID .

Come potete vedere, interrogare questo sarebbe un po 'un dolore.

Qualche idea di alternativa?

risposta

14

Per problemi relazionali di piccole dimensioni mi piace usare l'incorporata di Python sets.

Per l'esempio di posizione = 'Boston' O type = 'primaria', se tu avessi questi dati:

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"), 
    #... 
} 

Si può fare la query WHERE ... OR ... in questo modo:

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) 

Or con una sola espressione:

result = set(u for u in users if users[u]['Location'] == 'Boston' 
           or users[u]['Type'] == 'Primary') 

È inoltre possibile utilizzare le funzioni in itertools per creare query abbastanza efficienti dei dati. Per esempio, se si vuole fare qualcosa di simile a un GROUP BY city:

cities = ('Boston', 'New York', 'Chicago') 
cities_users = dict(map(lambda city: (city, ifilter(lambda u: users[u]['Location'] == city, users)), cities)) 

Si potrebbe anche costruire indici manualmente (costruire una mappatura dict posizione per User ID) per accelerare le cose. Se questo diventa troppo lento o ingombrante, probabilmente passerei a sqlite, che ora è incluso nella libreria standard di Python (2.5).

+0

Grazie, non ho mai usato i set integrati prima. Questo dovrebbe almeno rendere più esplicito cosa sta succedendo nel codice. – akoumjian

2

Se è davvero una piccola quantità di dati, che non avevo fastidio con un indice e probabilmente solo scrivere una funzione di supporto:

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

che permetterà query belle simile a questo:

result = search(users, Type="Secondary") 
+0

Anche questo è molto utile. L'estetica di questo codice si mantiene maggiormente con ciò che le persone si aspettano con Python. Tuttavia, mi piace il poco di flessibilità che i set sembrano offrire con i sindacati/intersezioni. – akoumjian

5

Non penso che sqlite sia "overkill" - viene fornito con Python standard dal 2.5, quindi non c'è bisogno di installare roba, e può creare e gestire database in memoria o su file disco locali. Davvero, come potrebbe essere più semplice ...? Se si vuole tutto in memoria compresi i valori iniziali, e si desidera utilizzare dicts per esprimere quei valori iniziali, per esempio ...:

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 

e ora il tuo in-memory piccolo "db" è pronto ad andare .Non è più difficile creare un DB in un file su disco e/o leggere i valori iniziali da un file di testo, un CSV e così via, ovviamente.

Interrogazione è particolarmente flessibile, facile e dolce, per esempio, si possono mescolare l'inserimento di stringa e la sostituzione di parametro a volontà ...:

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

emette [u'Mr. Foo', u'Mr. Quux'], proprio come il più elegante, ma equivalente

print [r["Name"] for r in where('Type=?', ["Secondary"])] 

e la vostra query desiderata solo:

print [r["Name"] for r in where('Location="Boston" or Type="Primary"')] 

ecc S eriously - cosa non va?

+0

Il vantaggio sembra essere ancora più flessibile nelle query e l'opzione di spostare facilmente un db dalla memoria ae da file, esportando, ecc. Lo svantaggio, come vedo, è che hai dovuto importare un modulo aggiuntivo (non un grosso problema), e qualcun altro che legge il codice deve imparare quali sono tutti quei metodi oggetto. È una soluzione eccellente, ma non proprio l'imho più semplice. – akoumjian

+1

Scommettiamo che più persone conoscono già l'API del DB Python piuttosto che su set, genexps, '.union', etc? -) –

+1

Ho appena visto molta più eleganza nell'usare un dizionario, un set e un loop rispetto all'utilizzo un oggetto database, un oggetto cursore e sei metodi. – akoumjian

Problemi correlati