Python での入力の影響

Susan Sarandon
Susan Sarandonオリジナル
2025-01-16 22:13:14580ブラウズ

O impacto da tipagem no python

Python バージョン 3.5 では、コードを読みやすくし、開発者がお互いのコードを理解しやすくするために「型ヒント」が導入されました。

型ヒントが重要なのはなぜですか?

Java や C などの強く型付けされた言語では、依存関係の反転 (DI - dependency Inversion) は重要な技術ですが、弱い型付けの言語では実装が困難です。

依存関係の逆転の中心となる考え方は、クラスは特定の実装ではなく抽象化に依存すべきであるということです。抽象化 (インターフェイスまたは抽象クラス) は比較的安定したコントラクトであるためです。

悪い例:

<code class="language-python">class GasStation:
    def fill_tank(car, amount):
        car.fill(amount)</code>

この例では、ガソリン スタンドは車に給油することしかできません。さらに悪いことに、関数 fill_tank には型が定義されていないため、任意の値が渡される可能性があり、エラーは実行時にのみ発見されます。

良い例:

<code class="language-python">from typing import Protocol

class Vehicle(Protocol):
    def fill(amount: int) -> None:
        ...
class GasStation:
    def fill_tank(vehicle: Vehicle, amount: int) -> None:
        vehicle.fill(amount)</code>

この例では、最初に抽象クラス Vehicle を定義します (typing.Protocol を使用)。 GasStationfill_tank 機能は、特定の自動車クラスに依存するのではなく、Vehicle インターフェースに依存するようになり、より一般化され、fill メソッドを実装するあらゆる車両に燃料を補給できます。

PyDIT とは何ですか?

私は Python の型ヒント システムを利用して、PyDIT (Python dependency Injection with Types) と呼ばれる依存関係反転の使用を簡素化するライブラリを作成しました。

ユーザー データを保存するためのデータベース インターフェイスが必要であるとします。PostgreSQL、MySQL、OracleDB、インメモリ データベース、NoSQL データベースのいずれを使用する場合でも、データベース接続クラスを実装し、レコードの読み取り、書き込み、削除の機能を提供する必要があります。 。

<code class="language-python">from time import sleep
from typing import TypedDict
from typing_extensions import override
from uuid import UUID
from src.configs.di import pydit
from src.adapters.repositories.interfaces.user import UserRepository
from src.constants.injection import MEMORY_REPOSITORY_CONFIG_TOKEN
from src.domain.user.models.user import UserModel


class ConfigType(TypedDict):
    delay: int


class MemoryUserRepository(UserRepository):

    __users: dict[UUID, UserModel] = {}

    def __init__(self):
        self.__delay = self.config.get("delay", 0.2)

    @pydit.inject(token=MEMORY_REPOSITORY_CONFIG_TOKEN)
    def config(self) -> ConfigType:  # TODO: supress return type error
        pass

    @override
    def get_by_id(self, *, id_: UUID) -> UserModel:
        sleep(self.__delay)

        user = self.__users.get(id_)

        if user is None:
            raise ValueError("User not found")

        return user

    @override
    def save(self, *, data: UserModel) -> None:
        sleep(self.__delay)
        self._check_pk_conflict(pk=data.id)

        self.__users[data.id] = data

    @override
    def list_(self) -> list[UserModel]:
        return list(self.__users.values())

    def _check_pk_conflict(self, *, pk: UUID) -> None:
        if pk not in self.__users:
            return

        raise ValueError("Primary key conflicts: DB alrady has a user with this ID")</code>

コードがデータベース テクノロジと無関係であることを保証するために、すべてのデータベース クラスが従う必要があるインターフェイスを定義します。

<code class="language-python">from abc import abstractmethod
from typing import Protocol
from uuid import UUID
from src.domain.user.models.user import UserModel


class UserRepository(Protocol):
    @abstractmethod
    def get_by_id(self, *, id_: UUID) -> UserModel:
        pass

    @abstractmethod
    def save(self, *, data: UserModel) -> None:
        pass

    @abstractmethod
    def list_(self) -> list[UserModel]:
        pass</code>

次に、注入用の依存関係を初期化します。

<code class="language-python">from src.adapters.repositories.in_memory.user import MemoryUserRepository
from src.constants.injection import MEMORY_REPOSITORY_CONFIG_TOKEN
from .di import pydit
from .get_db_config import get_db_config


def setup_dependencies():
    pydit.add_dependency(get_db_config, token=MEMORY_REPOSITORY_CONFIG_TOKEN)
    pydit.add_dependency(MemoryUserRepository, "UserRepository")</code>

最後に、ユーザーを作成するモジュールに依存関係を挿入します。

<code class="language-python">from typing import cast
from src.adapters.repositories.interfaces.user import UserRepository
from src.configs.di import pydit
from src.domain.user.models.create_user import CreateUserModel
from src.domain.user.models.user import UserModel
from src.domain.user.services.create import CreateUserService
from src.domain.user.services.list import ListUsersService


class UserModule:
    @pydit.inject()
    def user_repository(self) -> UserRepository:
        return cast(UserRepository, None)

    def create(self, data: CreateUserModel) -> None:
        CreateUserService(self.user_repository).execute(data)

    def list_(self) -> list[UserModel]:
        return ListUsersService().execute()</code>

依存関係はプロパティとして挿入され、 self または module.user_repository を介してアクセスできます。

この例は単純ですが、PyDIT はさまざまなプロジェクト構成、コードの抽象化、および SOLID 原則のシナリオに適用できます。コードを試して貢献することを歓迎します。

コードリポジトリ: Github
LinkedIn: マルセロ アルメイダ (MrM4rc)
PyPI: python-pydit

以上がPython での入力の影響の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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