Dies ist der letzte Artikel dieser Serie. Mit dieser Implementierung soll der Hauptnachteil des Boiler-Plate-Codes behoben werden, den ich im vorherigen Artikel beschrieben habe. Ich nenne diese Implementierung eine dynamische Eigenschaftsklasse.
Das folgende Klassendiagramm zeigt die wiederverwendbare Klasse DynamicConfiguration und die unterstützenden Datenstrukturen, die ein Entwickler zur Nutzung dieser Funktionalität benötigt. Es bietet weiterhin alle Grundfunktionen von Version 2, einschließlich Auto-Boot-Strapping, Erstellung fehlender Abschnitte und Schlüsselwerte.
Ich werde den vollständigen Quellcode für eine Anwendung präsentieren, die diese Klasse verwenden möchte. Ich verwende die vorherigen Eigenschaften, die wir in den vorherigen drei Artikeln besprochen haben.
from codeallybasic.DynamicConfiguration import DynamicConfiguration from codeallybasic.DynamicConfiguration import KeyName from codeallybasic.DynamicConfiguration import SectionName from codeallybasic.DynamicConfiguration import Sections from codeallybasic.DynamicConfiguration import ValueDescription from codeallybasic.DynamicConfiguration import ValueDescriptions from codeallybasic.SecureConversions import SecureConversions from codeallybasic.SingletonV3 import SingletonV3 from ByteSizedPython.ImpostorEnumByName import ImpostorEnumByName from ByteSizedPython.PhoneyEnumByValue import PhoneyEnumByValue LOGGER_NAME: str = 'Tutorial' BASE_FILE_NAME: str = 'config.ini' MODULE_NAME: str = 'version3properties' DEFAULT_PHONEY_ENUM_BY_VALUE: PhoneyEnumByValue = PhoneyEnumByValue.FakeBrenda DEFAULT_IMPOSTOR_ENUM_BY_NAME: ImpostorEnumByName = ImpostorEnumByName.High GENERAL_PROPERTIES: ValueDescriptions = ValueDescriptions( { KeyName('debug'): ValueDescription(defaultValue='False', deserializer=SecureConversions.secureBoolean), KeyName('logLevel'): ValueDescription(defaultValue='Info'), KeyName('phoneyEnumByValue'): ValueDescription(defaultValue=DEFAULT_PHONEY_ENUM_BY_VALUE.value, enumUseValue=True), KeyName('impostorEnumByName'): ValueDescription(defaultValue=DEFAULT_IMPOSTOR_ENUM_BY_NAME.name, enumUseName=True), } ) DATABASE_PROPERTIES: ValueDescriptions = ValueDescriptions( { KeyName('dbName'): ValueDescription(defaultValue='dbName'), KeyName('dbHost'): ValueDescription(defaultValue='localhost'), KeyName('dbPort'): ValueDescription(defaultValue='5342', deserializer=SecureConversions.secureInteger), } ) CONFIGURATION_SECTIONS: Sections = Sections( { SectionName('General'): GENERAL_PROPERTIES, SectionName('Database'): DATABASE_PROPERTIES, } ) class ConfigurationPropertiesVersion3(DynamicConfiguration, metaclass=SingletonV3): def __init__(self): self._logger: Logger = getLogger(LOGGER_NAME) super().__init__(baseFileName=BASE_FILE_NAME, moduleName=MODULE_NAME, sections=CONFIGURATION_SECTIONS)
Zeilen 45–50 sind der Code, den Sie schreiben müssen. Im Wesentlichen stellen Sie lediglich sicher, dass Sie den Dateinamen, den Modulnamen und die Konfigurationsabschnitte übergeben. Dieser Abschnittstyp stammt aus dem DynamicConfiguration-Modul.
Zeilen 21–28 und 30–36 sind ValueDescriptions-Wörterbücher. Der KeyName ist die Eigenschaft und verweist auf eine ValueDescription. Beachten Sie, dass der Indikator zum Beibehalten einer Aufzählung vom Dekorator der vorherigen Implementierung in ein boolesches Attribut in einer ValueDescription verschoben wurde.
Wenn Sie sich das Klassendiagramm für DynamicConfiguration genau ansehen, werden Sie feststellen, dass es zwei Python-magic-Methoden implementiert. Es handelt sich um die Methoden __getattr__(self, name)__ und __setattr__(self, name, value)__.
Das Folgende ist der Code für __getattr__. Dies ähnelt stark dem Dekorator, den wir in Version 2 verwendet haben. Die Schlüsselarbeit erfolgt beim Aufruf der geschützten Methode _lookupKey() in Zeile 14. Es gibt eine vollständige Beschreibung des Attributs zurück, damit wir den Attributabruf simulieren können.
def __getattr__(self, attrName: str) -> Any: """ Does the work of retrieving the named attribute from the configuration parser Args: attrName: Returns: The correctly typed value """ self._logger.info(f'{attrName}') configParser: ConfigParser = self._configParser result: LookupResult = self._lookupKey(searchKeyName=KeyName(attrName)) valueDescription: ValueDescription = result.keyDescription valueStr: str = configParser.get(result.sectionName, attrName) if valueDescription.deserializer is not None: value: Any = valueDescription.deserializer(valueStr) else: value = valueStr return value
Das Folgende ist die Implementierung von__setattr__(). Beachten Sie die Unterstützung für Aufzählungen in den Zeilen 22–27 und die Funktion Durchschreiben in Zeile 30.
def __setattr__(self, key: str, value: Any): """ Do the work of writing this back to the configuration/settings/preferences file Ignores protected and private variables uses by this class Does a "write through" to the backing configuration file (.ini) Args: key: The property name value: Its new value """ if key.startswith(PROTECTED_PROPERTY_INDICATOR) or key.startswith(PRIVATE_PROPERTY_INDICATOR): super(DynamicConfiguration, self).__setattr__(key, value) else: self._logger.debug(f'Writing `{key}` with `{value}` to configuration file') configParser: ConfigParser = self._configParser result: LookupResult = self._lookupKey(searchKeyName=KeyName(key)) valueDescription: ValueDescription = result.keyDescription if valueDescription.enumUseValue is True: valueStr: str = value.value configParser.set(result.sectionName, key, valueStr) elif valueDescription.enumUseName is True: configParser.set(result.sectionName, key, value.name) else: configParser.set(result.sectionName, key, str(value)) self.saveConfiguration()
Der Zugriff auf und die Änderung von Eigenschaften erfolgt genau wie in Version 2.
basicConfig(level=INFO) config: ConfigurationPropertiesVersion2 = ConfigurationPropertiesVersion2() logger: Logger = getLogger(LOGGER_NAME) logger.info(f'{config.debug=}') logger.info(f'{config.logLevel=}') logger.info(f'{config.phoneyEnumByValue=}') logger.info(f'{config.impostorEnumByName=}') logger.info('Database Properties Follow') logger.info(f'{config.dbName=}') logger.info(f'{config.dbHost=}') logger.info(f'{config.dbPort=}') logger.info('Mutate Enumeration Properties') config.phoneyEnumByValue = PhoneyEnumByValue.TheWanderer logger.info(f'{config.phoneyEnumByValue=}') config.impostorEnumByName = ImpostorEnumByName.Low logger.info(f'{config.impostorEnumByName=}')
Das obige Snippet erzeugt die folgende Ausgabe.
INFO:Tutorial:config.debug='False' INFO:Tutorial:config.logLevel='Info' INFO:Tutorial:config.phoneyEnumByValue=<PhoneyEnumByValue.FakeBrenda: 'Faker Extraordinaire'> INFO:Tutorial:config.impostorEnumByName='High' INFO:Tutorial:Database Properties Follow INFO:Tutorial:config.dbName='example_db' INFO:Tutorial:config.dbHost='localhost' INFO:Tutorial:config.dbPort=5432 INFO:Tutorial:Mutate Enumeration Properties INFO:Tutorial:config.phoneyEnumByValue=<PhoneyEnumByValue.TheWanderer: 'The Wanderer'> INFO:Tutorial:config.impostorEnumByName='Low'
Der Quellcode für diesen Artikel ist hier. Siehe die Support-Klasse SingletonV3. Sehen Sie sich die Implementierung von
anDas obige ist der detaillierte Inhalt vonAuf dem Weg zu mühelosen Python-Konfigurationsdateien Version 3. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!