0

我有一个模块,我在其中定义了一个使用另一个类 Vertex 的 Graph 类。

# Graph.py
class Graph(object):
    def __init__(self):
        self.vertList = {}
        self.numVertices = 0 

    def addVertex(self,key):
        self.numVertices += 1
        newVert = Vertex(key)
        self.vertList[key] = newVert
        return newVert

    def getVertex(self,k):
        if k in self.vertList:
            return self.vertList[k]
        else:
            return None

class Vertex(object):
    def __init__(self,key):
        self.id = key
        self.connectedTo = {}

我想扩展 Vertex 类以在另一个模块中使用:

# BFSGraph.py
from Graph import Vertex,Graph

class Vertex(Vertex):
    def __init__(self,key):
        super(Vertex,self).__init__(key)

        # extensions for BFS
        self.predecessor = None
        self.dist = 0 
        self.color = 'w' # white, grey, and black

class BFSGraph(Graph):
    def getColor(self,k):
        return self.getVertex(k).color

def test():
    g=BFSGraph()
    g.addVertex('a')
    g.getColor('a')

当我运行测试例程时,它返回“'Vertex' 对象没有属性 'color'”,因此我对 Vertex 所做的更改不会传播到 Graph,并且 BFSGraph 没有使用扩展的 Vertex。

如何让 Graph 和 BFSGraph 使用新的 Vertex?

4

3 回答 3

3

基本上,你不能不修改GraphandBFSGraph类。如果Graph引用Vertex,它引用Vertex,并且你不能让它引用其他任何东西而不实际更改Graph. 也就是说,有三种方法可以做这样的事情。

最简单的解决方案是制作Graph该覆盖的派生版本,addVertex以便它使用您的新Vertex子类。然后你使用那​​个新类而不是原来的类Graph,一切都很好。

第二种更偷偷摸摸且风险更大的方法是对其进行猴子补丁:

import graph
graph.Vertex = MyNewVertex

现在,任何试图使用模块中的Vertex类的东西graph实际上都会使用你的类。但是,这是有风险的,因为您永远不知道它会对其他认为它使用原始Vertex. (将您的类命名为不同的名称仍然是一个好主意,否则会混淆正在使用哪个类。)此外,如果Vertex在您的 monkeypatch 生效之前导入了另一个类,它可能会默默地无法工作。

如果您正在设计整个代码库并且确实需要做很多事情,那么更大规模的解决方案是将顶点参数化为类的一部分。这使得编写您自己的相互操作的派生类变得更加容易。也就是说,您执行以下操作:

class Graph(object):
    vertexClass = Vertex

    def addVertex(self, key):
        # ...
        newVert = self.vertexClass(key)
        # etc.

    # etc.

然后,如果您需要,您可以稍后执行:

class MyVertex(Vertex):
     # blah

class MyGraph(Graph):
    vertexClass = MyVertex

这个想法是您使用一个类变量,以便 Graph 类知道将哪个类用于其顶点。然后,您可以轻松地创建一个只更改此变量的派生类,而无需重写所有实际方法代码(假设您确实在 MyVertex 类中保持 API 相同)。这增加了一层间接性,对于小型项目来说可能有点矫枉过正,但如果你有很多相互依赖的类,这样做让它们明确地跟踪它们需要如何相互使用会很有用。

于 2013-10-29T20:10:06.433 回答
0

唯一Graph明确引用 & 依赖的部分VertexaddVertex,它使用Vertex的构造函数来创建一个对象,然后将其填充到字典中。我建议进行更改addVertex,以便将Vertex对象作为参数添加,从而使调用者进行构造并让他们确定Vertex要使用的类,例如:

def addVertex(self, key, newVert):
    self.numVertices += 1
    self.vertList[key] = newVert
    return newVert

或者,为了减少多余的参数,只需简单地添加Vertex

def addVertex(self, newVert):
    self.numVertices += 1
    self.vertList[newVert.getKey()] = newVert
    return newVert
于 2013-10-29T20:14:25.747 回答
0

正确的做法是允许您的Graph类将用户定义的类作为参数,并使用它来表示顶点。您的graph模块可以提供合适的默认值。

graph.py(模块应以小写字母开头):

class Vertex(object):
    def __init__(self,key):
        self.id = key
        self.connectedTo = {}

class Graph(object):
    def __init__(self, vertexclass=Vertex):
        self.vertList = {}
        self.numVertices = 0 
        self.vertexclass = vertexclass

    def addVertex(self,key):
        self.numVertices += 1
        newVert = self.vertexclass(key)
        self.vertList[key] = newVert
        return newVert

    def getVertex(self,k):
        if k in self.vertList:
            return self.vertList[k]
        else:
            return None

唯一的变化是它Graph.__init__有一个默认值为普通Vertex类的参数,允许您在创建Graph实例时传递不同的类。该类已保存,您可以在每次addVertex调用时使用它来创建新顶点。

然后,在要使用自定义顶点类的另一个模块或脚本中:

#!/usr/bin/python
import graph

class MyVertex(graph.Vertex):
    def __init__(self,key):
        super(Vertex,self).__init__(key)

        # extensions for BFS
        self.predecessor = None
        self.dist = 0 
        self.color = 'w' # white, grey, and black

class BFSGraph(Graph):

    def __init__(self):
        super(BFSGraph, self).__init__(MyVertex)

    def getColor(self,k):
        return self.getVertex(k).color


def test():
    g=BFSGraph()
    g.addVertex('a')
    g.getColor('a')    

你的BFSGraph.__init__,当被调用时,只是用你想要使用__init__的子类调用它的父类。graph.Vertex

这不是组织代码的唯一方法,但要记住的关键是您的Graph类(或从它派生的任何子类)应准确记录实现顶点的类需要实现的行为。

于 2013-10-29T20:23:09.080 回答