Zuweisen von 2 elementiger Liste schlägt im Generator fehl

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
ManOki
User
Beiträge: 5
Registriert: Mittwoch 21. November 2018, 11:54

Hallo,

ich habe Strings wie im folgenden Beispiel l1 und l2, also kommaseparierte Zuweisungen von Key-Value-Paaren. Diese wollte ich mit einem Einzeiler aufsplitten und in eine Liste von Dicts (oder wahlweise nur dict) speichern.

Code: Alles auswählen

l1 = 'k1 : v1'
l2 = 'k1:v2:3, k3:v4'

k, v = l1.split(':', 1)
print(k, v)
# k1   v1

l = l1

print([a.split(':', 1) for a in l.split(',')])
# [['k1 ', ' v1']]

print([(k, v) for a in l.split(',') for k, v, *_ in a.split(':', 1)])
# [('k', '1'), (' ', 'v')]

print([(k, v) for a in l.split(',') for k, v, in a.split(':', 1)])
# ValueError: too many values to unpack (expected 2
Wie zu sehen, wird mit split(':', 1) eine 2 elementige Liste erzeugt, die dann direkt zwei Variablen zugewiesen werden kann.
Das ganze funktioniert aber nicht im Generator-Pattern darunter, da dort bei der Zuweisung von split(':', 1) die Liste in einen String konvertiert wird und dann die Länge vom String bei der Zuweisung entscheidend ist.

Wieso ist das so, was müsste ich machen, damit es doch funktioniert?

Viele Grüße
ManOki
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

Das split liefert Dir keine Liste von Daten, sondern ein Key-Value-Paar. Das Unpacking in der for-Schleife zerlegt dann die Strings (key und value) nacheinander in die einzelnen Zeichen. 'k1' -> 'k', '1'
Was willst Du denn mit dem nächsten Schritt erreichen? `[a.split(':', 1) for a in l.split(',')]` liefert doch schon die Zeile in key-value-pairs getrennt.
ManOki
User
Beiträge: 5
Registriert: Mittwoch 21. November 2018, 11:54

Vorweg, ich nutze Python 3, sollte aber in dem Beispiel keinen Unterschied machen:

Laut der Dokumentation Built-in Types und einem einen print(type(...)) liefert mir split eine Liste, kein Key-Value-Paar (Tupel, nehme ich an). Und mit dem zweiten Parameter maxsplit=1 stelle ich sicher, dass nur eine 2-elementige Liste erstellt wird.

Ich möchte ein dict erstellen, z.B. {'k1':'v1'} für String l1 oder {'k1':'v2:3', 'k3':'v4'} für String l2. Alternativ ginge auch eine Liste von dicts, also [{'k1':'v2:3'}, {'k3':'v4'}] für l2

Deswegen trenne ich zunächst den String nach Kommata in einzelne Zuweisungen "a", die einzelnen Zuweisungen dann jeweils nach Doppelpunkten in Key und Value.


Hier sieht man ganz gut, dass die beiden split hintereinander eine Liste von Listen erstellt:

Code: Alles auswählen

print([a.split(':', 1) for a in l.split(',')])
# [['k1 ', ' v1']]
Vielen Dank vorab
ManOki
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

Mit Key-Value-Paar meinte ich eine Liste mit zwei Elementen, die Du dann in einer weiteren for-Schleife durchgehst und die einzelnen Elemente per Unpacking in ihre Buchstaben aufteilst.

Du hast ja jetzt eine Liste von Listen. dict-liefert Dir darauf ein Wörterbuch.
ManOki
User
Beiträge: 5
Registriert: Mittwoch 21. November 2018, 11:54

Danke schonmal für den Tip, dass dict auch mit Liste von 2-elementigen Listen umgehen kann. Bleibt noch ein Problem: ich würde gerne Key und Value strippen, also die Leerzeichen entfernen.
Müsste ich also doch wieder alles auspacken, strippen und wieder einpacken, womit der Einzeiler mit meinen Künsten nicht gerade sehr kurz/übersichtlich bleiben würde.

Außerdem würde ich gerne verstehen, warum ohne das Generator-Pattern die Zuweisung funktioniert, aber eben nicht im Generator-Pattern. Ist das ggf. ein Bug?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

ManOki hat geschrieben: Mittwoch 21. November 2018, 14:54 ich würde gerne Key und Value strippen, also die Leerzeichen entfernen.
Da bietet es sich an, auf die von split erzeugten Strings jeweils die Funktion strip anzuwenden. Die Funktion map hilft dabei.

Code: Alles auswählen

print([list(map(str.strip, element.split(':', 1))) for element in l2.split(',')])
ManOki hat geschrieben: Mittwoch 21. November 2018, 14:54 Außerdem würde ich gerne verstehen, warum ohne das Generator-Pattern die Zuweisung funktioniert, aber eben nicht im Generator-Pattern. Ist das ggf. ein Bug?
Nein, das ist ein Verständnisproblem. Wenn du Daten in einer Liste möchtest, dann musst du auch eine Liste erzeugen. split macht das für dich. Mit der von dir verwendeten zweiten for-Schleife lieferst du aber die Einzelelemente der Liste zurück.

Bei dir ist das so etwas:

Code: Alles auswählen

for element in l2.split(','):
    print('element: ' + element)
    for data in element.split(':', 1):
        print('data: ' + data)

element: k1:v2:3
data: k1
data: v2:3
element:  k3:v4
data:  k3
data: v4
Du möchtest aber das hier:

Code: Alles auswählen

for element in l2.split(','):
    print('element: ' + element)
    print(element.split(':', 1))
	
element: k1:v2:3
['k1', 'v2:3']
element:  k3:v4
[' k3', 'v4']
ManOki
User
Beiträge: 5
Registriert: Mittwoch 21. November 2018, 11:54

Danke für die umfangreiche Antwort!

Jetzt verstehe ich das Problem bzw. kenne die (nicht python-konforme) Lösung: Ein Assignment statt dem "in", was aber nicht erlaubt ist.

Code: Alles auswählen

print([(k, v) for a in l.split(',') for k, v = a.split(':', 1)])
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

Was da in den zwei for-Schleifen passiert, hab ich Dir schon zweimal erklärt; am besten untersuchst Du Dir das selbst, indem Du die List-Comprehension in for-Schleifen umwandelst und jeden Schritt ausgibst.
Die Lösung hat Dir dann /me auf dem Silbertablett präsentiert. Aber statt dessen verfällst Du in Raten, was beim Programmieren selten eine gute Idee ist.

Ich würde hier ja einfach einen regulären Ausdruck benutzen:

Code: Alles auswählen

re.findall('\s*(.*?)\s*:\s*(.*?)\s*(?:,|$)', line)
ManOki
User
Beiträge: 5
Registriert: Mittwoch 21. November 2018, 11:54

Sorry, das hätte ich vielleicht explizit dazu schreiben sollen: Ich verstehe jetzt (bzw. schon vorher) komplett mein Problem und meine fehlerhafte Denkweise. Der Beispiel-Code von /me hat mir sehr weitergeholfen und mein Problem habe ich bereits gelöst. Trotzdem auch vielen Dank nochmal an dich, Sirius3, dass du trotzdem noch weiterhelfen wolltest!
Antworten