¿Qué es un Python el camino para la Inyección de Dependencia?

Introducción

Para Java, la Inyección de Dependencia trabaja como pura programación orientada a objetos, es decir, puede proporcionar una interfaz para ser implementados en el marco del código de aceptar una instancia de una clase que implementa la interfaz definida.

Ahora para Python, que son capaces de hacer de la misma manera, pero creo que ese método era demasiado arriba a la derecha en el caso de Python. Así que entonces, ¿cómo sería implementarlo en el Python el camino?

Caso De Uso

Decir que este es el marco de trabajo de código:

class FrameworkClass():
    def __init__(self, ...):
        ...

    def do_the_job(self, ...):
        # some stuff
        # depending on some external function

El Enfoque Básico

La mayoría de los ingenuos (y tal vez el mejor?) forma es la de exigir la función externa para ser suministrada a la FrameworkClass constructor y, a continuación, se invoca desde el do_the_job método.

Marco De Código:

class FrameworkClass():
    def __init__(self, func):
        self.func = func

    def do_the_job(self, ...):
        # some stuff
        self.func(...)

El Código De Cliente:

def my_func():
    # my implementation

framework_instance = FrameworkClass(my_func)
framework_instance.do_the_job(...)

Pregunta

La pregunta es corta. Es mejor que comúnmente se utiliza Python manera de hacer esto? O tal vez ninguna de las bibliotecas que apoyan dicha funcionalidad?

ACTUALIZACIÓN: Situación Concreta

Imaginar que desarrollar un micro framework web, que se encarga de la autenticación mediante fichas. Este marco tiene una función para el suministro de algunos ID obtenidos a partir de la señal y conseguir que el usuario correspondiente para que ID.

Obviamente, el marco no sabe nada acerca de los usuarios o cualquier otra aplicación específica de la lógica, de forma que el código de cliente se tiene que inyectar el usuario captador de funcionalidad en el marco para realizar la autenticación de trabajo.

  • ¿Por qué no «proporcionar una interfaz para ser implementados en el marco del código de aceptar una instancia de una clase que implementa la interfaz definida»? En Python se haría esto en un EAFP el estilo (es decir, suponer que se cumple que la interfaz y un AttributeError o TypeError obtiene produce lo contrario), pero por lo demás es el mismo.
  • Es fácil hacer que el uso de abs‘s ABCMeta metaclass con @abstractmethod decorador, y no hay validación manual. Sólo quieren obtener par de opciones y sugerencias. La que presento es la más limpia, pero creo que con más sobrecarga.
  • Entonces no sé cuál es la pregunta que usted está tratando de preguntar.
  • Ok, voy a probar en otras palabras. El problema es claro. La pregunta es cómo hacerlo en Python el camino. Opción 1: La forma en que usted ha citado, la Opción 2: El enfoque básico descrito en la pregunta. Así que la pregunta es, cualquier otro Python formas de hacer eso?
  • «No debe ser uno, y preferiblemente sólo una –una manera obvia de hacerlo.»
  • Gracias, yo sé que 🙂 pero aquí, obviamente, hay más de una manera.
  • Esta es, obviamente, muy específicos de la aplicación. Hay una razón particular por qué, en su aplicación, myfunc no está envuelto en mysubclass? (A continuación, usted no necesita asignar en __init__)
  • Gracias por el comentario. He actualizado la pregunta a responder su pregunta.
  • Aquí usted puede encontrar un buen ejemplo: code.activestate.com/recipes/413268

InformationsquelleAutor bagrat | 2015-07-28

6 Kommentare

  1. 53

    Ver Raymond Hettinger – Super considerado super! – PyCon 2015 para una discusión acerca de cómo utilizar el super y la herencia múltiple en lugar de DI. Si usted no tiene tiempo para ver el vídeo entero, saltar a 15 minutos (pero me gustaría recomendar ver todo).

    Aquí es un ejemplo de cómo aplicar lo que se describe en este vídeo a su ejemplo:

    Marco De Código:

    class TokenInterface():
        def getUserFromToken(self, token):
            raise NotImplementedError
    
    class FrameworkClass(TokenInterface):
        def do_the_job(self, ...):
            # some stuff
            self.user = super().getUserFromToken(...)

    El Código De Cliente:

    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(...)

    Esto va a funcionar porque el Python MRO se garantiza que el getUserFromToken cliente llama al método (si super() se utiliza). El código tendrá que cambiar si estás en Python 2.x.

    Un beneficio añadido es que esto provocará una excepción si el cliente no proporciona una implementación.

    Por supuesto, esto no es realmente la inyección de dependencia, es la herencia múltiple y mixins, pero es un Python manera de resolver su problema.

    • Esta respuesta se considera super() 🙂
    • Raymond lo llamó CI, mientras que, pensé, que es un puro mixin. Pero podría ser que en Python mixin y CI son prácticamente los mismos? La única diferencia es el nivel de indection. Mixin inyecta la dependencia en un nivel de clase, mientras que CI inyecta dependencia en una instancia.
    • Creo constructor nivel de inyección es muy fácil de hacer en Python de todos modos, como la forma OP describió. esta python el camino que parece ser muy interesante, aunque. solo requiere de un poco más de cableado que una simple inyección de constructor de la OMI.
    • Aunque me parece muy elegante, tengo dos problemas con este enfoque: 1. ¿Qué sucede cuando usted necesita varios elementos inyectados en su clase? 2. La herencia es la más utilizada en un «es»/especialización de sentido. Utilizando para DI desafía la idea (por ejemplo si quiero inyectar un Servicio en un Presentador).
  2. 17

    La forma de hacer la inyección de dependencia en nuestro proyecto es el uso de la inyectar lib. Retirar el documentación. Recomiendo usarlo para DI. Es un poco sin sentido con una sola función, pero empieza a hacer un montón de sentido cuando tienes que gestionar múltiples fuentes de datos, etc, etc.

    Siguiendo tu ejemplo podría ser algo similar a:

    # framework.py
    class FrameworkClass():
        def __init__(self, func):
            self.func = func
    
        def do_the_job(self):
            # some stuff
            self.func()

    Su función personalizada:

    # my_stuff.py
    def my_func():
        print('aww yiss')

    En algún lugar de la aplicación que desea crear un archivo de bootstrap que realiza un seguimiento de todas las dependencias definidas:

    # bootstrap.py
    import inject
    from .my_stuff import my_func
    
    def configure_injection(binder):
        binder.bind(FrameworkClass, FrameworkClass(my_func))
    
    inject.configure(configure_injection)

    Y, a continuación, usted podría consumir el código de esta manera:

    # 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()

    Me temo que esto es como python como se puede conseguir (el módulo tiene algunas python dulzura como decoradores para inyectar por parámetro etc – verificación de la documentación), como python no tiene complicaciones como interfaces o tipo de sugerencias.

    Así a responder a su pregunta directamente sería muy duro. Creo que la verdadera pregunta es: ¿de python tienen algún soporte nativo para el DI? Y la respuesta es, lamentablemente: no.

    • Gracias por la respuesta, parece bastante interesante. Voy a descargar el decoradores parte. En el mientras tanto, vamos a esperar más respuestas.
    • Gracias por el enlace a la ‘inyectar’ de la biblioteca. Esto es lo más cercano que he encontrado hasta el momento para llenar los huecos que quería llenado por DI-y bono, que en realidad se mantiene!
  3. 7

    Hace algún tiempo escribí la inyección de dependencia microframework con una ambición de hacer Python – La Dependencia Del Inyector. Que trata de cómo el código puede verse como en el caso de su uso:

    """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)

    Aquí hay un enlace a una descripción más extensa de este ejemplo – http://python-dependency-injector.ets-labs.org/examples/services_miniapp.html

    La esperanza puede ayudar un poco. Para obtener más información, por favor visite:

    • Gracias @Romano Mogylatov. Tengo la curiosidad de saber cómo configurar/adaptar estos contenedores en tiempo de ejecución, por ejemplo, de un archivo de configuración. Parece que estas dependencias están codificados en el contenedor (Platform y Services). Es la solución para crear un nuevo contenedor para cada combinación de inyectables de la biblioteca de clases?
    • Hola @BillDeRose. Mientras que mi respuesta fue considerado como demasiado largo para ser un MODO de comentario, he creado un github edición y post mi respuesta no – github.com/ets-labs/python-dependency-injector/issues/197 🙂 Espero ayuda, Gracias, Romano
  4. 1

    Creo que DI y posiblemente AOP no son consideradas como Python el porque de las típicas desarrolladores de Python preferencias, en lugar de que las características del idioma.

    Como una cuestión de hecho, usted puede implementar un básico DI marco en <100 líneas, utilizando metaclasses y la clase de los decoradores.

    De una forma menos invasiva de la solución, estas construcciones pueden ser utilizados para el plug-in de implementaciones personalizadas en un marco genérico.

  5. 0

    Debido a Python programación orientada a objetos la aplicación de la Coi y de la inyección de dependencia no son prácticas comunes en Python en el mundo. Sin embargo, el enfoque parecían prometedores, incluso para Python.

    • El uso de dependencias como argumentos incluso es una clase definida en la misma base de código es radicalmente no-python el enfoque. Python es el lenguaje OOP con bonito y elegante modelo de programación orientada a objetos, por lo que ignorar no es una buena idea.
    • Definir clases lleno de métodos abstractos sólo para imitar tipo de interfaz es extraño demasiado.
    • Enorme contenedor en contenedor soluciones son también menos elegante para ser utilizado.
    • Tampoco me gusta usar las bibliotecas, cuando todo lo que necesita es un pequeño patrón.

    Así que mi solución es:

    # 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         
  6. 0

    También hay Pinject, de código abierto python dependencia del inyector por Google.

    Aquí es un ejemplo

    >>> 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

    Y aquí está el código fuente

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea