이 시리즈의 마지막 기사입니다. 이 구현은 이전 기사에서 설명한 보일러 플레이트 코드의 주요 단점을 해결하려고 합니다. 저는 이 구현을 동적 속성 클래스라고 부릅니다.
다음 클래스 다이어그램은 개발자가 이 기능을 사용하는 데 필요한 DynamicConfiguration 재사용 가능 클래스와 지원 데이터 구조를 보여줍니다. 자동 부팅 스트래핑, 누락된 섹션 생성 및 키 값을 포함하여 버전 2의 모든 기본 기능을 계속 제공합니다.
이 클래스를 사용하려는 애플리케이션의 전체 소스 코드를 제시하겠습니다. 이전 3개의 기사에서 논의한 이전 속성을 사용하고 있습니다.
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)
45~50번째 줄은 작성해야 하는 코드입니다. 기본적으로 파일 이름, 모듈 이름 및 구성 섹션을 전달하기만 하면 됩니다. 이 섹션 유형은 DynamicConfiguration 모듈에서 제공됩니다.
21-28행과 30-36행은 ValueDescriptions 사전입니다. KeyName은 속성이며 ValueDescription을 가리킵니다. 열거형을 유지하는 방법에 대한 표시가 이전 구현의 데코레이터에서 ValueDescription의 부울 속성으로 이동되었습니다.
DynamicConfiguration의 클래스 다이어그램을 자세히 살펴보면 두 개의 Python magic 메소드가 구현되어 있음을 알 수 있습니다. 그것은 __getattr__(self, name)__ 및 __setattr__(self, name, value)__ 메소드입니다.
다음은 __getattr__에 대한 코드입니다. 이는 버전 2에서 사용한 데코레이터와 매우 유사합니다. 핵심 작업은 보호된 메서드 _lookupKey()에 대한 14행 호출에서 발생합니다. 속성 검색을 시뮬레이션할 수 있도록 속성에 대한 전체 설명을 반환합니다.
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
다음은 __setattr__() 구현입니다. 22-27행의 열거형 지원과 30행의 write-through 기능을 확인하세요.
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()
속성 접근 및 수정은 버전 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=}')
위 스니펫은 다음과 같은 출력을 생성합니다.
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'
이 글의 소스코드는 여기에 있습니다. 지원 클래스 SingletonV3을 참조하세요.
구현 보기위 내용은 간편한 Python 구성 파일 버전 3을 향하여의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!