ベースクラスを派生したクラスPython(またはクラスを拡張するより多くのPythonic方法)にキャスト

StackOverflow https://stackoverflow.com/questions/3464061

質問

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でキャストするように、問題に何らかの考えを与えずに行われるべきではありません。かなり限られた例を投稿しましたが、制約の範囲内にとどまることができれば(動作を追加するだけで、新しいインスタンスVARSなし)、これは問題に対処するのに役立ちます。

他のヒント

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

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) is called
2. If the returned object is a subclass of nx.Graph, 
   __init__ is called on the object
3. The object is returned as the instance

交換します 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()

ほとんどの場合、これはあなたが知る必要があるすべてですが、一つのゴッチャがあります。私たちのオーバーライド __new__ メソッドは影響のみに影響します nx.Graph, 、そのサブクラスではありません。たとえば、電話する場合 nx.gn_graph, 、のインスタンスを返します nx.DiGraph, 、私たちの派手な拡張機能はありません。サブクラスのそれぞれをサブクラス化する必要があります nx.Graph 必要な方法と属性を使用して追加したいこと。使用 ミックスイン 従いながら一貫してサブクラスを簡単に拡張できるようにするかもしれません ドライ 原理。

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

関数がグラフオブジェクトを作成している場合、それらを新しいオブジェクトに変えることはできません。

NewGraphの別のオプションは、グラフではなくグラフを持つことです。グラフメソッドを持っているグラフオブジェクトに委任し、任意のグラフオブジェクトを新しい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):
        ....

簡単なケースでは、サブクラスを書くこともできます __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())

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

単に新しいものを作成することができます 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)

皆さんに試しましたかPython]派生クラスにベースクラスをキャストします

私はそれをテストしました、そしてそれはうまくいくようです。また、この方法は以下の方法よりも少し優れていると思います。 初期化 導出された関数の関数。

c.__class__ = CirclePlus
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top