2010-08-12 40 views
28

我需要延長Networkx Python包,並添加一些方法給Graph類我特別需要鑄造基類派生類的Python(或擴展類的更Python的方式)

我想過做的方式這是簡化派生一個新的類說NewGraph,並添加所需的方法。

然而,networkx中還有其他幾個函數可以創建並返回對象(例如生成一個隨機圖)。我現在需要將這些Graph對象轉換爲NewGraph對象,以便我可以使用我的新方法。

這樣做的最好方法是什麼?還是應該以完全不同的方式解決問題?

回答

39

如果你是剛剛加入的行爲,而不是依賴於額外的實例值,你可以指定對象的__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,如果不考慮這個問題,不要這樣做。我已經發布了一個相當有限的例子,但是如果你能保持約束(只是添加行爲,沒有新的實例變量),那麼這可能有助於解決你的問題。

+1

好的,那麼當你需要添加變量時會發生什麼? – 2015-06-05 15:42:17

+0

您可以在運行時添加/設置實例變量。但請注意,您不會與被CirclePlus __init__添加的實例變量混淆,而您忘記添加實例變量,因爲此鑄造方法會繞過__init__我想?順便說一下,由於Python的類型系統可以被覆蓋,所以這種轉換方法並不總是可行的。 – 2016-03-08 20:01:12

+0

如果您發現您還需要添加實例變量,那麼我認爲您很快就會超越可維護代碼的範圍 - 重新考慮您的設計的時間,可能使用某種形式的遏制和/或委派。 – PaulMcG 2017-03-02 20:40:07

0

如果函數正在創建Graph對象,則不能將它們轉換爲NewGraph對象。

另一種選擇是爲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): 
     .... 
+2

謝謝 我看別的地方,我可以只改變__class__屬性。例如MyRandomGraphObject .__ class__ = NewGraph。它確實有效。不好的做法? – zenna 2010-08-12 01:35:29

12

以下是如何「神奇地」用一個定製的子類替換一個模塊中的類而不用觸摸模塊。這只是普通子類化過程中的一些額外的行,因此可以提供(幾乎)所有子類的強大功能和靈活性作爲獎勵。例如,如果你願意,這可以讓你添加新的屬性。

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.Graphnx.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而不是方法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的子類,並添加所需的方法和屬性。在遵守DRY原則的同時,使用mix-ins可以更容易地一致地擴展子類。

雖然這個例子可能看起來很直截了當,但這種掛鉤到模塊中的方法很難以涵蓋所有可能出現的小問題的方式進行概括。我相信只是針對手頭的問題而定製它更容易。例如,如果您正在接入的類定義了自己的自定義方法__new__,則需要在替換它之前將其存儲,並調用此方法而不是object.__new__

+0

我可以使用內置功能嗎?例如,如果我想將'set'轉換爲'SpecialSet',我可以更改內置的'__new__'行爲嗎? – GrantJ 2014-10-17 17:22:55

+1

@GrantJ這不起作用。大多數python內置函數都是用C實現的,因此不像純Python類那樣可塑。你會得到這個錯誤:'TypeError:不能設置內置/擴展類型'set''的屬性。 – 2014-10-17 21:28:36

0

爲了您的簡單情況,您也可以像這樣編寫子類__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) 

你也可以使用複製()或deepcopy的()中分配,但如果你正在做的,你還不如用

G=MyGraph() 
G.add_nodes_from(R) 
G.add_edges_from(R.edges()) 

來加載圖形數據。

+0

這對我有效。但如何用雙下劃線方法來做到這一點? – GrantJ 2014-10-18 01:38:33

-1

有你們試過 [Python] cast base class to derived class

我已經測試過它,而且似乎它的工作原理。此外,我認爲這種方法比下面的方法好一點,因爲下面的方法不執行init派生函數的函數。

c.__class__ = CirclePlus 
+0

請在發佈相同的解決方案之前閱讀所有現有解決方案。 – wieczorek1990 2017-02-15 16:00:36

0

你可以簡單地創建一個新的NewGraphGraph對象派生,並有__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)