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

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

Linda Hamilton
Linda Hamiltonオリジナル
2024-11-30 02:39:11266ブラウズ

導入

前の記事では、アプリケーションの構成値を管理するためのシミュレートされたプロパティ データクラスを作成することができました。 ただし、これは基本的に単なるテンプレートであり、用途ごとに再実装する必要がありました。 私の仕事のバージョン 2 では、再利用可能なクラスを実装することに成功しました。

  • プロパティ定義に 1 組のデコレータが必要です。
  • さらに、実装には、各構成セクションのセクション エントリを含むセクション データ型が必要です。
  • 各セクションには ConfigurationNameValue エントリのリストが必要です

クラスの代表

次のクラス図は、基本的な再利用可能なクラスと、開発者がこの機能を使用するために必要なデータ構造を示しています。

Towards Effortless Python Configuration Files Version 2

サブクラス構成プロパティ

開発者は、次のように構成プロパティをサブクラス化することでプロセスを開始します。

BASE_FILE_NAME: str = 'config.ini'
MODULE_NAME:    str = 'version2properties'

class ConfigurationPropertiesVersion2(ConfigurationProperties, metaclass=SingletonV3):

    def __init__(self):
        self.logger: Logger = getLogger(LOGGER_NAME)

        super().__init__(baseFileName=BASE_FILE_NAME, moduleName=MODULE_NAME, sections=CONFIGURATION_SECTIONS)

        self._configParser.optionxform = self._toStr    # type: ignore

        self._loadConfiguration()

super への呼び出しにより、構成ファイルへの完全修飾パスが作成されます。 このコードは、XDG Base Directory 仕様のサブセットに従っています。 このコードは、最初に XDG_CONFIG_HOME を試行し、次に HOME を試行し、両方とも失敗した場合は最後に現在のディレクトリを使用します。 13 行目で、開発者は保護されたメソッドを呼び出して、構成パーサーを準備します。 さらに、その呼び出しは

  • 設定ファイルが存在することを確認し、存在しない場合は空のファイルを作成します
  • 不足しているセクションを作成します
  • セクションに欠落しているキーを作成します

以前の機能により、多くの再利用可能なコードとブートストラップ構成ファイルが生成されます。 さらに、アプリケーション コード ベース全体でクラスをインスタンス化する際の作成コストを軽減するために、クラスをシングルトンにします。

セクションの定義

スーパーコールのパラメータセクションに注目してください。 このセクションの定義は次のようになります:

from codeallybasic.ConfigurationProperties import Sections

CONFIGURATION_SECTIONS: Sections = Sections(
    {
        SectionName('General'):  SECTION_GENERAL,
        SectionName('Database'): SECTION_DATABASE,
    }
)

上記はキーがセクション名、値がセクションである辞書です。

セクションの定義

セクションは、ConfigurationNameValue エントリの単なるリストです。 ConfigurationNameValue は、PropertyName とそのデフォルト値の 2 つの値を持つデータ クラスです。ここにセクションがあります。

from codeallybasic.ConfigurationProperties import Section
from codeallybasic.ConfigurationProperties import ConfigurationNameValue
from codeallybasic.ConfigurationProperties import PropertyName

SECTION_GENERAL: Section = Section(
    [
        ConfigurationNameValue(name=PropertyName('debug'),                           defaultValue='False'),
        ConfigurationNameValue(name=PropertyName('logLevel'),                     defaultValue='Info'),
        ConfigurationNameValue(name=PropertyName('phoneyEnumByValue'),  defaultValue=DEFAULT_PHONEY_ENUM_BY_VALUE.value),
        ConfigurationNameValue(name=PropertyName('impostorEnumByName'), defaultValue=DEFAULT_IMPOSTOR_ENUM_BY_NAME.name),
    ]
)

SECTION_DATABASE: Section = Section(
    [
        ConfigurationNameValue(name=PropertyName('dbName'), defaultValue='example_db'),
        ConfigurationNameValue(name=PropertyName('dbHost'), defaultValue='localhost'),
        ConfigurationNameValue(name=PropertyName('dbPort'), defaultValue='5432'),
    ]
)

2 つの新しい列挙プロパティを指定したことに注目してください。 永続化したいのは value で、もう 1 つはその名前です。

ここに列挙型の定義があります。

class PhoneyEnumByValue(Enum):
    TheWanderer = 'The Wanderer'
    Mentiroso   = 'Mentiroso'
    FakeBrenda  = 'Faker Extraordinaire'
    NotSet          = 'Not Set'

    @classmethod
    def deSerialize(cls, value: str) -> 'PhoneyEnumByValue':

    @classmethod
    def deSerialize(cls, value: str) -> 'PhoneyEnumByValue':

        match value:
            case PhoneyEnumByValue.TheWanderer.value:
                phoneyEnum: PhoneyEnumByValue = PhoneyEnumByValue.TheWanderer
            case PhoneyEnumByValue.Mentiroso.value:
                phoneyEnum = PhoneyEnumByValue.Mentiroso
            case PhoneyEnumByValue.FakeBrenda.value:
                phoneyEnum = PhoneyEnumByValue.FakeBrenda
            case _:
                raise Exception('Unknown PhoneyEnumByValue')

        return phoneyEnum


class ImpostorEnumByName(Enum):
    Low       = 0.1
    Medium = 0.5
    High     = 1.0
    NotSet = -1.0

これらが開発者クラスのプロパティ定義にどのような影響を与えるかを見ていきます

プロパティの定義

文字列プロパティは次のように定義されます。

BASE_FILE_NAME: str = 'config.ini'
MODULE_NAME:    str = 'version2properties'

class ConfigurationPropertiesVersion2(ConfigurationProperties, metaclass=SingletonV3):

    def __init__(self):
        self.logger: Logger = getLogger(LOGGER_NAME)

        super().__init__(baseFileName=BASE_FILE_NAME, moduleName=MODULE_NAME, sections=CONFIGURATION_SECTIONS)

        self._configParser.optionxform = self._toStr    # type: ignore

        self._loadConfiguration()

私たちが取り除いたのは、configParser にアクセスして値を取得および設定するための定型文です。 私たちが追加したのは、configurationGetter デコレータとconfigurationSetter デコレータです。 デコレータの実装の詳細については説明せず、読者のための演習として残しておきます。 これらのデコレーターは、構成パーサーとの対話を処理して、値を取得および設定します。 値を設定すると、configurationSetter デコレータが ライトスルー を実行します。

開発者は整数プロパティを次のように定義します。

from codeallybasic.ConfigurationProperties import Sections

CONFIGURATION_SECTIONS: Sections = Sections(
    {
        SectionName('General'):  SECTION_GENERAL,
        SectionName('Database'): SECTION_DATABASE,
    }
)

configurationGetter デコレーターにはオプションのパラメーターがあることに注意してください。 これは、文字列プロパティ値を取得し、呼び出し側プロパティに戻る前に、それを適切な型指定された値に変換する関数です。 これは float プロパティに適用できます。

列挙名を永続化する列挙プロパティは次のように定義されます:

from codeallybasic.ConfigurationProperties import Section
from codeallybasic.ConfigurationProperties import ConfigurationNameValue
from codeallybasic.ConfigurationProperties import PropertyName

SECTION_GENERAL: Section = Section(
    [
        ConfigurationNameValue(name=PropertyName('debug'),                           defaultValue='False'),
        ConfigurationNameValue(name=PropertyName('logLevel'),                     defaultValue='Info'),
        ConfigurationNameValue(name=PropertyName('phoneyEnumByValue'),  defaultValue=DEFAULT_PHONEY_ENUM_BY_VALUE.value),
        ConfigurationNameValue(name=PropertyName('impostorEnumByName'), defaultValue=DEFAULT_IMPOSTOR_ENUM_BY_NAME.name),
    ]
)

SECTION_DATABASE: Section = Section(
    [
        ConfigurationNameValue(name=PropertyName('dbName'), defaultValue='example_db'),
        ConfigurationNameValue(name=PropertyName('dbHost'), defaultValue='localhost'),
        ConfigurationNameValue(name=PropertyName('dbPort'), defaultValue='5432'),
    ]
)

適切なデコレータを使用することに加えて、列挙名を永続化するには enumUseName パラメータを使用し、それを True に設定することに注意してください。

以下は、開発者が値を永続化したい列挙型プロパティです。 setter デコレーターが列挙型であることを示していることに注意してください。 さらに、開発者は値を特定の列挙値に変換できる逆シリアル化メソッドを提供する必要があることに注意してください。

class PhoneyEnumByValue(Enum):
    TheWanderer = 'The Wanderer'
    Mentiroso   = 'Mentiroso'
    FakeBrenda  = 'Faker Extraordinaire'
    NotSet          = 'Not Set'

    @classmethod
    def deSerialize(cls, value: str) -> 'PhoneyEnumByValue':

    @classmethod
    def deSerialize(cls, value: str) -> 'PhoneyEnumByValue':

        match value:
            case PhoneyEnumByValue.TheWanderer.value:
                phoneyEnum: PhoneyEnumByValue = PhoneyEnumByValue.TheWanderer
            case PhoneyEnumByValue.Mentiroso.value:
                phoneyEnum = PhoneyEnumByValue.Mentiroso
            case PhoneyEnumByValue.FakeBrenda.value:
                phoneyEnum = PhoneyEnumByValue.FakeBrenda
            case _:
                raise Exception('Unknown PhoneyEnumByValue')

        return phoneyEnum


class ImpostorEnumByName(Enum):
    Low       = 0.1
    Medium = 0.5
    High     = 1.0
    NotSet = -1.0

プロパティへのアクセスと変更

プロパティへのアクセスと変更はバージョン 1 とまったく同じです。

    @property
    @configurationGetter(sectionName='General')
    def debug(self) -> str:
        return ''       # never executed

    @debug.setter
    @configurationSetter(sectionName='General')
    def debug(self, newValue: str):
        pass

上記のスニペットは次の出力を生成します。

    @property
    @configurationGetter(sectionName='Database', deserializeFunction=int)
    def dbPort(self) -> int:
        return -1

    @dbPort.setter
    @configurationSetter(sectionName='Database',)
    def dbPort(self, newValue: int):
        pass

結論

この記事のソースコードはここにあります。 サポート クラス SingletonV3 を参照してください。 ConfigurationProperties

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

実装の結果は、コードの消費者として満足しました。 型付きプロパティを取得および設定することができました。 当初期待していたほど多くのコードは削除されませんでした。 ただし、再利用可能なコードは提供されました。 ただし、個々のプロパティを生成する必要があるため、PyCharm で ライブ テンプレート を作成するように求められました。

利点

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

短所

  • まだたくさんの 定型文 コード
  • デコレータの使用は誤用だったような気がします

次の投稿では、いわゆる動的プロパティを実装しました。 すべての定型コードを完全に削除し、前述の利点を維持しました。

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

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