ホームページ >バックエンド開発 >Python チュートリアル >楽な Python 設定ファイル バージョン 3 に向けて

楽な Python 設定ファイル バージョン 3 に向けて

Barbara Streisand
Barbara Streisandオリジナル
2024-12-20 14:26:16352ブラウズ

導入

これがこのシリーズの最後の記事です。 この実装は、前の記事で説明した定型コードの主な欠点を修正することを目的としています。 私はこの実装を動的プロパティ クラスと呼びます。

クラスの代表

次のクラス図は、DynamicConfiguration 再利用可能クラスと、開発者がこの機能を使用するために必要なサポート データ構造を示しています。 自動ブートストラップ、欠落セクションとキー値の作成など、バージョン 2 のすべての基本機能が引き続き提供されます。

Towards Effortless Python Configuration Files Version 3

開発者コードの説明

このクラスを使用するアプリケーションの完全なソース コードを提示します。 以前の 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 のクラス図をよく見ると、2 つの Python magic メソッドが実装されていることがわかります。 それは、__getattr__(self, name)__ メソッドと __setattr__(self, name, value)__ メソッドです。

  • __getattr__(self, name)__ クラスのコンシューマーが存在しない属性にアクセスしようとしたときの動作を開発者が定義できるようにします。
  • __setattr__(self, name, value)__ 開発者が属性への割り当ての動作を定義できるようにします。

以下は __getattr__ のコードです。 これは、バージョン 2 で使用したデコレータに非常によく似ています。重要な作業は、14 行目の保護されたメソッド _lookupKey() の呼び出しで行われます。 属性の完全な説明が返されるので、属性の取得をシミュレートできます。

    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 行目の ライトスルー 機能に注目してください。

    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 を参照してください。

の実装を参照してください。

利点

  • アプリケーションのプロパティに簡単にタイプセーフにアクセスできます
  • さまざまな実装で再利用可能な親クラス
  • 新しいセクションと構成キーを追加するデータ構造主導のコード
  • プロパティの定型コードはありません

短所

  • 実際のプロパティが実装されていないため、それらに対する IDE サポートは得られません
  • さらに、キーの検索方法のため、異なるセクションの異なるキーに同じ名前を付けることはできません

以上が楽な Python 設定ファイル バージョン 3 に向けての詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。