【问题标题】:difficulty moving class code to modules in python难以将类代码移动到 python 中的模块
【发布时间】:2014-04-23 05:49:26
【问题描述】:

你如何创建有效的可重用模块

  1. 一个模块中的一个类在另一个模块中创建另一个类的实例,并且
  2. 主程序想要使用仅在其中一个模块的子类中的方法?

在类和子类的上下文中,我的目标是:

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

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

这是我遇到的一个例子:

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

我知道为什么会发生这种情况,但我不知道如何优雅地避免它。

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

【问题讨论】:

    标签: python class module subclass modularity


    【解决方案1】:

    这不是关于模块的真正问题。即使所有类都在同一个文件中,您也会遇到同样的问题。问题是您的 DataObject 类被硬编码为创建Shape 的实例,并且不知道您已将Shape 子类化并希望使用该子类。有两种解决方法:

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

    即:

    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

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

    【讨论】:

    • “一个缺点是,如果 createShape 是一个长方法并且您需要更改的只是 Shape 类的名称,那么重写它会很麻烦。”这正是挑战。告诉我更多关于 2 的信息。
    • @WesModes:你看到我在回答中添加的其他内容了吗?
    • 太棒了。你让我开始沿着这些思路思考。但是我在想一些愚蠢的事情,比如创建一个被子类覆盖的单行方法。这要优雅得多。快速回答、优雅和理解我表达不清的目标的要点。谢谢。
    • 当我在DataObject类中设置shapeClass = Shape时,Shape类已经在导入的模块中声明了。但是当我在子类中设置 shapeClass = MyShape 时,它还没有被定义并导致一个恼人的 NameError: name 'MyShape' is not defined。如何在代码中进一步引用类而无需重新排序所有类?
    • @WesModes:最简单的方法是重新排序类,以便需要引用其他类的类比他们需要引用的类晚。你有什么理由不能这样做吗?
    猜你喜欢
    • 1970-01-01
    • 2013-03-18
    • 2012-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 1970-01-01
    相关资源
    最近更新 更多