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
사용). GasStation
의 fill_tank
기능은 더 이상 특정 자동차 클래스에 의존하지 않고 Vehicle
인터페이스에 의존하므로 더욱 일반화되고 fill
메서드를 구현하는 모든 차량에 연료를 공급할 수 있습니다.
Python의 유형 힌트 시스템을 활용하여 PyDIT(Python 종속성 주입 with 유형)라는 종속성 반전 사용을 단순화하는 라이브러리를 만들었습니다.
사용자 데이터를 저장하기 위한 데이터베이스 인터페이스가 필요하다고 가정해 보겠습니다. 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: Marcelo Almeida(MrM4rc)
PyPI: python-pydit
위 내용은 Python에서 타이핑이 미치는 영향의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!