2009-09-24 2 views
13

Lassen Sie uns sagen, ich habe nicht mehr als ein oder zwei Dutzend Objekte mit unterschiedlichen Eigenschaften, wie die folgenden:Kleine Tabellen in Python?

UID, Name, Wert, Farbe, Typ, Standort

ich in der Lage sein wollen Rufen Sie alle Objekte mit Location = "Boston" oder Type = "Primary" auf. Klassischer Datenbankabfragetyp stuff.

Die meisten Tabellenlösungen (pytables, * sql) sind wirklich übertrieben für solch eine kleine Menge von Daten. Sollte ich einfach über alle Objekte iterieren und für jede Datenspalte ein eigenes Wörterbuch erstellen (indem ich Wörter zu Wörtern hinzufüge, wenn ich neue Objekte hinzufüge)?

Diese dicts wie dies schaffen würde:

{ 'Boston': [234, 654, 234], 'Chicago': [324, 765, 342]} - wenn diese 3-stelligen Einträge Dinge wie UID darstellen .

Wie Sie sehen können, wäre eine Abfrage ein wenig schmerzhaft.

Irgendwelche Gedanken an eine Alternative?

Antwort

14

Für kleine relationale Probleme liebe ich Pythons eingebaute sets.

Für das Beispiel location = 'Boston' OR type = 'Primär', wenn Sie diese Daten hatte:

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

Sie können die WHERE ... OR ... Abfrage wie folgt tun:

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 mit nur einem Ausdruck:

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

Sie können auch die Funktionen inverwenden 10, um ziemlich effiziente Abfragen der Daten zu erstellen. Zum Beispiel, wenn Sie etwas ähnlich einem GROUP BY city tun:

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

Sie könnten auch Indizes manuell erstellen (Build eine dict Mapping Lage zu Benutzer-ID) die Dinge zu beschleunigen. Wenn dies zu langsam oder unhandlich wird, würde ich wahrscheinlich zu sqlite wechseln, das jetzt in der Standardbibliothek Python (2.5) enthalten ist.

+0

Vielen Dank, ich habe die eingebauten Sets noch nie zuvor benutzt. Dies sollte zumindest deutlicher machen, was im Code passiert. – akoumjian

2

Wenn es wirklich eine kleine Menge an Daten, würde ich nicht mit einem Index der Mühe und wahrscheinlich schreibe nur eine Hilfsfunktion:

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

Welche gut aussehende Abfragen wie folgt ermöglicht:

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

Dies ist auch sehr hilfreich. Die Ästhetik dieses Codes hält mehr mit dem, was die Leute von Python erwarten. Allerdings mag ich das bisschen Flexibilität, das Sets bei Gewerkschaften/Kreuzungen bieten. – akoumjian

5

Ich glaube nicht, dass SQLite "Overkill" sein würde - es kommt mit Standard Python seit 2.5, also keine Notwendigkeit, Sachen zu installieren, und es kann Datenbanken im Speicher oder lokalen Dateien machen und bearbeiten. Wirklich, wie könnte es einfacher sein ...? Wenn Sie alles im Speicher einschließlich der Anfangswerte möchten, und wollen dicts verwenden, um diese Anfangswerte, zum Beispiel zum Ausdruck bringen ...:

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 

und jetzt Ihre speicher winzige „db“ ist bereit zu gehen .Es ist nicht schwieriger, eine Datenbank in einer Festplattendatei zu erstellen und/oder die Anfangswerte aus einer Textdatei, einer CSV usw. zu lesen.

Querying ist besonders flexibel, einfach und süß, zum Beispiel, können Sie String Einfügen und Parametersubstitution nach Belieben mischen ...:

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'] emittiert, genau wie die elegantere, aber äquivalente

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

und Ihre gewünschte Abfrage ist einfach:

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

usw. S sehr - was ist nicht zu mögen?

+0

Der Vorteil scheint noch mehr Flexibilität bei der Abfrage zu sein, und die Möglichkeit, eine db aus dem Speicher zu und von Dateien zu verschieben, exportieren, etc. Der Nachteil, wie ich es sehe, ist, dass Sie importieren mussten zusätzliches Modul (keine große Sache), und jemand anderes, der den Code liest, muss lernen, was all diese Objektmethoden sind. Es ist eine feine Lösung, aber nicht wirklich die einfachste Imho. – akoumjian

+1

Willst du wetten, dass mehr Leute bereits über die Python DB API Bescheid wissen als über Sets, Genexps, '.union' usw.? -) –

+1

Ich sehe viel mehr Eleganz in der Verwendung eines Wörterbuchs, eines Sets und einer Schleife als bei der Verwendung ein Datenbankobjekt, ein Cursor-Objekt und sechs Methoden. – akoumjian