Seite 2 von 2

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Sonntag 10. November 2019, 16:31
von Atalanttore
@__blackjack__: Danke für die gut verständlichen Erklärungen.
__blackjack__ hat geschrieben: Samstag 9. November 2019, 21:39 @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)
    ):
        ...
Ich habe zwar alle Bedingungen negiert, aber `and` habe ich nicht durch `or` ersetzt. Das war dann wohl das Problem.

__blackjack__ hat geschrieben: Samstag 9. November 2019, 21:39 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.
Hängt bei einer Liste die Bedeutung der Elemente nicht auch von ihrer Position ab?

Bisher habe ich die Eigenschaften der verschiedenen Datenstrukturen so verstanden:

Liste: Änderbar, Elemente geordnet in Position, Elemente können mehrfach vorkommen
Tupel: Nicht änderbar, Elemente geordnet in Position, Elemente können mehrfach vorkommen
Set: Änderbar, Elemente an beliebiger Position, Elemente können nicht mehrfach vorkommen

Gruß
Atalanttore

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Sonntag 10. November 2019, 17:24
von __blackjack__
@Atalanttore: Bei einer Liste sollte die Bedeutung eines Elements nicht von der Position abhängen. Alle Elemente sollten dort die gleiche Bedeutung haben. Anders beim Tupel. Wenn dort jedes Element die gleiche Bedeutung hat, ist das ein Zeichen das man eigentlich eine Liste verwenden wollte. Ausnahmen sind Fälle wo man ein Tupel wegen dessen Eigenschaften braucht, weil man es beispielsweise als Schlüssel in einem Wörterbuch verwenden will, oder in einem `set` speichern.

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Sonntag 10. November 2019, 17:46
von Atalanttore
@__blackjack__: Danke für die Erklärung.

Gruß
Atalanttore

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Sonntag 10. November 2019, 19:48
von Atalanttore
Der Funktion `create_mileage()` habe ich mit freundlicher Unterstützung meiner IDE PyCharm nun auch einen docstring verpasst.

Code:

Code: Alles auswählen

def create_mileage(exclude=None):
    """
    Create a new mileage dictionary with default “unknown” value.

    :param exclude: Type and model of vehicles to be excluded from the dictionary.
    :return: dict[str, dict[str, set[str]]]
    """
    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
Zur Beschreibung des Rückgabewertes habe ich einfach die jeweiligen Datentypen von außen nach innen angegeben. Macht man das so?

Gruß
Atalanttore

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Sonntag 10. November 2019, 20:40
von __blackjack__
@Atalanttore: Nein bei ``:return:`` beschreibt man als Text was da zurückgegeben wird. Der Rückgabe*typ* ist ``:rtype:``. Wobei Du da anscheinend auch gar nicht die Rückgabe beschreibst, sondern den Typ von `exclude`-Argument.

Wobei ich denke dass so eine Typbeschreibung nicht wirklich sinnvoll ist. Das ist zu kompliziert, denn der Benutzer will ja nicht unbedingt oder nicht unbedingt nur wissen wie der verschachtelte Typ aussieht, sondern was die einzelnen Werte *bedeuten*. Das geht bei so verschachtelten Grunddatentypen IMHO am besten über Beispiele.

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Montag 11. November 2019, 21:41
von Atalanttore
@__blackjack__: Wäre folgender docstring besser?

Code: Alles auswählen

"""
Create a new mileage dictionary with default “unknown” value.

:param exclude: Type and model of vehicles to be excluded from the dictionary.
:return: {“type of the vehicle“: {“model of the vehicle“: “unknown“}}
"""
Gruß
Atalanttore

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Dienstag 12. November 2019, 06:20
von snafu
Ist es nicht besser, wenn "excludes" eine Liste ist? Dann könnte man mit Sets arbeiten:

Code: Alles auswählen

def make_mileage(vehicles, excludes=None):
    if excludes is None:
        excludes = {}
    mileage = {}
    for kind, models in vehicles.items():
        if kind in excludes:
            models = set(models) - set(excludes[kind])
        if models:
            mileage[kind] = dict.fromkeys(models, "unknown")
    return mileage

def main():
    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"}}
               }
    excludes = {"car": ["VW Polo"], "truck": ["Mercedes-Benz Actros"]}
    print(make_mileage(vehicles, excludes))

if __name__ == '__main__':
    main()

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Dienstag 12. November 2019, 07:10
von snafu
Nochmal etwas umgeformt und mit der neuen Python 3.8 Syntax:

Code: Alles auswählen

def make_mileage(vehicles, excludes=None):
    if excludes is None:
        excludes = {}
    mileage = {}
    for kind, models in vehicles.items():
        if models := models.keys() - excludes.get(kind, []):
            mileage[kind] = dict.fromkeys(models, "unknown")
    return mileage
Lässt sich auch als Dict Comprehension ausdrücken, aber ich finde das dann nicht mehr lesbar. Selbst jetzt ist es ja schon grenzwertig, weil da relativ viel auf kleinstem Raum passiert.

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Dienstag 12. November 2019, 08:27
von Sirius3
Wieder etwas gelernt: ein dictkey-Objekt verhält sich so, wie man es sich von einem Set wünschen würde:

Code: Alles auswählen

>>> {7} - [7]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'set' and 'list'
>>> {7:4}.keys() - [7]
set()
Was noch verwirrender ist, auch die umgedrehten Operanden funktionieren:

Code: Alles auswählen

>>> [7] - {7:4}.keys()
set()
Die Operatoren sind alle in erweiterter Form implementiert, die Methoden fehlen aber bis auf isdisjoint.
Wer hat sich da welche Gedanken gemacht?

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Dienstag 12. November 2019, 12:10
von snafu
Och ja, tupleobject.c und listobject.c sind sich auch sehr ähnlich und trotzdem gibt es keine gemeinsame Quelldatei als Basis. Das muss man nicht verstehen und auch nicht, warum die Schlüssel teilweise mächtiger als ein Set sind... 🤷🏿‍♂️

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Dienstag 12. November 2019, 13:13
von Sirius3
Die Dokumentation ist in dieser Beziehung aber auch recht sparsam:
For set-like views, all of the operations defined for the abstract base class collections.abc.Set are available (for example, ==, <, or ^).
An anderer Stelle für Sets:
difference(*others)
set - other - ...
Return a new set with elements in the set that are not in the others.
werden zwar `differenz` und ´-` in einem Satz genannt, ersteres funktioniert aber mit irgendeinem Iterable, zweiteres nur mit Set-Like.

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Dienstag 12. November 2019, 22:58
von Atalanttore
@Sirius3 + snafu: Danke für die alternativen Lösungsvorschläge und den Code.

Gruß
Atalanttore

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Dienstag 12. November 2019, 23:18
von Atalanttore
__blackjack__ hat geschrieben: Samstag 9. November 2019, 21:39

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.
In welchen Situationen macht dieses verwirrende Verhalten von Python in der Praxis einen Sinn?

Gruß
Atalanttore

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Mittwoch 13. November 2019, 00:01
von __deets__
Keine Zeit mit der Erstellung von Objekten zu verschwenden wenn es eigentlich nicht nötig ist. Wenn es anders wäre, hätte jede Funktion eine Zeile mehr, mit letztlich unbekannten Laufzeit-Folgen.

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Mittwoch 13. November 2019, 06:02
von snafu
Man kann natürlich auch bei meinem Beispiel einfach ein leeres Wörterbuch in der Signatur für "excludes" nehmen. Dann spart man sich anfangs den Test samt Ersetzung und somit zwei Zeilen.

Re: Zweidimensionales Dictionary mit Daten aus einem dreidimensionalen Dictionary erstellen

Verfasst: Mittwoch 13. November 2019, 07:46
von snafu
Hier nun doch mal als Dict Comprehension:

Code: Alles auswählen

def make_mileages(vehicles, excludes={}):
    return {
        kind: dict.fromkeys(remaining, "unknown")
        for kind, models in vehicles.items()
        if (remaining := models.keys() - excludes.get(kind, []))
    }