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

2024-09-29 23:29:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我在一个模块中定义了一个使用另一个类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'object has no attribute'color'”,因此我对Vertex所做的更改不会向下传播到Graph,BFSGraph也不会使用扩展的Vertex。你知道吗

如何使Graph和BFSGraph使用新的顶点?你知道吗


Tags: 模块keypyselfreturnobjectinitdef
3条回答

基本上,如果不修改GraphBFSGraph类,就不能这样做。如果Graph引用了Vertex,那么它引用了Vertex,如果不实际更改Graph的代码,就不能让它引用其他任何东西。也就是说,有三种方法可以做到这一点。你知道吗

最简单的解决方案是生成一个Graph的派生版本来覆盖addVertex,这样它就使用了新的Vertex子类。然后使用新类而不是原来的Graph,一切正常。你知道吗

第二种、更狡猾、更危险的方法是monkeypatch:

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)。这增加了一层间接性,对于小型项目来说可能有点过分,但是如果您有很多相互依赖的类,那么让它们显式地跟踪它们需要如何相互使用是非常有用的。你知道吗

Graph中唯一显式引用并依赖于Vertex的部分是addVertex,它使用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

正确的做法是允许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__时,只需使用要使用的graph.Vertex子类调用其父类的__init__。你知道吗

这不是组织代码的唯一方法,但要记住的关键是Graph类(或从中派生的任何子类)应该准确地记录实现vertex的类需要实现的行为。你知道吗

相关问题 更多 >

    热门问题