Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

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.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Hallo

Ich würde gerne ein zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen.

Beispielhaft mal folgender Code:

Code: Alles auswählen

VEHICLES = {"car": {
                "VW Polo": {"power": 75,
                            "color": "blue"},
                "BMW 5 series": {"power": 400,
                                 "color": "black"}},
            "truck": {
                "MAN TGL": {"power": 200,
                            "color": "red"},
                "Mercedes-Benz Actros": {"power": 500,
                                         "color": "white"}
            }}

mileage_list = {}


def create_mileage_list():
    mileage_list.clear()
    for vehicle_type in VEHICLES.keys():
        for vehicle_model in VEHICLES[vehicle_type]:
            mileage_list[vehicle_type] = {vehicle_model: "unknown"} # ?


create_mileage_list()
Die `mileage_list` enthält nach dem Aufruf der Funktion `create_mileage_list()` folgende Einträge:

Code: Alles auswählen

{'car': {'BMW 5 series': 'unknown'}, 'truck': {'Mercedes-Benz Actros': 'unknown'}}
Anstatt 4 Einträgen für die 4 Fahrzeuge in `VEHICLES` sind nur Einträge für 2 Fahrzeuge vorhanden.

An der Codestelle mit dem Fragezeichen im Kommentar vermute ich das Problem. Ich vermute, dass das innere Dictionary überschrieben wird anstatt die Key/Value-Kombination zum bestehenden inneren Dictionary hinzuzufügen. Eine Lösung ist mir bis dato noch nicht eingefallen.

Gruß
Atalanttore
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Funktionen sind dazu da, Dinge zu kapseln. Du benutzt aber globale Variablen, die diese Funktion mit der Umgebung verknüpft. Statt dessen solltest Du richtige Funktionen schreiben, die ein neues Wörterbuch zurückgeben.
Warum iterierst Du einmal über die Keys, in dem Du explizit `keys` aufrufst, und einmal direkt?
Du überschreibst in jedem Lauf der inneren Schleife den Wert zum Schlüssel ›vehicle_type‹
Eine Funktion, die create_xy_list heißt und ein Wörterbuch erzeugt, ist sehr verwirrend.

Wenn ich Dich richtig verstehe, willst Du zu jedem Schlüssel in der zweiten Ebene den Wert "unkown" zuweisen?

Code: Alles auswählen

def create_mileage():
    return {
        vehicle_type: dict.fromkeys(vehicle_models, "unknown")
        for vehicle_type, vehicle_models in VEHICLES.items()
    }
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@Sirius3: Wahrscheinlich wird die Funktion zu einer Methode innerhalb einer Klasse und das Wörterbuch `VEHICLES` eine Klassenvariable.

Die Methode `fromkeys()` habe ich bisher noch nie benutzt. Das zurückgegebene Wörterbuch der Funktion `create_mileage()` ist nun genauso wie ich es ursprünglich mit der Funktion `create_mileage_list()` erreichen wollte. Danke für deinen Code.

Gruß
Atalanttore
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Als zusätzliche Schwierigkeit würde ich die Funktion `create_mileage()` gerne um eine Möglichkeit zum Ausschluss bestimmter Fahrzeuge erweitern.

Bei meinen verschiedenen Ansätzen diese Erweiterung umzusetzen, bin ich immer wieder auf folgende Fehlermeldung gestoßen:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/ata/test/nested_dict/main.py", line 22, in <module>
    print(create_mileage({'car': 'VW Polo', 'truck': "Mercedes-Benz Actros"}))
  File "/home/ata/test/nested_dict/main.py", line 17, in create_mileage
    for vehicle_type, vehicle_model in VEHICLES.items()
  File "/home/ata/test/nested_dict/main.py", line 18, in <dictcomp>
    if {vehicle_type: vehicle_model} not in exclude
TypeError: unhashable type: 'dict'

Process finished with exit code 1
Das übergebene Wörterbuch mit den auszuschließenden Fahrzeugen scheint Probleme zu machen.

Aktueller Code:

Code: Alles auswählen

VEHICLES = {"car": {
                "VW Polo": {"power": 75,
                            "color": "blue"},
                "BMW 5 series": {"power": 400,
                                 "color": "black"}},
            "truck": {
                "MAN TGL": {"power": 200,
                            "color": "red"},
                "Mercedes-Benz Actros": {"power": 500,
                                         "color": "white"}
            }}


def create_mileage(exclude=None):
    return {
        vehicle_type: dict.fromkeys(vehicle_model, "unknown")
        for vehicle_type, vehicle_model in VEHICLES.items()
        if {vehicle_type: vehicle_model} not in exclude
    }


print(create_mileage({'car': 'VW Polo', 'truck': "Mercedes-Benz Actros"}))
Was muss man am Code ändern, damit es funktioniert?

Gruß
Atalanttore
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Das Wörterbuch, das Du da als exclude übergibst, funktioniert nicht mehr, wenn Du mehrere Cars ausschließen willst. Wie würde es dann aussehen? Und wie würde eine Bedingung aussehen, wenn sowohl Typ als auch Modell übereinstimmen müssen?
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@Sirius3: Wahrscheinlich müsste die Bedingung anders aussehen als die im Code unten. Der Code verursacht mittlerweile zwar keinen Fehler mehr, aber der Ausschluss bestimmter Fahrzeuge funktioniert nach wie vor nicht.

Code:

Code: Alles auswählen

VEHICLES = {"car": {
                "VW Polo": {"power": 75,
                            "color": "blue"},
                "BMW 5 series": {"power": 400,
                                 "color": "black"}},
            "truck": {
                "MAN TGL": {"power": 200,
                            "color": "red"},
                "Mercedes-Benz Actros": {"power": 500,
                                         "color": "white"}
            }}


def create_mileage(exclude=None):
    result = {}
    for vehicle_type, vehicle_model in VEHICLES.items():
        if {vehicle_type: vehicle_model} not in exclude.items():
            result[vehicle_type] = dict.fromkeys(vehicle_model, "unknown")
    return result


print(create_mileage({"car": "VW Polo", "truck": "Mercedes-Benz Actros"}))
Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Das sieht nach Programmieren durch raten aus. Die ``if``-Bedinung ist immer wahr, denn ein Wörterbuch ist niemals in Tupeln aus Schlüssel und Wert enthalten.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__blackjack__: Wo im Code entstehen diese Tupeln?

Code:

Code: Alles auswählen

VEHICLES = {"car": {
                "VW Polo": {"power": 75,
                            "color": "blue"},
                "BMW 5 series": {"power": 400,
                                 "color": "black"}},
            "truck": {
                "MAN TGL": {"power": 200,
                            "color": "red"},
                "Mercedes-Benz Actros": {"power": 500,
                                         "color": "white"}
            }}


def create_mileage(exclude=None):
    result = dict()
    print(type(result))
    for vehicle_type, vehicle_model in VEHICLES.items():
        if {vehicle_type: vehicle_model} not in exclude.items():
            print(type({vehicle_type: vehicle_model}))
            result[vehicle_type] = dict.fromkeys(vehicle_model, "unknown")
    return result


print(type({"car": "VW Polo", "truck": "Mercedes-Benz Actros"}))
print(create_mileage({"car": "VW Polo", "truck": "Mercedes-Benz Actros"}))
Ausgabe:

Code: Alles auswählen

<class 'dict'>
<class 'dict'>
<class 'dict'>
<class 'dict'>
{'car': {'VW Polo': 'unknown', 'BMW 5 series': 'unknown'}, 'truck': {'MAN TGL': 'unknown', 'Mercedes-Benz Actros': 'unknown'}}
Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ob da wirklich Tupel in `dict_items`-Objekten stecken und ob ``in`` darauf nicht doch ein klein bisschen effizienter implementiert ist weiss ich nicht, aber es muss sich halt so verhalten als wenn, damit ``in`` und drüber iterieren konsistente Ergebnisse liefern. In Python 2 hat die `items()`-Methode auch noch tatsächlich eine Liste mit Tupeln geliefert.

Code: Alles auswählen

In [29]: exclude                                                                
Out[29]: {'car': 'VW Polo', 'truck': 'Mercedes-Benz Actros'}

In [30]: exclude.items()                                                        
Out[30]: dict_items([('car', 'VW Polo'), ('truck', 'Mercedes-Benz Actros')])

In [31]: ("car", "VW Polo") in exclude.items()                                  
Out[31]: True

In [32]: {"car": "VW Polo"} in exclude.items()  # Never, ever...                
Out[32]: False

In [33]: next(iter(exclude.items()))                                            
Out[33]: ('car', 'VW Polo')

In [34]: type(next(iter(exclude.items())))                                      
Out[34]: tuple
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__blackjack__: Ich habe die if-Bedingung in der Funktion `create_mileage()` nun ein weiteres Mal angepasst (= geraten), aber es werden nach wie vor nicht die gewünschten Werte ausgeschlossen.

Funktion `create_mileage()`:

Code: Alles auswählen

def create_mileage(exclude=None):
    result = dict()
    for vehicle_type, vehicle_model in VEHICLES.items():
        if (vehicle_type, vehicle_model) not in exclude.items():
            result[vehicle_type] = dict.fromkeys(vehicle_model, "unknown")
    return result
Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Natürlich nicht. Es wird aber langsam echt ein bisschen langweilig Dir beim raten zuzuschauen. Genau wie Du Dir die rechte Seite der Testoperation selber mal hättest anschauen könne, könntest Du das mit der linken Seite auch mal machen. Das ist spätestens dann ziemlich offensichtlich warum der Test bei den gegebenen Daten immer fehlschlagen muss.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__blackjack__: Einigermaßen offensichtlich ist das Problem im Code schon, aber beheben kann ich es nicht.

Code:

Code: Alles auswählen

VEHICLES = {"car": {
                "VW Polo": {"power": 75,
                            "color": "blue"},
                "BMW 5 series": {"power": 400,
                                 "color": "black"}},
            "truck": {
                "MAN TGL": {"power": 200,
                            "color": "red"},
                "Mercedes-Benz Actros": {"power": 500,
                                         "color": "white"}
            }}


def create_mileage(exclude=None):
    result = dict()
    for vehicle_type, vehicle_model in VEHICLES.items():
        links = (vehicle_type, vehicle_model.keys())
        rechts = exclude.items()
        print("Links:", links)
        print("Rechts:", rechts)
        if links not in rechts:
            result[vehicle_type] = dict.fromkeys(vehicle_model, "unknown")
    return result


print(create_mileage({"car": "VW Polo", "truck": "Mercedes-Benz Actros"}))
Ausgabe:

Code: Alles auswählen

Links: ('car', dict_keys(['VW Polo', 'BMW 5 series']))
Rechts: dict_items([('car', 'VW Polo'), ('truck', 'Mercedes-Benz Actros')])
Links: ('truck', dict_keys(['MAN TGL', 'Mercedes-Benz Actros']))
Rechts: dict_items([('car', 'VW Polo'), ('truck', 'Mercedes-Benz Actros')])
{'car': {'VW Polo': 'unknown', 'BMW 5 series': 'unknown'}, 'truck': {'MAN TGL': 'unknown', 'Mercedes-Benz Actros': 'unknown'}}
Gruß
Atalanttore
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Beschreib doch mal, wie Du als Mensch heruasfinden würdest, wie die linke zur rechten Seite passt.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@Sirius3: Als Mensch würde ich so vorgehen:

Wenn ein Schlüssel im übergebenen Dictionary mit einem Schlüssel (`vehicle_type`) des äußeren Dictionary (`VEHICLES`) übereinstimmt
und
der Wert des Schlüssels im übergebenen Dictionary mit dem zugehörigen Schlüssel (`vehicle_model`) im inneren Dictionary, welches den Wert für das äußere Dictionary (`VEHICLES`) darstellt, übereinstimmt,
dann
soll dafür kein neuer Eintrag im Dictionery `result` der Funktion `create_mileage()` erstellt werden.

Gruß
Atalanttore
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Das kann man ja schon fast wörtlich nach Python übersetzen. Vor allem der Teil mit „Wenn es einen Schlüssel gibt, …”.
Besser umzusetzen ist allerdings die umgekehrte Bedingung. Wann soll es einen Eintrag geben?
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Ich habe die Funktion `create_mileage()` nun etwas anders umgesetzt.

Code:

Code: Alles auswählen

VEHICLES = {"car": {
                "VW Polo": {"power": 75,
                            "color": "blue"},
                "BMW 5 series": {"power": 400,
                                 "color": "black"}},
            "truck": {
                "MAN TGL": {"power": 200,
                            "color": "red"},
                "Mercedes-Benz Actros": {"power": 500,
                                         "color": "white"}},
            "bus": {
                "Mercedes-Benz Citaro": {"power": 300,
                                         "color": "yellow"},
                "Solaris Vacanza 13": {"power": 400,
                                       "color": "green"}}
            }


def create_mileage(exclude=None):
    result = dict()
    for vehicle_type, vehicle_models in VEHICLES.items():
        for model in vehicle_models:
            print("Exclude:", exclude.get(vehicle_type))
            print("Model:", model)
            if exclude.get(vehicle_type) != model:
                result[vehicle_type] = dict.fromkeys(vehicle_models, "unknown")
    return result


print(create_mileage({"car": "VW Polo", "truck": "Mercedes-Benz Actros"}))
Leider wird nach wie vor immer eine vollständige Liste ausgegeben und die Einträge in `exclude` nicht beachtet.


Gruß
Atalanttore
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Nach mehreren intensiven Stunden mit dem Debugger funktioniert der Code nun endlich so wie er soll.

Code:

Code: Alles auswählen

VEHICLES = {"car": {
                "VW Polo": {"power": 75,
                            "color": "blue"},
                "BMW 5 series": {"power": 400,
                                 "color": "black"}},
            "truck": {
                "MAN TGL": {"power": 200,
                            "color": "red"},
                "Mercedes-Benz Actros": {"power": 500,
                                         "color": "white"}},
            "bus": {
                "Mercedes-Benz Citaro": {"power": 300,
                                         "color": "yellow"},
                "Solaris Vacanza 13": {"power": 400,
                                       "color": "green"}}
            }


def create_mileage(exclude=None):
    result = dict()

    for vehicle_type, vehicle_models in VEHICLES.items():
        inner = dict()
        for model in vehicle_models:
            if exclude is not None and exclude.get(vehicle_type) and model in exclude.get(vehicle_type):
                continue
            else:
                inner[model] = "unknown"
                result[vehicle_type] = inner
    return result


print(create_mileage({"car": ("VW Polo", "BMW 5 series")}))
print("="*50)
print(create_mileage({"car": "VW Polo"}))
print("="*50)
print(create_mileage({"truck": "Mercedes-Benz Actros", "bus": "Solaris Vacanza 13"}))
print("="*50)
print(create_mileage({"car": "VW Polo", "bus": "Mercedes-Benz Citaro"}))
print("="*50)
print(create_mileage({"bus": ("Solaris Vacanza 13", "Mercedes-Benz Citaro")}))
print("="*50)
print(create_mileage())
print("="*50)
Die Tests verliefen bei mir erfolgreich.

Ausgabe:

Code: Alles auswählen

{'truck': {'MAN TGL': 'unknown', 'Mercedes-Benz Actros': 'unknown'}, 'bus': {'Mercedes-Benz Citaro': 'unknown', 'Solaris Vacanza 13': 'unknown'}}
==================================================
{'car': {'BMW 5 series': 'unknown'}, 'truck': {'MAN TGL': 'unknown', 'Mercedes-Benz Actros': 'unknown'}, 'bus': {'Mercedes-Benz Citaro': 'unknown', 'Solaris Vacanza 13': 'unknown'}}
==================================================
{'car': {'VW Polo': 'unknown', 'BMW 5 series': 'unknown'}, 'truck': {'MAN TGL': 'unknown'}, 'bus': {'Mercedes-Benz Citaro': 'unknown'}}
==================================================
{'car': {'BMW 5 series': 'unknown'}, 'truck': {'MAN TGL': 'unknown', 'Mercedes-Benz Actros': 'unknown'}, 'bus': {'Solaris Vacanza 13': 'unknown'}}
==================================================
{'car': {'VW Polo': 'unknown', 'BMW 5 series': 'unknown'}, 'truck': {'MAN TGL': 'unknown', 'Mercedes-Benz Actros': 'unknown'}}
==================================================
{'car': {'VW Polo': 'unknown', 'BMW 5 series': 'unknown'}, 'truck': {'MAN TGL': 'unknown', 'Mercedes-Benz Actros': 'unknown'}, 'bus': {'Mercedes-Benz Citaro': 'unknown', 'Solaris Vacanza 13': 'unknown'}}
==================================================
Wie findet ihr den Code?

Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Das ``continue`` ist unschön. Wenn man die Bedingung von dem ``if`` umdreht, wird das auch ganz einfach überflüssig.

Der Test beim ``if`` ist ziemlich lang. Wenn man immer dafür sorgt das `exclude` ein Wörterbuch ist, braucht man nicht immer wieder prüfen ob es ein Wörterbuch ist oder nicht.

Dann steht ``exclude.get(vehicle_type)`` zweimal in der Bedingung. Auch hier kann man einfach dafür sorgen das dort *immer* eine Liste zurückgegeben wird — wenn es den Fahrzeugtyp nicht gibt, dann halt eine leere Liste.

Und die letzte, innerste Zeile ``result[vehicle_type] = inner`` hat bis auf beim allerersten Aufruf keinen wirklichen Effekt mehr, das heisst eigentlich müsste man die nur *einmal* aufrufen. Das wird aber aus dem Code nicht sofort ersichtlich. Ich würde das hinter die Schleife ziehen:

Code: Alles auswählen

def create_mileage(exclude=None):
    if exclude is None:
        exclude = dict()
    result = dict()
    for vehicle_type, vehicle_models in VEHICLES.items():
        inner = dict()
        for model in vehicle_models:
            if model not in exclude.get(vehicle_type, []):
                inner[model] = "unknown"
        if inner:
            result[vehicle_type] = inner
    return result
Allerdings hat die Funktion einen Fehler, beziehungsweise machst Du einen beim Aufruf. Ruf die mal mit ``{"car": "VW Polo XL"}`` auf. Ich denke nicht, dass das erwartete Ergebnis bringt. Ein Schlüssel zu einfachem Code ohne viele Sonderfälle sind regelmässige Datenstrukturen ohne viele Sonderfälle. `exclude` sollte immer die gleiche Struktur haben und nicht mal Zeichenketten die auf Zeichenketten und mal Zeichenketten die auf Tupel mit Zeichenketten abegebildet werden. Semantisch würde ich da auch keine Tupel sondern Listen nehmen. Oder gleich `set`\s.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__blackjack__: Vielen Dank für deine Verbesserungsvorschläge und Erklärungen. Mit Datenstrukturen beschäftige ich mich gerade.
__blackjack__ hat geschrieben: Samstag 9. November 2019, 20:13 @Atalanttore: Das ``continue`` ist unschön. Wenn man die Bedingung von dem ``if`` umdreht, wird das auch ganz einfach überflüssig.
Die lange `if`-Bedingung umzudrehen ist mir nicht fehlerfrei gelungen. Nach einiger Zeit war mir das `continue` dann gut genug. :wink:

__blackjack__ hat geschrieben: Samstag 9. November 2019, 20:13 Der Test beim ``if`` ist ziemlich lang. Wenn man immer dafür sorgt das `exclude` ein Wörterbuch ist, braucht man nicht immer wieder prüfen ob es ein Wörterbuch ist oder nicht.
Ganz ähnliche Konstrukte habe ich schon in Code von anderen Entwicklern gesehen.
1. Ist die Zuweisung eines neuen Wertes, wenn ein Schlüsselwortparameter den Wert `None` hat, eine Konvention bei Python?

__blackjack__ hat geschrieben: Samstag 9. November 2019, 20:13 Dann steht ``exclude.get(vehicle_type)`` zweimal in der Bedingung. Auch hier kann man einfach dafür sorgen das dort *immer* eine Liste zurückgegeben wird — wenn es den Fahrzeugtyp nicht gibt, dann halt eine leere Liste.
Mit der Rückgabe eines leeren `set`s oder eines leeren `tuple`s funktioniert der Code bei mir auch noch.
2. Hat die Rückgabe einer leeren Liste einen bestimmten Grund?

__blackjack__ hat geschrieben: Samstag 9. November 2019, 20:13 Und die letzte, innerste Zeile ``result[vehicle_type] = inner`` hat bis auf beim allerersten Aufruf keinen wirklichen Effekt mehr, das heisst eigentlich müsste man die nur *einmal* aufrufen. Das wird aber aus dem Code nicht sofort ersichtlich. Ich würde das hinter die Schleife ziehen:

Code: Alles auswählen

def create_mileage(exclude=None):
    if exclude is None:
        exclude = dict()
    result = dict()
    for vehicle_type, vehicle_models in VEHICLES.items():
        inner = dict()
        for model in vehicle_models:
            if model not in exclude.get(vehicle_type, []):
                inner[model] = "unknown"
        if inner:
            result[vehicle_type] = inner
    return result
Allerdings hat die Funktion einen Fehler, beziehungsweise machst Du einen beim Aufruf. Ruf die mal mit ``{"car": "VW Polo XL"}`` auf. Ich denke nicht, dass das erwartete Ergebnis bringt. Ein Schlüssel zu einfachem Code ohne viele Sonderfälle sind regelmässige Datenstrukturen ohne viele Sonderfälle. `exclude` sollte immer die gleiche Struktur haben und nicht mal Zeichenketten die auf Zeichenketten und mal Zeichenketten die auf Tupel mit Zeichenketten abegebildet werden. Semantisch würde ich da auch keine Tupel sondern Listen nehmen. Oder gleich `set`\s.
Der `in`-Operator prüft bei Strings offensichtlich nur, ob der erste String im zweiten String enthalten ist. Ich habe nun alle Strings der Fahrzeugmodelle im übergebenen Wörterbuch `exclude` in `set`s verpackt. Das Fahrzeugmodell muss nun den exakt gleichen Namen haben, damit es von der Funktion `create_mileage()` aussortiert wird.

Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13112
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Es ist eigentlich egal wie lang eine Bedingung ist: Klammern drum und ein ``not`` davor funktioniert *immer* für eine Negation. Also Ausgangeslage:

Code: Alles auswählen

    if (
        exclude is not None
        and exclude.get(vehicle_type)
        and model in exclude.get(vehicle_type)
    ):
        ...
Zum negieren einfach ein ``not`` davor:

Code: Alles auswählen

    if not (
        exclude is not None
        and exclude.get(vehicle_type)
        and model in exclude.get(vehicle_type)
    ):
        ...
Wenn man das ``not`` da jetzt rein ziehen will, geht das nach einem festen und eigentlich relativ einfachen Schema: Alle Teilbedingungen negieren und ``and``/``or`` austauschen:

Code: Alles auswählen

    if (
        exclude is None
        or not exclude.get(vehicle_type)
        or model not in exclude.get(vehicle_type)
    ):
        ...
Ja, das mit dem `None` ist ein Idiom in Python. Das Problem bei veränderbaren Objekten als Defaultwerten ist, dass die nur ein einziges mal erstellt werden, nämlich wenn die ``def``-Anweisung ausgeführt wird um die Funktion oder Methode zu definieren. Wenn man da nicht aufpasst, handelt man sich leicht einen verstecketen, globalen Wert ein. Dummes Beispiel:

Code: Alles auswählen

In [8]: def f(xs=[]): 
   ...:     xs.append(42) 
   ...:     print(xs) 
   ...:                                                                         

In [9]: f()                                                                     
[42]

In [10]: f()                                                                    
[42, 42]

In [11]: f()                                                                    
[42, 42, 42]

In [12]: f()                                                                    
[42, 42, 42, 42]
Das wäre im Fall von `create_mileage()` kein Problem weil auf das Wörterbuch nur lesend zugegriffen wird, aber nur um niemanden zu verwirren, habe ich da trotzdem `None` genommen. Es gibt nämlich auch Fälle wo man das absichtlich machen kann. Beispielsweise um einen Cache für Funktionsergebnisse anzulegen. Ist natürlich ein Hack und eine Lösung mit einem Dekorator ist schöner.

Ich habe da eine Liste gewählt weil es im Argument wenn es vorhanden ist eine Liste sein sollte. Oder ein `set`. Aber kein Tupel. Funktionieren tun alle drei. Tupel halt nicht weil man als Leser bei Tupeln erwartet, dass die Bedeutung der Elemente von der Position abhängt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten