【问题标题】:importing from within a python Abtract Factory从 python 抽象工厂中导入
【发布时间】:2020-05-15 23:29:54
【问题描述】:

我想创建一个抽象工厂,以便在 Python 2.7 中抽象出计算机(比如 RaspberryPi 和 Arduino)之间的硬件差异。

我正在使用抽象工厂的以下实现:

  '''
  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_displayArduinoDisplay.arduino_display,但无济于事。

我显然可以捕获导入错误,例如,添加到顶部:

    try:
        import arduino_display
    except:
        pass

...这会在 RPI 上失败,这很好,但必须有更好的方法...

【问题讨论】:

  • 有一个错字:ArduionDisplay 在里面。
  • 您是否尝试过使导入全球化?
  • 修正了错字。
  • 问题是您的导入没有在任何地方保存以供其他方法查看,因此它们在其他任何地方都不可见,只能在本地看到。导入后尝试设置ArduinoDisplay.arduino_display = arduino_display。您可能需要使用 ArduinoDisplay.arduino_display = staticmethod(arduino_display) 中的静态方法。现在,在show 中使用ArduinoDisplay.arduino_display,即保存的导入。

标签: python abstract-factory new-style-class


【解决方案1】:

问题是由于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

【讨论】:

    【解决方案2】:

    您是否尝试过使用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")
    
    

    如果你需要类和嵌套工厂,你也可以应用同样的原则来存储函数对象。

    【讨论】:

      【解决方案3】:

      问题是您导入的函数没有保存在任何地方以供其他方法查看,因此它们在其他任何地方都不可见,只能在本地看到。导入后尝试设置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")
      

      【讨论】:

        猜你喜欢
        • 2011-01-05
        • 1970-01-01
        • 1970-01-01
        • 2014-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多