__contains__ andere Werte als True/False liefern lassen?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
bearcat
User
Beiträge: 5
Registriert: Freitag 27. Mai 2011, 10:09

Hallo.

Nachdem ich nun Google und die Suchfunktion hier im Forum genutzt habe, aber nicht wirklich zu einem befriedigenden Ergebnis gekommen.

Ich habe eine Klasse x, die durch eine Konfigurationsdatei verschiedene Daten erhält, die unter bestimmten Titeln in einem Dictionary gespeichert werden. Es kann sich dabei um einzelne Strings handeln oder um Listen.

Code: Alles auswählen

dict = {"Titel1": "Datum1", "Titel2": ["Datum2", "Datum3"], "Titel3": "Datum4"}
Meine Idee war nun über __contains__ die Titel zurückliefern lassen, also dass mir

Code: Alles auswählen

"Datum4" in x
ein "Titel3" zurückliefert. Ist das überhaupt möglich oder muss ich mir eine eigene Funktion dafür schreiben?
Mein bisheriger Versuch sah in etwa so aus:

Code: Alles auswählen

class x:
   def __init__(self):
      self.testdict = {"a": 1, "b": "blub"}
   def __contains__(self, x):
      if x in self.testdict.values():
         for i in self.testdict.items():
            a, b = i
            if b == x:
               return a
            else:
               return False
      else:
         return False
Lade ich nun die Klasse in den Interpreter und erstelle ein Objekt, kann ich fragen, ob "1 in x" und bekomme ein True, aber kein "a". Frage ich hingegen nach "blub", bekomme ich ein False.
Daher meine Frage: Geht das überhaupt so, wie ich es mir vorstelle, oder habe ich etwas bei __contains__ nicht beachtet?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Du suchst offenbar `__getitem__()`. `__contains__()` ist nicht dafür gedacht, etwas anderes als Wahrheitswerte zurückzuliefern. Es ist außerdem kein besonders gute Programmierstil, wenn eine Funktion/Methode gänzlich unterschiedliche Typen zurückliefert. Von sowas solltest du besser Abstand nehmen.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Dass der Weg über '__contains__()' der falsche ist, hat snafu ja bereits gesagt.
Dass Du bei 'blub' ein False bekommst, liegt daran, dass Du nicht über alle Werte in Deinem dictionary iterierst. Folgendes passiert:
Du holst Dir das erste key/value-Paar mit testdict.items().
Wenn value übereinstimmt, wird der key zurückgegeben.
Wenn nicht, wird False zurückgegeben.
In beiden Fällen wird die Schleife durch das 'return'-statement beendet und somit die weiteren key/value-Paare nicht mehr ausgewertet.
So würde es funktionieren:

Code: Alles auswählen

def search(d, search):
    matches = []
    for key, value in d.items():
        if str(search) in str(value):
            print '{0} ist im key {1} enthalten'.format(search, key)
            matches.append((key, value))
    return matches
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

snafu hat recht. Dein Programm sollte IMO eher so aussehen:

Code: Alles auswählen

class reversed_dict_lookup(object):
   def __init__(self, orig):
      self.orig = orig
   def __getitem__(self, value):
     return [key for key, values in self.orig.items() if value in values]


from collections import defaultdict

publishing_dates = defaultdict(list)

publishing_dates['Titel1'].append('Datum1')
publishing_dates['Titel2'].append('Datum2')
publishing_dates['Titel2'].append('Datum3')
publishing_dates['Titel3'].append('Datum4')
publishing_dates['Titel3'].append('Datum1')


rev = reversed_dict_lookup(publishing_dates)

print rev['Hallo']
print rev['Datum2']
print rev['Datum1']
Ergebnis:

Code: Alles auswählen

[]
['Titel2']
['Titel3', 'Titel1']
In specifications, Murphy's Law supersedes Ohm's.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Und wenn man's genau nimmt, sollte man auch eine Typprüfung einbauen, weil eine 1 kommt nicht in '1' vor und '1' hat nichts mit einer 1 zu tun...

Code: Alles auswählen

def search(d, search):
    matches = []
    for key, value in d.items():
        if isinstance(search, basestring) and \
            isinstance(value, basestring) or \
            isinstance(value, list) or \
            isinstance(value, tuple):
            match = lambda search, value: search in value
        else:
            match = lambda search, value: search == value
        if match(search, value):
            print '{0} ist im key {1} enthalten'.format(search, key)
            matches.append((key, value))
    return matches
mutetella


EDIT: Ähm, bezogen auf mein such-Beispiel, nicht auf pillmuncher's Lösung...
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@mutella: Ich glaube, du hast die Aufgabenstellung missverstanden. Der OP möchte quasi das Verhalten eines Wörterbuchs umkehren: Für einen gegebenen Wert soll der passende Schlüssel zurückgegeben werden. Was du gezeigt hast, ist ja eher ein Szenario, wo Teilbereiche eines Wertes mit der Suchanfrage übereinstimmen müssen. Das ist eine komplett andere Geschichte.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@snafu:
Das ist mir schon klar, ich habe mich auch nur auf
bearcat hat geschrieben:

Code: Alles auswählen

...
      if x in self.testdict.values():
         for i in self.testdict.items():
            a, b = i
            if b == x:
               return a
            else:
               return False
      else:
         return False
diesen Teil bezogen, den ich nicht so gelungen finde.

Ich wollte (und konnte... :wink: ) gar keine Antwort auf die eigentliche Frage liefern sondern nur diesen Teilbereich in bearcat's Codebeispiel verbessern.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
bearcat
User
Beiträge: 5
Registriert: Freitag 27. Mai 2011, 10:09

Ich bedanke mich bei Euch dreien (snafu, mutetella und pillmuncher) für die Hilfe, sie hat mich wirklich weiter gebracht. Das ist genau das, was ich eigentlich machen wollte. Vielen Dank.

Und ich sehe, dass ich noch einen weiten Weg vor mir habe, wenn ich mir die Kurzform von pillmuncher und die lambdas (die sich mir irgendwie immer noch verschließen) von mutetella anschaue.

Jedenfalls vielen Dank :)
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@bearcat:
lambdas sind sowas wie "Wegwerf-Funktionen" ohne eigenen Namen (anonym), die man für kleine Aufgaben, die man nur in einem bestimmten Kontext benötigt, verwendet.
Um bei meinem Beispiel zu bleiben, statt

Code: Alles auswählen

def search(d, search):
    ...
            match = lambda search, value: search in value
    ...
könnte man auch

Code: Alles auswählen

def search(d, search):
    ...
            match = iter_search
    ...

def iter_search(search, value):
    return search in value
lambdas sind allerdings in ihrem Umfang gegenüber "erwachsenen" Funktionen eingeschränkt, siehe hier.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
bearcat
User
Beiträge: 5
Registriert: Freitag 27. Mai 2011, 10:09

Also die Theorie dahinter war mir grob geläufig, also dass lambdas "syntaktischer Zucker" sind, wie das einmal so schön ausgedrückt wurde.
Ich hatte halt nur nie so recht verstanden, wo ich das nun genau anwenden kann, weil mir irgendwie immer die Beispiele fehlten. Aber ich denke, ich verstehe es jetzt. Danke dafür :)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

mutetella hat geschrieben:könnte man auch

Code: Alles auswählen

def search(d, search):
    ...
            match = iter_search
    ...

def iter_search(search, value):
    return search in value
Oder einfach operator.contains nutzen...
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Oder ganz einfach:

Code: Alles auswählen

match = search in value
Der Ausdruck verhält sich ja ähnlich wie ein Vergleich, wo man bekanntlich auch den boolschen Rückgabewert direkt an einen Namen binden kann.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

snafu hat geschrieben:Oder ganz einfach:
Was geht nur in meinem Kopf nicht vor, weshalb ich um die einfachsten Dinge immer einen so großen Bogen mache... :mrgreen:
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten