如何在另一个已加载的类中使用新的扩展类而不是原始类

时间:2013-10-29 20:05:40

标签: python class python-2.7

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

# 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?

3 个答案:

答案 0 :(得分:3)

基本上,如果不修改GraphBFSGraph类,则不能。如果Graph引用Vertex,则引用Vertex,如果不实际更改Graph的代码,则无法引用其他任何内容。也就是说,有三种方法可以做到这一点。

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

第二种,更狡猾,风险更高的方法是将它点缀:

import graph
graph.Vertex = MyNewVertex

现在,任何尝试使用Vertex模块中的graph类的内容都会实际使用您的类。但这是有风险的,因为你永远不知道它会对其他认为使用原始Vertex的代码做什么。 (将你的类命名为不同的东西仍然是一个好主意,否则它会让人混淆,告诉你哪一个被使用。)此外,如果在你的monkeypatch生效之前另一个类导入Vertex,它可能会无声地失败。

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

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相同)。这会增加一个间接层,对于小项目来说可能有点过分,但是如果你有很多相互依赖的类,那么这样做就可以让它们明确地跟踪它们如何相互使用。

答案 1 :(得分:0)

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

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

答案 2 :(得分: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类(或从中派生的任何子类)应该准确记录实现顶点的类需要实现的行为。