INI-Datei einlesen, trotz fehlenden Sektionen/Optionen

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.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Ich habe nochmals über die Problematik des Zusammenfassens der Konfigurationen nachgedacht und herausgekommen ist folgendes Programm. Dieses sollte nun soweit funktionieren, wie in meiner ersten Antwort aufgezeichnet. Darüber hinaus verwendet die get_configuration-Funktion nunmehr einen Dateiobjekt, so spielt es besser mit Sirius3s Beispiel zusammen:

Code: Alles auswählen

#!/usr/bin/env python3
import configparser
import itertools


def get_configuration(file_object):  # -> dict
    parser = configparser.ConfigParser()
    parser.read_file(file_object)
    return {section: dict(parser.items(section))
            for section
            in parser.sections()}

            
def merge_dicts(a, b):  # -> dict
    """Merges two dicts prefering the values of the second one."""
    return {k: v for k, v in itertools.chain(a.items(), b.items())}

    
def merge_configurations(a, b):  # -> dict
    # 1. Dafür Sorge trage, dass das Ergebnis die Sektionen beider
    #    Konfigurationen enthält. Hierzu bilde ich zunächst ein set
    #    (=Menge) über die Sektionsnamen beider Konfigurationen.
    section_names = set(itertools.chain(a.keys(), b.keys()))
    
    # 2. Dictionary erzeugen und die Inhalte der Sektionen
    #    zusammenfassen (merge_dicts). Hierbei werden die Inhalte des
    #    zweiten Konfiguration bevorzugt.
    return {section_name: merge_dicts(a.get(section_name, {}),
                                      b.get(section_name, {}))
            for section_name
            in section_names}
    

def main():
    with open('default.ini') as default_config_file:
        default_configuration = get_configuration(default_config_file)
        
    with open('user.ini') as user_config_file:
        user_configuration = get_configuration(user_config_file)
        
    print(merge_configurations(default_configuration, user_configuration))

    
if __name__ == '__main__':
    main()
Hier bietet sich ein Blick in die Dokumentation zu itertools und zu LC/DC im Allgemeinen an.

PS: Ich nutze ausschließlich Python 3.5., configparser.ConfigParser.read_file im obigen Beispiel müsste ggf. durch readfp ersetzt werden. readfp ist ab Python 3.2 veraltet.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
BlackJack

@bwbg: Die „dict comprehension“ finde ich hier ungünstig. Wenn man mit den Schlüssel/Wert-Paaren nichts macht, kann man auch einfacher und kürzer einfach `dict()` verwenden: ``return dict(itertools.chain(a.items(), b.items()))``. Alternativ ginge auch noch ``result = dict(a); result.update(b); return result``.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Für die Funktion merge_dicts stimme ich Dir sogar zu ... 8)
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

die einzeilige Variante wäre `result = dict(a, **b)`.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Damit ich hinterher komme, bedeuten diese beiden Sternchen (**), dass das Wörterbuch in einem Rutsch aktualisiert wird? Denn ich kenne diese Sternchen nur im Kontext von Funktionen (*args, **kwargs), um beliebe Anzahl von Argumenten oder Positionensargumente übergeben zu können.
BlackJack

@Sophus: Die ``**`` bedeuten hier genau das gleiche wie bei jedem anderen Funktions- oder Methodenaufruf.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Sirius3 hat geschrieben:die einzeilige Variante wäre `result = dict(a, **b)`.
Das Problem hier ist jedoch, dass eine (unvollständige) Sektion aus der user.ini die entsprechende aus der default.ini ersetzt. merge_configurations ließe sich in einem Ausdruck schreiben, aber nicht wirklich lesbar in einer Zeile :wink:

Sophus hat nun vom Baum der Erkenntnis gekostet: Jede neue Antwort bringt neue Fragen.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Sirius3 hat geschrieben:die einzeilige Variante wäre `result = dict(a, **b)`.
Hm.

Code: Alles auswählen

>>> a = {1: 2}
>>> b = {1: 3, 2: 3}
>>> c = {2: 4, 3: 4}
>>> dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings
>>> {**a, **b}
{1: 3, 2: 3}
>>> {**a, **b, **c}
{1: 3, 2: 4, 3: 4}
Die tolle neue Syntax gibt es übrigens seit 3.5 und ist in PEP 448 beschrieben.
BlackJack

@DasIch: Bei der Konfiguration kann man allerdings davon ausgehen, dass die Schlüssel Zeichenketten sind. :-)
Antworten