Pythonic 방법은 무엇입니까?
소개
Java의 경우 종속성 주입은 순수 OOP로 작동합니다. 즉, 구현할 인터페이스를 제공하고 프레임 워크 코드에서 정의 된 인터페이스를 구현하는 클래스의 인스턴스를 수락합니다.
이제 Python의 경우 동일한 방식으로 수행 할 수 있도록 Python의 경우에는 그 방법이 너무 많은 오버 헤드를 생각합니다. 표준 방식으로 어떻게 구현할까요?
사용 사례
다음이 프레임 워크 코드라고 가정합니다.
class FrameworkClass():
def __init__(self, ...):
...
def do_the_job(self, ...):
# some stuff
# depending on some external function
기본 접근 방식
가장 순진한 (그리고 아마도 가장 좋은?) 방법은 생성자 FrameworkClass
에 외부 함수를 제공하는 한 다음 do_the_job
메서드 에서 호출하는 것입니다.
프레임 워크 코드 :
class FrameworkClass():
def __init__(self, func):
self.func = func
def do_the_job(self, ...):
# some stuff
self.func(...)
클라이언트 코드 :
def my_func():
# my implementation
framework_instance = FrameworkClass(my_func)
framework_instance.do_the_job(...)
질문
질문은 짧습니다. 이를 위해 더 일반적으로 사용되는 Pythonic 방법이 있습니까? 아니면 대체 기능을 지원하는 라이브러리가 있습니까?
업데이트 : 구체적인 상황
토큰을 사용하여 인증을 처리하는 마이크로 웹 프레임 워크를 개발할 상상해. 이 프레임 워크 ID
에는 토큰에서 얻은 일부 를 제공하고 이에 해당하는 사용자를 가져 오는 함수가 필요 합니다 ID
.
분명히 프레임 워크는 사용자 또는 기타 애플리케이션에 어떤 특정 기능에 전혀 알지 못 클라이언트 코드는 인증이 작동하도록 프레임 워크에 사용자 getter 기능을 삽입해야합니다.
참조 레이몬드 Hettinger-슈퍼 슈퍼 고려! -DI 대신 수퍼 및 다중 상속을 사용하는 방법에 대한 논의에 대한 PyCon 2015 . (하지만 모두 시청하는 것이 좋습니다).
다음은이 비디오에 설명 된 내용을 예제에 적용하는 방법의 예입니다.
프레임 워크 코드 :
class TokenInterface():
def getUserFromToken(self, token):
raise NotImplementedError
class FrameworkClass(TokenInterface):
def do_the_job(self, ...):
# some stuff
self.user = super().getUserFromToken(...)
클라이언트 코드 :
class SQLUserFromToken(TokenInterface):
def getUserFromToken(self, token):
# load the user from the database
return user
class ClientFrameworkClass(FrameworkClass, SQLUserFromToken):
pass
framework_instance = ClientFrameworkClass()
framework_instance.do_the_job(...)
이것은 Python MRO가 getUserFromToken 클라이언트 메서드가 호출되도록 보장하기 때문에 작동합니다 (super ()가 사용되는 경우). Python 2.x를 사용하는 경우 코드를 변경해야합니다.
여기에 추가 된 한 가지 이점은 클라이언트가 구현을 제공하지 예외가 발생하는 것입니다.
물론 다중 상속과 믹스 인이지만 문제가 있습니다.
프로젝트에서 의존성 주입을하는 방법은 주사한다 lib 디렉토리 를 사용하는을 구석으로 입니다. 문서를 확인하십시오 . DI에 사용하는 것이 좋습니다. 하나의 기능만으로는 의미가 없지만 여러 데이터 소스 등을 관리해야 할 때 많은 의미가 있습니다.
귀하의 예를 따르면 다음과 유사 할 수 있습니다.
# framework.py
class FrameworkClass():
def __init__(self, func):
self.func = func
def do_the_job(self):
# some stuff
self.func()
사용자 정의 기능 :
# my_stuff.py
def my_func():
print('aww yiss')
모든 애플리케이션을 어딘가에 정의 된 모든 애플리케이션을 추적하는 부트 파일을 만들려고합니다.
# bootstrap.py
import inject
from .my_stuff import my_func
def configure_injection(binder):
binder.bind(FrameworkClass, FrameworkClass(my_func))
inject.configure(configure_injection)
그런 다음 다음과 같이 코드를 사용할 수 있습니다.
# some_module.py (has to be loaded with bootstrap.py already loaded somewhere in your app)
import inject
from .framework import FrameworkClass
framework_instance = inject.instance(FrameworkClass)
framework_instance.do_the_job()
나는 인터페이스 나 힌팅과 같은 멋진 유형 가지고 있지 않기 때문에 이것이 바로 수있는 그런 큼 비단뱀 적입니다 (하나의 변수 등에서 할 데코레이터와 같은 단맛이 있습니다-문서 확인).
따라서 귀하의 질문에 직접 답변하는 것은 매우 어려운 것입니다. 진정한 질문은 기본적으로 DI를 기본적으로 지원합니까? 그리고 그 대답은 슬프게도 아니오입니다.
얼마 전에 나는 그것을 Pythonic- Dependency Injector 로 생성자하는 야망으로 의존성 도입 마이크로 프레임 워크를 썼다 . 사용하는 경우 코드가 다음과 같이 보일 수 있습니다.
"""Example of dependency injection in Python."""
import logging
import sqlite3
import boto.s3.connection
import example.main
import example.services
import dependency_injector.containers as containers
import dependency_injector.providers as providers
class Platform(containers.DeclarativeContainer):
"""IoC container of platform service providers."""
logger = providers.Singleton(logging.Logger, name='example')
database = providers.Singleton(sqlite3.connect, ':memory:')
s3 = providers.Singleton(boto.s3.connection.S3Connection,
aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(containers.DeclarativeContainer):
"""IoC container of business service providers."""
users = providers.Factory(example.services.UsersService,
logger=Platform.logger,
db=Platform.database)
auth = providers.Factory(example.services.AuthService,
logger=Platform.logger,
db=Platform.database,
token_ttl=3600)
photos = providers.Factory(example.services.PhotosService,
logger=Platform.logger,
db=Platform.database,
s3=Platform.s3)
class Application(containers.DeclarativeContainer):
"""IoC container of application component providers."""
main = providers.Callable(example.main.main,
users_service=Services.users,
auth_service=Services.auth,
photos_service=Services.photos)
다음은이 예제에 대한보다 광범위한 설명에 대한 링크입니다 -http : //python-dependency-injector.ets-labs.org/examples/services_miniapp.html
도움이되기를 바랍니다. 자세한 내용은 다음을 방문하십시오.
- GitHub https://github.com/ets-labs/python-dependency-injector
- 문서 http://python-dependency-injector.ets-labs.org/
DI와 아마도 AOP는 일반적인 Python 개발자의 선호도 때문에 일반적으로 Pythonic으로 간주되지 않는다고 생각합니다.
사실 메타 클래스와 클래스 데코레이터를 사용하여 100 줄 미만의 기본 DI 프레임 워크를 구현할 수 있습니다 .
덜 침습적 인 솔루션의 경우 이러한 구성을 사용하여 사용자 정의 구현을 일반 프레임 워크에 플러그인 할 수 있습니다.
Python OOP 구현으로 인해 IoC 및 종속성 주입은 Python 세계에서 일반적인 관행이 아닙니다. 그럼에도 불구하고 접근 방식은 Python에게도 유망 해 보였습니다.
- 동일한 코드 기반에 정의 된 클래스라도 종속성을 인수로 사용하는 것은 극적으로 비 파이썬 접근 방식입니다. Python은 아름답고 우아한 OOP 모델을 가진 OOP 언어이므로 무시하는 것은 좋은 생각이 아닙니다.
- 인터페이스 유형을 모방하기 위해 추상 메서드로 가득 찬 클래스를 정의하는 것도 이상합니다.
- 래퍼에 대한 거대한 해결 방법은 사용하기에 너무 덜 우아합니다.
- 또한 작은 패턴 만 필요한 경우 라이브러리를 사용하는 것을 좋아하지 않습니다.
그래서 내 해결책 은 다음과 같습니다.
# Framework internal
def MetaIoC(name, bases, namespace):
cls = type("IoC{}".format(name), tuple(), namespace)
return type(name, bases + (cls,), {})
# Entities level
class Entity:
def _lower_level_meth(self):
raise NotImplementedError
@property
def entity_prop(self):
return super(Entity, self)._lower_level_meth()
# Adapters level
class ImplementedEntity(Entity, metaclass=MetaIoC):
__private = 'private attribute value'
def __init__(self, pub_attr):
self.pub_attr = pub_attr
def _lower_level_meth(self):
print('{}\n{}'.format(self.pub_attr, self.__private))
# Infrastructure level
if __name__ == '__main__':
ENTITY = ImplementedEntity('public attribute value')
ENTITY.entity_prop
Google의 오픈 소스 파이썬 종속성 주입기 인 Pinject도 있습니다.
다음은 예입니다.
>>> class OuterClass(object):
... def __init__(self, inner_class):
... self.inner_class = inner_class
...
>>> class InnerClass(object):
... def __init__(self):
... self.forty_two = 42
...
>>> obj_graph = pinject.new_object_graph()
>>> outer_class = obj_graph.provide(OuterClass)
>>> print outer_class.inner_class.forty_two
42
그리고 여기에 소스 코드가 있습니다
참고 URL : https://stackoverflow.com/questions/31678827/what-is-a-pythonic-way-for-dependency-injection
'ProgramingTip' 카테고리의 다른 글
HAL과 HATEOAS의 관계 및 차이점 (0) | 2020.12.05 |
---|---|
Docker- 컨테이너가 실행되고 있지 않습니다. (0) | 2020.12.05 |
C ++ 17에서 보호 된 생성자에 대한 규칙이 변경 되었습니까? (0) | 2020.12.05 |
ThreadPool 대 작업 (0) | 2020.12.05 |
사실과 이론의 차이? (0) | 2020.12.05 |