难以将类代码移动到python中的模块

时间:2014-04-23 05:49:26

标签: python class module subclass modularity

如何在

时创建有效的可重用模块
  1. 模块中的一个类在另一个模块中创建另一个类的实例,并且
  2. 主程序想要使用仅在其中一个模块的子类中的方法吗?
  3. 在类和子类的上下文中,我的目标是:

    • 获取最常用和可重用的类,并将它们提取到可以在多个程序中重用的模块。
    • 在主程序中定义子类,以使代码中最具体(和不可重用)的部分保留在模块之外。

    各个类都有自己的逻辑,倾向于在模块中交互并创建彼此的实例 - 一切都很好。但是当我需要向类添加更多特定的子类方法时,这些方法不可用于模块中创建的实例。

    以下是我遇到的一个例子:

    ==firstclass.py===
    
    """This is a reusable class within a module.  It creates an instance of
    another class in another module."""
    
    from secondclass import Shape
    
    class DataObject(object):
        """Create a class that holds data objects."""  
        def __init__(self, x, y):
            self.m_x = x
            self.m_y = y
            self.m_shape_list = []
    
        def createShape(self, type):
            # here we create an instance of the second class
            new_shape = Shape(type)
            self.m_shape_list.append(new_shape)
    
        def printCoords(self):
            print "Coordinates:", (x,y)
    
    ===secondclass.py===
    
    """This is another reusable class. An instance of this gets created within
    an another class and it is also subclassed by the main program."""
    
    class Shape(object):
        """Create a class that holds shape info."""
        def __init__(self,type):
            self.m_type = type
            print "Shape:",type
    
        def printShape(self):
            print "Shape:",self.m_type
    ===main.py===
    
    """This is my main program and where all the classes get subclassed to add
    specific implementation details."""
    
    from firstclass import DataObject
    from secondclass import Shape
    
    class GraphicObject(DataObject):
        """Create a subclass of DataObject that holds graphic specific info."""
        def __init__(self, x, y, color):
            print "Init MySubClass"
            super(GraphicObject,self).__init__(x, y)
    
        def createSomeShapes(self):
            self.createShape('circle')
            self.createShape('square')
            self.createShape('octogon')
    
        def renderAll(self):
            for shape in self.m_shape_list:
                shape.render()
    
    class MyShape(Shape):
        """Create a subclass of Shape that holds graphic specific info."""
        def __init__(self, type):
            if type == circle:
                type = 'round thing'
            super(MyShape,self).__init__(type)
    
        def render(self):
            print "We got a",shape
    
    # Create an instance of the first class
    obj = GraphicObject(10, 10, 'yeller')
    
    # Create a few instances of the second class through the interface 
    #    provided by the first class
    obj.createSomeShapes()
    
    # Now attempts to call a method of the subclassed second class
    obj.renderAll()
    

    当我跑步时,我得到:

    $ python main.py
    Init MySubClass
    Shape: circle
    Shape: square
    Shape: octogon
    Traceback (most recent call last):
      File "main.py", line 35, in <module>
        obj.renderAll()
      File "main.py", line 21, in renderAll
        shape.render()
    AttributeError: 'Shape' object has no attribute 'render'
    

    我知道为什么正在发生,但我不知道一种优雅的方法来避免它。

    这里的最佳做法是什么?如何在访问子类方法时保持模块代码可重用?

1 个答案:

答案 0 :(得分:3)

这不是关于模块的问题。即使所有类都在同一个文件中,您也会遇到同样的问题。问题是您的DataObject类是硬编码的,以创建Shape的实例,并且不知道您有子类Shape并且想要使用该子类。有两种方法:

  1. 如果子类要使用自己的自定义Shape子类而不是createShape,则必须覆盖Shape的文档。这很简单有效。一个缺点是,如果它很长并且需要更改的是Shape类的名称,则覆盖整个方法可能很麻烦。另一方面,如果createShape也做了其他通常需要在子类中被覆盖的工作,那么必须覆盖它不是一个大问题。
  2. 通过为DataObject类提供shapeClass类属性来参数化DataObject / Shape关系,并使用它来实例化形状而不是直接引用Shape
  3. 那是:

    class DataObject(object):
        # other stuff...
    
        shapeClass = Shape
    
        def createShape(self, type):
            # here we create an instance of the second class
            new_shape = self.shapeClass(type)
            self.m_shape_list.append(new_shape)
    

    然后你可以在子类中更简洁地覆盖它:

    class GraphicObject(DataObject):
        # other stuff...
    
        shapeClass = MyShape
    

    通过将Shape拉出方法代码并使其成为单独的属性,您允许子类仅覆盖该行为的那一部分。现在,调用someGraphicObject.createShape()会自动创建MyShape

    哪种方法更好取决于您的整体类设计,但上面提到的因素(关于使用参数化类的方法是否可能需要在子类中被覆盖)可能是相关的。