55

Networkx pythonパッケージを拡張しGraph、特定のニーズに合わせてクラスにいくつかのメソッドを追加する必要があります

これを行うことについて私が考えた方法は、単に新しいクラスsayNewGraphを派生させ、必要なメソッドを追加することです。

Graphただし、networkxには、オブジェクトを作成して返す(たとえば、ランダムグラフを生成する)他の関数がいくつかあります。新しいメソッドを使用できるように、これらのGraphオブジェクトをオブジェクトに変換する必要があります。NewGraph

これを行うための最良の方法は何ですか?それとも、まったく別の方法で問題に取り組む必要がありますか?

4

9 に答える 9

81

動作を追加するだけで、追加のインスタンス値に依存しない場合は、オブジェクトの__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でのキャストと同様に、問題を考慮せずに実行することはできません。かなり限定された例を投稿しましたが、制約の範囲内にとどまることができる場合(動作を追加するだけで、新しいインスタンス変数はありません)、これは問題の解決に役立つ可能性があります。

于 2010-08-12T01:39:13.813 に答える
15

モジュールに触れることなく、モジュール内のクラスをカスタムメイドのサブクラスに「魔法のように」置き換える方法は次のとおりです。これは通常のサブクラス化手順からほんの数行余分にあるため、ボーナスとしてサブクラス化の(ほぼ)すべてのパワーと柔軟性を提供します。たとえば、これにより、必要に応じて新しい属性を追加できます。

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ます。その中で、の__new__メソッドのobject代わりにの__new__メソッドを呼び出しますNewGraph。後者は、置き換えるメソッドを呼び出す別の方法であり、したがって、無限の再帰が発生するためです。

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し、必要なメソッドと属性を追加する必要があります。ミックスインを使用すると、 DRYの原則に従いながら、サブクラスを一貫して拡張することが容易になる場合があります。

この例は簡単に思えるかもしれませんが、モジュールにフックするこの方法は、発生する可能性のあるすべての小さな問題をカバーする方法で一般化するのは困難です。手元の問題に合わせて調整する方が簡単だと思います。たとえば、フックしているクラスが独自のカスタム__new__メソッドを定義している場合、それを置き換える前にそれを格納し、の代わりにこのメソッドを呼び出す必要がありますobject.__new__

于 2011-01-17T15:19:12.437 に答える
2

PaulMcGが行ったことを拡張し、ファクトリパターンにしました。

class A:
 def __init__(self, variable):
    self.a = 10
    self.a_variable = variable

 def do_something(self):
    print("do something A")


class B(A):

 def __init__(self, variable=None):
    super().__init__(variable)
    self.b = 15

 @classmethod
 def from_A(cls, a: A):
    # Create new b_obj
    b_obj = cls()
    # Copy all values of A to B
    # It does not have any problem since they have common template
    for key, value in a.__dict__.items():
        b_obj.__dict__[key] = value
    return b_obj

if __name__ == "__main__":
 a = A(variable="something")
 b = B.from_A(a=a)
 print(a.__dict__)
 print(b.__dict__)
 b.do_something()
 print(type(b))

結果:

{'a': 10, 'a_variable': 'something'}
{'a': 10, 'a_variable': 'something', 'b': 15}
do something A
<class '__main__.B'>
于 2020-12-19T16:01:03.487 に答える
0

単純なケースでは__init__、このようにサブクラスを記述し、グラフデータ構造からサブクラスデータにポインターを割り当てることもできます。

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())

グラフデータをロードします。

于 2011-01-17T23:06:11.247 に答える
0

関数が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):
        ....
于 2010-08-12T01:23:31.590 に答える
0

独自のプロパティを定義する前に、オブジェクトNewGraphから派生した新しいものを作成し、関数に最初の行のようなものを含めることができます。このようにして、基本的に、所有しているすべてのプロパティを、から派生した新しいオブジェクトにコピーしますが、特別なソースを使用します。Graph__init__self.__dict__.update(vars(incoming_graph))GraphGraph

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)
于 2017-06-06T20:48:41.283 に答える
0

に貢献するときに同じ質問に遭遇しました。networkxこれは、の多くの新しいメソッドが必要だからですGraph。@Aricによる答えは最も簡単な解決策ですが、継承は使用されません。ここでは、ネイティブnetworkx機能が利用されており、より効率的であるはずです。

チュートリアルには、グラフコンストラクターを使用したセクションnetworkxがあり、グラフGraphの既存のオブジェクト、特に別のグラフオブジェクトからオブジェクトを初期化する方法を示しています。これはそこに示されている例です。既存のオブジェクトから新しいDiGraphオブジェクトを初期化できます。HGraphG

>>> G = Graph()
>>> G.add_edge(1, 2)
>>> H = nx.DiGraph(G)   # create a DiGraph using the connections from G
>>> list(H.edges())
[(1, 2), (2, 1)]

既存のグラフを有向グラフに変換するときの数学的意味に注意してください。この機能は、関数やコンストラクターを介して実現できる可能性がありますが、の重要な機能だと思いますnetworkx。それらの実装をチェックしていませんが、私はそれがより効率的だと思います。

この機能をNewGraphクラスで保持するには、次のように、既存のオブジェクトを引数として使用できるようにする必要があります__init__

from typing import Optional
import networkx as nx


class NewGraph(nx.Graph):

    def __init__(self, g: Optional[nx.Graph] = None):
        """Init an empty directed graph or from an existing graph.

        Args:
            g: an existing graph.
        """
        if not g:
            super().__init__()
        else:
            super().__init__(g)

次に、オブジェクトがあるときはいつでも、次の方法でオブジェクトを初期Graph化できます(直接オブジェクトに向けないでください)。NewGraph

>>> G = nx.some_function()
...
>>> NG = NewGraph(G)

NewGraphまたは、空のオブジェクトを初期化できます。

>>> NG_2 = NewGraph()

Graph同じ理由で、次の場所から別のオブジェクトを初期化できますNG

>>> G_2 = nx.Graph(NG)

ほとんどの場合、オブジェクトをsuper().__init__()開始した後は多くの操作が行われるため、 @ PaulMcGによる回答は、彼/彼女が述べたように、そのような状況ではお勧めできません。NewGraph

于 2021-05-24T10:49:56.593 に答える
-1

__class__代入アプローチは実際に変数を変更します。スーパークラスから関数を呼び出したいだけの場合は、を使用できますsuper。例えば:

class A:
    def __init__(self):
        pass
    def f(self):
        print("A")

class B(A):
    def __init__(self):
        super().__init__()
    def f(self):
        print("B")

b = B()
b.f()
super(type(b), b).f()

戻ってきました

B
A
于 2020-09-16T11:42:20.943 に答える
-3

[Python]キャスト基本クラスを派生クラスに試してみましたか

私はそれをテストしました、そしてそれはうまくいくようです。また、この方法は、派生関数のinit関数を実行しないため、以下の方法よりも少し優れていると思います。

c.__class__ = CirclePlus
于 2013-01-25T00:05:46.370 に答える