Networkx pythonパッケージを拡張し、特定のニーズに合わせてGraph
クラスにいくつかのメソッドを追加する必要があります
これを行うことについて私が考えた方法は、単純に新しいクラスNewGraph
を派生させ、必要なメソッドを追加することです。
ただし、networkxには、Graph
オブジェクトを作成して返す(ランダムグラフを生成するなど)関数が他にもいくつかあります。新しいメソッドを使用できるように、これらのGraph
オブジェクトをNewGraph
オブジェクトに変換する必要があります。
これを行う最良の方法は何ですか?または、まったく別の方法で問題に取り組む必要がありますか?
動作を追加するだけで、追加のインスタンス値に依存しない場合は、オブジェクトの__class__
に割り当てることができます。
from math import pi
class Circle(object):
def __init__(self, radius):
self.radius = radius
def area(self):
return pi * self.radius**2
class CirclePlus(Circle):
def diameter(self):
return self.radius*2
def circumference(self):
return self.radius*2*pi
c = Circle(10)
print c.radius
print c.area()
print repr(c)
c.__class__ = CirclePlus
print c.diameter()
print c.circumference()
print repr(c)
プリント:
10
314.159265359
<__main__.Circle object at 0x00A0E270>
20
62.8318530718
<__main__.CirclePlus object at 0x00A0E270>
これはPythonでできる「キャスト」に近いものであり、Cでキャストするのと同じように、問題を考えずに行うことはできません。かなり限定された例を掲載しましたが、制約内に留まることができる場合(動作を追加するだけで、新しいインスタンス変数はありません)、これは問題の解決に役立つ可能性があります。
モジュールに手を触れずに、モジュール内のクラスをカスタムメイドのサブクラスに「魔法のように」置き換える方法を次に示します。これは、通常のサブクラス化手順のほんの数行にすぎないため、サブクラス化の(ほぼ)すべての機能と柔軟性をボーナスとして提供します。たとえば、これにより、必要に応じて新しい属性を追加できます。
_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()
_
ほとんどの場合、これはあなたが知る必要があるすべてですが、1つの落とし穴があります。 ___new__
_メソッドのオーバーライドは、サブクラスではなく_nx.Graph
_にのみ影響します。たとえば、_nx.gn_graph
_を呼び出すと、_nx.DiGraph
_のインスタンスが返されます。これには、特別な拡張機能はありません。処理する_nx.Graph
_の各サブクラスをサブクラス化し、必要なメソッドと属性を追加する必要があります。 mix-ins を使用すると、 [〜#〜] dry [〜#〜] の原則に従いながら、サブクラスを一貫して拡張しやすくなります。
この例は簡単に思えるかもしれませんが、モジュールにフックするこの方法は、発生する可能性のあるすべての小さな問題をカバーする方法で一般化することは困難です。目前の問題に合わせて調整する方が簡単だと思います。たとえば、フックするクラスが独自のカスタム___new__
_メソッドを定義している場合、それを保存してから置き換える前に、_object.__new__
_ではなくこのメソッドを呼び出す必要があります。
単純なケースでは、サブクラスを書くこともできます__init__
このようにして、Graphデータ構造からのポインターをサブクラスデータに割り当てます。
from networkx import Graph
class MyGraph(Graph):
def __init__(self, graph=None, **attr):
if graph is not None:
self.graph = graph.graph # graph attributes
self.node = graph.node # node attributes
self.adj = graph.adj # adjacency dict
else:
self.graph = {} # empty graph attr dict
self.node = {} # empty node attr dict
self.adj = {} # empty adjacency dict
self.Edge = self.adj # alias
self.graph.update(attr) # update any command line attributes
if __name__=='__main__':
import networkx as nx
R=nx.gnp_random_graph(10,0.4)
G=MyGraph(R)
割り当てでcopy()またはdeepcopy()を使用することもできますが、そうしている場合は、
G=MyGraph()
G.add_nodes_from(R)
G.add_edges_from(R.edges())
グラフデータをロードします。
関数がGraphオブジェクトを作成している場合、それらをNewGraphオブジェクトに変換することはできません。
NewGraphのもう1つのオプションは、グラフではなくグラフを持つことです。持っているGraphオブジェクトにGraphメソッドを委任し、任意のGraphオブジェクトを新しいNewGraphオブジェクトにラップできます。
class NewGraph:
def __init__(self, graph):
self.graph = graph
def some_graph_method(self, *args, **kwargs):
return self.graph.some_graph_method(*args, **kwargs)
#.. do this for the other Graph methods you need
def my_newgraph_method(self):
....
独自に定義する前に、NewGraph
オブジェクトから派生した新しいGraph
を作成し、___init__
_関数にself.__dict__.update(vars(incoming_graph))
のようなものを最初の行として含めることができます。プロパティ。このようにして、基本的に、すべてのプロパティをGraph
からGraph
から派生した新しいオブジェクトにコピーしますが、特別なソースを使用します。
_class NewGraph(Graph):
def __init__(self, incoming_graph):
self.__dict__.update(vars(incoming_graph))
# rest of my __init__ code, including properties and such
_
使用法:
_graph = function_that_returns_graph()
new_graph = NewGraph(graph)
cool_result = function_that_takes_new_graph(new_graph)
_