импорт из python Abtract Factory - PullRequest
       28

импорт из python Abtract Factory

4 голосов
/ 30 января 2020

Я хочу создать абстрактную фабрику, чтобы абстрагировать аппаратные различия между компьютерами (например, RaspberryPi и Arduino) в Python 2.7.

Я использую следующую реализацию абстрактной фабрики:

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

Инстанцирование работает нормально, но когда я пытаюсь запустить это, я получаю:

    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

Так что импорт arduino_display работает, но я не могу найти способ чтобы использовать его в объекте.

Необходим условный импорт, поскольку на разных платформах будут установлены разные модули.

Есть идеи, как использовать этот условный импорт?

Я пытался self.arduino_display и ArduinoDisplay.arduino_display, но безрезультатно.

Я, очевидно, мог бы отследить ошибки импорта, например, добавить в начало:

    try:
        import arduino_display
    except:
        pass

... и это не получится на RPI, что было бы хорошо, но должен быть лучший способ ...

Ответы [ 3 ]

1 голос
/ 04 февраля 2020

Проблема связана с import только связывающими именами в текущей области видимости. Выполнение import в функции / методе не делает его доступным в других методах.

Выполните импорт там, где он вам действительно нужен. Например, ArduinoDisplay должен импортировать arduino_display там, где он используется:

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)

Обратите внимание, что import является идемпотентным - если модуль уже был загружен ранее, import просто связывает имя снова. Это делает такие вложенные операторы import достаточно быстрыми для большинства случаев.


Если ваши классы нуждаются в большом количестве операций импорта или если проблема заключается в скорости, изолируйте классы на отдельные модули и условно импортируйте весь модуль. Вы можете напрямую назначить правильный класс, используя общее имя, вместо фиктивного типа, который создает другой.

# ## 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
0 голосов
/ 04 февраля 2020

Проблема в том, что импортируемая вами функция нигде не сохраняется, чтобы другие методы могли их видеть, поэтому они не видны нигде, кроме как локально. Попробуйте установить ArduinoDisplay.arduino_display = arduino_display после импорта.

Вот некоторый пример кода, который иллюстрирует мое значение:

class ADisplay:

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

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

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

Это не работает с NameError: name 'display_write' is not defined.

Теперь, привяжите импорт к вашему классу.

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

Это работает. Фактически вы даже можете обойтись без show, назначив его напрямую, если все эти аппаратные методы печати принимают в качестве параметра только строку и если вам не нужно ничего форматировать или изменять.

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")
0 голосов
/ 30 января 2020

Вы пытались использовать from arduino_display import println, а затем println, как вы это сделали с RPI writeline?

Редактировать: пропустил очевидное. ..

Вы можете сделать что-то вроде этого:

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

Если вам нужны классы и вложенная фабрика, вы можете применить тот же принцип хранения функционального объекта там же.

...