以下是如何在不触及模块的情况下“神奇地”用定制的子类替换模块中的类。它只是正常子类化过程中的几行额外代码,因此(几乎)为您提供了子类化的所有功能和灵活性作为奖励。例如,如果您愿意,这允许您添加新属性。
import networkx as nx
class NewGraph(nx.Graph):
def __getattribute__(self, attr):
"This is just to show off, not needed"
print "getattribute %s" % (attr,)
return nx.Graph.__getattribute__(self, attr)
def __setattr__(self, attr, value):
"More showing off."
print " setattr %s = %r" % (attr, value)
return nx.Graph.__setattr__(self, attr, value)
def plot(self):
"A convenience method"
import matplotlib.pyplot as plt
nx.draw(self)
plt.show()
到目前为止,这与正常的子类化完全一样。现在我们需要将这个子类挂接到networkx 模块中,这样nx.Graph 的所有实例化都会生成NewGraph 对象。以下是使用 nx.Graph() 实例化 nx.Graph 对象时通常会发生的情况
1. nx.Graph.__new__(nx.Graph) 被调用
2.如果返回的对象是nx.Graph的子类,
__init__ 在对象上被调用
3.对象作为实例返回
我们将替换 nx.Graph.__new__ 并使其返回 NewGraph。其中,我们调用object 的__new__ 方法而不是NewGraph 的__new__ 方法,因为后者只是调用我们要替换的方法的另一种方式,因此会导致无限递归。
def __new__(cls):
if cls == nx.Graph:
return object.__new__(NewGraph)
return object.__new__(cls)
# We substitute the __new__ method of the nx.Graph class
# with our own.
nx.Graph.__new__ = staticmethod(__new__)
# Test if it works
graph = nx.generators.random_graphs.fast_gnp_random_graph(7, 0.6)
graph.plot()
在大多数情况下,这就是您需要知道的全部内容,但有一个问题。我们对__new__ 方法的覆盖只影响nx.Graph,而不影响它的子类。例如,如果您调用nx.gn_graph,它返回一个nx.DiGraph 的实例,它将没有我们花哨的扩展。您需要对您希望使用的nx.Graph 的每个子类进行子类化,并添加所需的方法和属性。使用mix-ins 可以更容易地一致地扩展子类,同时遵守DRY 原则。
虽然这个例子看起来很简单,但这种连接到模块的方法很难概括为涵盖所有可能出现的小问题。我相信根据手头的问题对其进行调整会更容易。例如,如果您要挂钩的类定义了自己的自定义 __new__ 方法,则需要在替换它之前将其存储,并调用此方法而不是 object.__new__。