4 votes

Importer à partir d'une usine Abtract en python

Je souhaite créer une Abstract Factory afin d'abstraire les différences matérielles entre les ordinateurs (disons un RaspberryPi et un Arduino) dans Python 2.7.

J'utilise l'implémentation suivante d'une usine abstraite :

  '''
  Provide a device-agnostic display interface
  '''
  from hardware import sysname

  class DisplayBase(object):
        def __init__(self):
           pass

        def show(self, message):
           pass

        def __str__(self):
           return "DisplayBase"

        def __repr__(self):
           return self.__str__()

   class RPIDisplay(DisplayBase):
        def __new__(cls, *args, **kwargs):
            from rpi_display import writeline
            instance = super(RPIDisplay, cls).__new__(cls, *args, **kwargs)
            return instance

        def __str__(self):
            return "RPIDisplay"

       def show(self, message):
           writeline(message)

    class ArduinoDisplay(DisplayBase):
        def __new__(cls, *args, **kwargs):
            import arduino_display
            instance = super(ArduinoDisplay, cls).__new__(cls, *args, **kwargs)
            return instance

        def __str__(self):
            return "ArduinoDisplay"

        def show(self, message):
            return arduino_display.println(message)

   class Display(DisplayBase): # Display Factory
       def __new__(cls, *args, **kwargs):
           platform = sysname()
           if platform == "RaspberryPi":
               return RPIDisplay()
           elif platform == "Arduino":
               return ArduinoDisplay()
           else:
               return MockDisplay()

    if __name__ == "__main__":
        display = Display()
        print display
        display.show("hello world")

L'instanciation fonctionne correctement, mais lorsque j'essaie de l'exécuter, j'obtiens :

    ArduinoDisplay
    Traceback (most recent call last):
    File "tt.py", line 56, in <module>
      display.show("hello world")
    File "tt.py", line 41, in show
      return arduino_display.println(message)
    NameError: global name 'arduino_display' is not defined

L'importation de arduino_display fonctionne en quelque sorte, mais je ne trouve pas le moyen de l'utiliser à l'intérieur de l'objet.

Les importations conditionnelles sont nécessaires car des modules différents sont installés sur différentes plates-formes.

Une idée sur la manière d'utiliser ces importations conditionnelles ?

J'ai essayé self.arduino_display y ArduinoDisplay.arduino_display mais en vain.

Je pourrais évidemment détecter les erreurs d'importation, comme dans le cas d'un ajout au sommet :

    try:
        import arduino_display
    except:
        pass

...et cela échouerait sur un RPI, ce qui serait très bien, mais il doit y avoir un meilleur moyen...

1voto

MisterMiyagi Points 2734

Le problème est dû à import uniquement les noms de liaison dans son actuel champ d'application. La réalisation d'un import dans une fonction/méthode ne la rend pas disponible dans d'autres méthodes.

Effectuez les importations là où vous en avez réellement besoin. Par exemple, ArduinoDisplay doit importer arduino_display où il est utilisé :

class ArduinoDisplay(DisplayBase):
    # no new, no import

    def __str__(self):
        return "ArduinoDisplay"

    def show(self, message):
        # import where needed
        import arduino_display
        return arduino_display.println(message)

Il convient de noter que import est idempotent -- si le module a déjà été chargé auparavant, import se contente de lier à nouveau le nom. Cela permet d'imbriquer des import suffisamment rapide dans la plupart des cas.


Si vos classes nécessitent de nombreuses importations ou si la vitesse est un problème, isolez les classes dans des modules distincts et importez l'ensemble du module de manière conditionnelle. Vous pouvez directement attribuer la bonne classe en utilisant le nom commun, au lieu d'avoir un type fictif qui en construit un autre.

# ## display/arduino.py ##
# other systems accordingly
from .base import DisplayBase

# import once, globally
import arduino_display

class ArduinoDisplay(DisplayBase):
    # no new, no import

    def __str__(self):
        return "ArduinoDisplay"

    def show(self, message):
        # import where needed
        return arduino_display.println(message)

# ## display/__init__.py ##
from hardware import sysname

platform = sysname()
# resolve and import appropriate type once
if platform == "RaspberryPi":
    from .rpi import RPIDisplay as Display
elif platform == "Arduino":
    from .arduino import ArduinoDisplay as Display
else:
    from .mock import MockDisplay as Display

0voto

schneebuzz Points 348

Avez-vous essayé d'utiliser from arduino_display import println puis utiliser println comme vous l'avez fait avec les RPI writeline ?

Editer : a manqué l'évidence...

Vous pouvez faire quelque chose comme ça :

from hardware import sysname

class Display(object):
    def __init__(self, print_function, display_name):
        self._print_function = print_function
        self._display_name = display_name

    def show(self, message):
        self.print_function(message)

    def __str__(self):
        return self._display_name

    def __repr__(self):
        return self.__str__()

def create_display():
    platform = sysname()
    if platform == "RaspberryPi":
        from rpi_display import writeline
        return Display(writeline, "RPIDisplay")
    elif platform == "Arduino":
        from arduino_display import println
        return Display(println, "ArduinoDisplay")

Si vous avez besoin des classes et de la fabrique imbriquée, vous pouvez appliquer le même principe de stockage de l'objet fonction.

0voto

JL Peyret Points 1271

Le problème est que la fonction que vous importez n'est enregistrée nulle part pour que les autres méthodes puissent la voir, de sorte qu'elle n'est visible nulle part ailleurs que localement. Essayez de définir ArduinoDisplay.arduino_display = arduino_display après l'importation.

Voici un exemple de code qui illustre mon propos :

class ADisplay:

    def __init__(self):
        from builtins import print as display_write

    def show(self, msg):
        display_write(msg)

disp = ADisplay()
disp.show("mymsg")

Cela échoue avec NameError: name 'display_write' is not defined .

Maintenant, liez l'importation à votre classe.

class ADisplay:

    def __init__(self):
        from builtins import print as display_write
        ADisplay.display_write = display_write

    def show(self, msg):
        self.display_write(msg)

disp = ADisplay()
disp.show("mymsg")

Cela fonctionne. En fait, vous pouvez même vous passer de show en l'assignant directement, si toutes les méthodes d'impression matérielles ne prennent qu'une chaîne de caractères comme paramètre et si vous n'avez pas besoin de formater ou de modifier quoi que ce soit.

class ADisplay:

    def __init__(self):
        #do other init stuff...
        pass

        #only bind show the first time.
        if getattr(ADisplay, "show", None):
            return

        from builtins import print as display_write
        ADisplay.show = display_write

disp = ADisplay()
disp.show("mymsg")

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X