ProgramingTip

Pythonic 방법은 무엇입니까?

bestdevel 2020. 12. 5. 10:20
반응형

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

도움이되기를 바랍니다. 자세한 내용은 다음을 방문하십시오.


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

반응형