Heim >Backend-Entwicklung >Python-Tutorial >Auf dem Weg zu mühelosen Python-Konfigurationsdateien Version 1

Auf dem Weg zu mühelosen Python-Konfigurationsdateien Version 1

Susan Sarandon
Susan SarandonOriginal
2024-12-02 14:55:19735Durchsuche

Einführung

Wie im vorherigen Artikel erwähnt, ist die vereinfachte Version voller Probleme, einschließlich Skalierbarkeit, Wartbarkeit und Erweiterbarkeit.

Eine einfache Erweiterung von Version Ø besteht darin, zu versuchen, die Python-Konfigurationsdetails hinter einer Eigenschaftsklasse zu verbergen. Das bedeutet, dass eine Pseudodatenklasse implementiert wird, die eine Reihe von Eigenschaften verfügbar macht, die es einem Entwickler ermöglichen, einfach Eigenschaftsaufrufe set und get auszuführen, um Eigenschaftswerte abzurufen und beizubehalten.

Aus Sicht des Betreuers sollte diese Implementierung die folgenden Funktionen unterstützen.

  1. Automatische Erstellung von Konfigurationsabschnitten zulassen, wenn diese fehlen
  2. Automatische Erstellung von Eigenschaftswerten zulassen, wenn diese fehlen
  3. Die Eigenschaften sollten sowohl als Durchlese- als auch als Durchschreibfunktion implementiert werden.
  4. Um die Startkosten für das oben Genannte zu vermeiden, sollte diese Klasse ein Singleton sein, da eine Anwendung diese Klasse in der gesamten Anwendung instanziiert.

Klassenvertretung

Das folgende UML-Klassendiagramm beschreibt eine Klasse, die die Anforderungen in der Einleitung erfüllen würde. Die ConfiguratonProperties-Klasse erfüllt die Anforderungen 1 und 2 mit den geschützten Methoden .createMissingSections und .createMissingKeys

Towards Effortless Python Configuration Files Version 1

Erstellen Sie Implementierungen

Erstellen Sie fehlende Abschnitte

Der folgende Code zeigt die Implementierung. Beachten Sie, dass für zusätzliche Abschnitte Codeaktualisierungen dieser Methode erforderlich sind

SECTION_GENERAL:  str = 'General'
SECTION_DATABASE: str = 'Database'

def _createMissingSections(self):
    """
    Create missing sections.  Add additional calls for
    each defined section
    """
    self._createMissingSection(SECTION_GENERAL)
    self._createMissingSection(SECTION_DATABASE)

Der fehlende Abschnittscode lautet wie folgt.

def _createMissingSection(self, sectionName: str):
    """
    Only gets created if it is missing
    Args:
        sectionName: The potential section to create
    """
    hasSection: bool = self._configParser.has_section(sectionName)
    self.logger.info(f'hasSection: {hasSection} - {sectionName}')
    if hasSection is False:
        self._configParser.add_section(sectionName)

Erstellen Sie fehlende Schlüssel

Der folgende Code zeigt die Implementierung. Beachten Sie erneut, dass der Entwickler beim Hinzufügen eines zusätzlichen Abschnitts eine zusätzliche Schleife für den neuen Abschnitt hinzufügen muss.

GENERAL_PREFERENCES: Dict[str, str] = {
    'debug':    'False',
    'logLevel': 'Info'
}

DATABASE_PREFERENCES: Dict[str, str] = {
    'dbName': 'example_db',
    'dbHost': 'localhost',
    'dbPort': '5432'
}

def _createMissingKeys(self):
    """
    Create missing keys and their values.  Add additional calls for
    each defined section.
    """
    for keyName, keyValue in GENERAL_PREFERENCES.items():
        self._createMissingKey(sectionName=SECTION_GENERAL, keyName=keyName, defaultValue=keyValue)
    for keyName, keyValue in DATABASE_PREFERENCES.items():
        self._createMissingKey(sectionName=SECTION_DATABASE, keyName=keyName, defaultValue=keyValue)

Der fehlende Schlüsselcode lautet wie folgt. Beachten Sie, dass alle fehlenden Schlüssel sofort beibehalten werden.

def _createMissingKey(self, sectionName: str, keyName: str, defaultValue: str):
    """
    Only gets created if it is missing.  The configuration file is updated
    immediately for each missing key and its value

    Args:
        sectionName:   The section name where the key resides
        keyName:       The key name
        defaultValue:  Itsß value
    """
    if self._configParser.has_option(sectionName, keyName) is False:
        self._configParser.set(sectionName, keyName, defaultValue)
        self._saveConfiguration()

Klasseneigenschaften

Beispielimplementierungen für Anforderung 3 folgen.

String-Eigenschaften

Beachten Sie, dass beim Festlegen einer Eigenschaft ein Durchschreiben in die Konfigurationsdatei erfolgt, indem die Eigenschaft festgelegt und sofort beibehalten wird. Leseeigenschaften werden effektiv durchgelesen, da wir festgelegte Eigenschaften sofort schreiben.

@property
def dbName(self) -> str:
    return self._configParser.get(SECTION_DATABASE, 'dbName')

@dbName.setter
def dbName(self, newValue: str):
    self._configParser.set(SECTION_DATABASE, 'dbName', newValue)
    self._saveConfiguration()

Ganzzahlige Eigenschaften

Integer-Eigenschaften verwenden die .getint-Methode, um den Wert abzurufen. Beim Festlegen der Eigenschaft muss der Entwickler sie manuell in eine Zeichenfolge konvertieren.

@property
def dbPort(self) -> int:
    return self._configParser.getint(SECTION_DATABASE, 'dbPort')

@dbPort.setter
def dbPort(self, newValue: int):
    self._configParser.set(SECTION_DATABASE, 'dbPort', str(newValue))
    self._saveConfiguration()

Boolesche Eigenschaften

Boolesche Eigenschaften verwenden die .getboolean-Methode, um ihren Wert abzurufen. Beim Festlegen der Eigenschaft muss der Entwickler sie manuell in eine Zeichenfolge konvertieren.

SECTION_GENERAL:  str = 'General'
SECTION_DATABASE: str = 'Database'

def _createMissingSections(self):
    """
    Create missing sections.  Add additional calls for
    each defined section
    """
    self._createMissingSection(SECTION_GENERAL)
    self._createMissingSection(SECTION_DATABASE)

Aufzählungseigenschaften

Auf Aufzählungseigenschaften werde ich in diesem Artikel nicht eingehen. Es gibt zwei Möglichkeiten, sie beizubehalten: nach ihrem Namen oder nach ihrem Wert. Jeder Mechanismus erfordert eine etwas andere Methode, um die Werte wieder in einen Aufzählungstyp zu deserialisieren.

Auf Eigenschaften zugreifen und diese ändern

Der folgende Codeausschnitt zeigt, wie auf die Eigenschaften zugegriffen und diese geändert werden.

def _createMissingSection(self, sectionName: str):
    """
    Only gets created if it is missing
    Args:
        sectionName: The potential section to create
    """
    hasSection: bool = self._configParser.has_section(sectionName)
    self.logger.info(f'hasSection: {hasSection} - {sectionName}')
    if hasSection is False:
        self._configParser.add_section(sectionName)

Das obige Snippet erzeugt die folgende Ausgabe

GENERAL_PREFERENCES: Dict[str, str] = {
    'debug':    'False',
    'logLevel': 'Info'
}

DATABASE_PREFERENCES: Dict[str, str] = {
    'dbName': 'example_db',
    'dbHost': 'localhost',
    'dbPort': '5432'
}

def _createMissingKeys(self):
    """
    Create missing keys and their values.  Add additional calls for
    each defined section.
    """
    for keyName, keyValue in GENERAL_PREFERENCES.items():
        self._createMissingKey(sectionName=SECTION_GENERAL, keyName=keyName, defaultValue=keyValue)
    for keyName, keyValue in DATABASE_PREFERENCES.items():
        self._createMissingKey(sectionName=SECTION_DATABASE, keyName=keyName, defaultValue=keyValue)

Abschluss

Der Quellcode für diesen Artikel ist hier. Die Support-Klasse SingletonV3 ist hier

Das Ergebnis der Implementierung hat mich als Konsumenten des Codes zunächst zufrieden gestellt. Ich konnte typisierte Eigenschaften abrufen und festlegen. Als Betreuer des Codes musste ich jedoch die Codedatenstrukturen und Codeschleifen jedes Mal manuell aktualisieren, wenn ich neue Abschnitte und neue Eigenschaften hinzufügte. Darüber hinaus habe ich dadurch nur einen Mechanismus/ein Muster erhalten, das ich immer dann verwenden kann, wenn ich in verschiedenen Anwendungen neue Konfigurationseigenschaften benötige.

Vorteile

  • Einfacher typsicherer Zugriff auf Anwendungseigenschaften
  • Der Aufruf des Singletons in verschiedenen Teilen meiner Anwendung ermöglichte einen konsistenten und zuverlässigen Zugriff auf Eigenschaften, unabhängig davon, welcher Teil der Anwendung Werte geändert hat

Nachteile

  • Aktualisierungen zum Hinzufügen neuer Eigenschaften waren mühsam
  • Viel Boiler-Plate-Code
  • Keine Wiederverwendbarkeit über verschiedene Anwendungen hinweg. Im Wesentlichen hatte ich nur eine Vorlage

Siehe meinen nächsten Beitrag, der eine alternative Implementierung dokumentiert, um die von mir aufgeführten Nachteile zu beheben und gleichzeitig die Vorteile beizubehalten.

Das obige ist der detaillierte Inhalt vonAuf dem Weg zu mühelosen Python-Konfigurationsdateien Version 1. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn