staging.inyokaproject.org

mit python3 yml parsen

Status: Ungelöst | Ubuntu-Version: Server 16.04 (Xenial Xerus)
Antworten |

BillMaier Team-Icon

Supporter

Anmeldungsdatum:
4. Dezember 2008

Beiträge: 6497

Hallo,

ich möchte gerne mit python3 eine Liste aus einem yml generieren. Bin allerdings völliger Anfänger und stehe etwas auf dem Schlauch.

Hier die yml:

version: '3.4'
services:
  service1:
    image: xzy
    ports:
      - "1053:1053/tcp"
    volumes:
      - /source1:/destination1
      - /source2:/destination2
  service2:
    volumes:
      - /source3:/destination3
      - ...

Die Liste soll enthalten:

- source1
- source2
- source3

Bisher gemacht:

1
2
3
4
5
6
7
8
9
>>> import yaml
>>> stream = open('test.yml', 'r')
>>> data = yaml.load(stream)
>>> services=data["services"]
>>> for a,b in services.items():
...     b["volumes"]
... 
['/source3:/destination3']
['/source1:/destination1', '/source2:/destination2']
  1. wie packe ich die Ergebnisse der Schleife in eine Variable? Brauche ich dazu eine extra Funktion?

  2. geht das einfacher?

Danke für alle geduldigen Tipps 😉

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

Meinst du sowas?

1
2
3
4
5
6
7
>>> import yaml
>>> with open('test.yml') as f:
...     data = yaml.safe_load(f)
... 
>>> source_list = [value["volumes"] for value in data["services"].values()]
>>> source_list
[['/source1:/destination1', '/source2:/destination2'], ['/source3:/destination3', '...']]

Oder willst du die verschachtelten Listen noch platt machen?

BillMaier Team-Icon

Supporter
(Themenstarter)

Anmeldungsdatum:
4. Dezember 2008

Beiträge: 6497

So, bin durch:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/usr/bin/env python3
import yaml

stream = open('test.yml', 'r')
data = yaml.load(stream)
services=data["services"]
volume_list = []
for a,b in services.items():
        volume_pairs=(b["volumes"])
        for volume_pair in volume_pairs:
                source,dest=volume_pair.split(":")
                print(source)

Verbesserungen?

//edited: Danke, seahawk1986, da hat sich wohl was überschnitten.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

Wenn man will geht es noch kürzer (und File-Deskriptoren würde ich immer schließen, wenn sie nicht mehr benötigt werden):

1
2
3
4
5
6
7
8
#!/usr/bin/env python3
import itertools
import yaml

with open('test.yml') as f:
    data = yaml.safe_load(f)
for entry in itertools.chain(*(service["volumes"] for service in data["services"].values()):
    print(entry.split(':')[0])

Ach ja: yaml.safe_load() sollte man zumindest immer dann nutzen, wenn die yaml-Datei nicht aus einer vertrauenswürdigen Quelle stammt, sonst öffnest du Tür und Tor für unfreiwillige Code-Ausführung - vgl. https://pyyaml.org/wiki/PyYAMLDocumentation

Warning: It is not safe to call yaml.load with any data received from an untrusted source! yaml.load is as powerful as pickle.load and so may call any Python function. Check the yaml.safe_load function though.

BillMaier Team-Icon

Supporter
(Themenstarter)

Anmeldungsdatum:
4. Dezember 2008

Beiträge: 6497

Ok, vielen Dank.

Kürzer war nicht unbedingt gewollt - eher "einfacher" im Sinne für einen Anfänger. Kürzer ist meist schlechter lesbar für Neulinge 😉

noisefloor Team-Icon

Anmeldungsdatum:
6. Juni 2006

Beiträge: 29567

Hallo,

Kürzer war nicht unbedingt gewollt - eher "einfacher" im Sinne für einen Anfänger.

Man muss zwar nicht unbedingt die List Comprehensions verwenden, aber stilistisch sollte es schon sauber programmiert sein. Dazu gehört u.a. geöffnete Dateien zu schließen. Bzw. viel besser ist, direkt den Context Manager über das with Statement zu nutzen.

1
for a,b in services.items():

ist in so fern schlecht, als das a) a und b nichtssagende Variablennamen sind - Variablennamen sollen aber aussagekräftig sein - und b) verwendest du a nirgends. Wenn du den Schlüssel nicht brauchst, wäre das von seahawk1986 gezeigte

1
for value in services.values()

besser. Wir machen dich noch zum Python-Guru, keine Sorge 😉

Gruß, noisefloor

Neral

Anmeldungsdatum:
3. Oktober 2007

Beiträge: 230

Man kann die Lösung von seahawk1986 noch ein wenig lesbarer gestalten, wenn man chain und das Sternchen-Argument zu chain.from_iterable zusammenfasst:

1
2
3
4
for entry in chain.from_iterable(
    service["volumes"] for service in data["services"].values()
):
    ...

Ansonsten würde ich mich noisefloor anschließen: Auch als Anfänger sollte man sauber programmieren (lernen). Außerdem sollte man (spätestens wenn man ein wenig fortgeschrittener ist), die wichtigen Teile der Standardbibliothek kennen. Vieles wird mit ein wenig Übung nicht nur kürzer, sondern auch einfacher lesbar. Wobei man sich in diesem Fall darüber streiten kann, ob chain.from_iterable wirklich besser ist als zwei verschachtelte for-Schleifen... 😉

BillMaier Team-Icon

Supporter
(Themenstarter)

Anmeldungsdatum:
4. Dezember 2008

Beiträge: 6497

noisefloor schrieb:

Man muss zwar nicht unbedingt die List Comprehensions verwenden, aber stilistisch sollte es schon sauber programmiert sein.

Deshalb frag ich ja hier. Mir ging es eben nicht um "möglichst wenig code", sondern schlicht und sauber.

Dazu gehört u.a. geöffnete Dateien zu schließen. Bzw. viel besser ist, direkt den Context Manager über das with Statement zu nutzen.

Also

with open('test.yml') as f:

muss dann nicht geschlossen werden?

1
for a,b in services.items():

ist in so fern schlecht, als das a) a und b nichtssagende Variablennamen sind

überzeugt.

1
for value in services.values()

gefällt mir auch besser. Kannte ich bisher nicht.

Wir machen dich noch zum Python-Guru, keine Sorge 😉

Alles klar, hab noch ein paar Jährchen bis zur Rente... 😀

Viele Grüße Euch und danke!

//edited: Ps: Python-Strukturen (dicts etc) kenne ich hauptsächlich von Ansible her, das macht es manchmal einfacher und manchmal schwieriger - weil ähnlich aber nicht gleich...

BillMaier Team-Icon

Supporter
(Themenstarter)

Anmeldungsdatum:
4. Dezember 2008

Beiträge: 6497

Neral schrieb:

Außerdem sollte man (spätestens wenn man ein wenig fortgeschrittener ist), die wichtigen Teile der Standardbibliothek kennen.

Kannst du mir da einen guten Link empfehlen, der mich nicht gleich erschlägt?

Neral

Anmeldungsdatum:
3. Oktober 2007

Beiträge: 230

@BillMaier: Einen Link speziell für die wichtigen Teile der Standardbibliothek hätte ich jetzt nicht (was wichtig ist, hängt auch vom Einsatzzweck ab), aber das offizielle Tutorial bietet einen guten Einstieg. Die Standardbibliothek ist in der Dokumentation dokumentiert. Vielleicht hilft es, einfach mal den Index zu überfliegen und sich die Module genauer anzugucken, die zu deinem Einsatzzweck passen. Das könnte dich dann allerdings erschlagen. 😉 Ansonsten ist es schwer, da was konkretes zu sagen, ohne deinen Einsatzzweck und Kenntnisstand zu kennen.

Das with-Statement benutzt Context-Manager: Wenn der with-Block verlassen wird, wird die __exit__-Methode des Context-Managers aufgerufen. Bei Dateiobjekten wird damit die Datei geschlossen.

PEP 8 ist sicherlich auch einen Blick wert. Und import this.

noisefloor Team-Icon

Anmeldungsdatum:
6. Juni 2006

Beiträge: 29567

Hallo,

@BillMaier: sehr lesenswert ist auch: Python Module of the Week 🇬🇧. Da werden nur Module aus der Standardbibliothek praktisch erklärt. Die Artikel kann man in beliebiger Reihenfolge lesen bzw. alles weg lassen, was nicht von Interesse ist.

Gruß, noisefloor

BillMaier Team-Icon

Supporter
(Themenstarter)

Anmeldungsdatum:
4. Dezember 2008

Beiträge: 6497

Moin, jetzt hatte ich zum ersten Mal den Fall, dass der Key "volumes" nicht vor kam. Meine Lösung:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def get_volumes(file):
    with open(file) as f:
      data = yaml.safe_load(f)
    services=data["services"]
    source_list=[]
    for value in services.values():
      try:
        volume_pairs=(value['volumes'])
        for volume_pair in volume_pairs:
          source=volume_pair.split(":")[0]
          source_list.append(source)
      except:
        pass
    return source_list

Gebt mir saures...

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4735

@BillMaier: Aber gerne doch: Du verschluckst da nicht nur den Fall das 'volumes' nicht vorkommt, sondern auch alle anderen Ausnahmen die zwischen try und except auftreten können. Inklusive Fehler an die Du nicht gedacht hast. Und das sind im Grunde fast alle Fehler, denn wenn man dran gedacht hätte, wären sie in der Regel nicht im Code. 😉

Niemals ein nacktes except:, sondern immer nur die Ausnahmen behandeln die man auch tatsächlich erwartet und bei denen das was im except-Block steht, auch eine sinnvolle Behandlung ist. Dabei den except-Block möglichst klein halten, denn je mehr Code darin enthalten ist, um so grösser ist die Chance das eine Ausnahme nicht nur an der einen Stelle auftreten kann an die man gerade gedacht hat, sondern auch an anderer Stelle, inklusive irgendwo in Code der aufgerufen wird. Und da ist die Behandlung, in diesem Falle nichts tun, vielleicht nicht mehr sinnvoll. Vor allem bekommt man so etwas dann gar nicht mit.

In diesem Fall würde ich gar keine Ausnahme verwenden, sondern dafür sorgen das man auf jeden Fall einen gültigen, passenden Wert bekommt, auch wenn 'volumes' nicht vorkommt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def get_volumes(filename):
    with open(filename) as yaml_file:
        data = yaml.safe_load(yaml_file)
    services = data['services']
    source_list = []
    for value in services.values():
        volume_pairs = value.get('volumes', [])
        for volume_pair in volume_pairs:
            source = volume_pair.split(':')[0]
            source_list.append(source)
    return source_list

Edit: Etwas kompakter ausgedrückt:

1
2
3
4
5
6
7
8
def get_volumes(filename):
    with open(filename) as yaml_file:
        data = yaml.safe_load(yaml_file)
    source_list = list()
    for value in data['services'].values():
        volume_pairs = value.get('volumes', [])
        source_list.extend(pair.split(':', 1)[0] for pair in volume_pairs)
    return source_list

Allerdings sollte man auch an das hier denken:

1
2
In [8]: 'kein doppelpunkt'.split(':')[0]
Out[8]: 'kein doppelpunkt'

Falls es wichtig ist diesen Fall zu berücksichtigen, sollte man das tun. ☺

BillMaier Team-Icon

Supporter
(Themenstarter)

Anmeldungsdatum:
4. Dezember 2008

Beiträge: 6497

Soweit so logisch.

Aber: woher kommt dieser Ausdruck?

Marc_BlackJack_Rintsch schrieb:

1
        volume_pairs = value.get('volumes', [])

also das get? Kann ich das "immer" verwenden statt dem bisher vorgeschlagenen:

1
for value in services.values()

}}} Allerdings sollte man auch an das hier denken:

1
2
In [8]: 'kein doppelpunkt'.split(':')[0]
Out[8]: 'kein doppelpunkt'

Falls es wichtig ist diesen Fall zu berücksichtigen, sollte man das tun. ☺

Ist hier nicht wichtig. Wenn da kein Doppelpunkt existiert ist die Syntax des zu prüfenden Files sowieso kaputt. Welcher Fehler da kommt ist recht egal...

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

Schau dir mal die Dokumentation zum Typ dict an: https://docs.python.org/3/library/stdtypes.html#dict - da sind die ganzen Methoden beschrieben, um auf die Keys und Values zuzugreifen.

Antworten |