将基类转换为派生类python(或更多pythonic方法的扩展类)

时间:2020-12-01 18:09:38

I need to extend the Networkx python package and add a few methods to the Graph class for my particular need

我需要扩展Networkx python包并向Graph类添加一些方法以满足我的特殊需求

The way I thought about doing this is simplying deriving a new class say NewGraph, and adding the required methods.

我想这样做的方法是简单地派出一个新类说NewGraph,并添加所需的方法。

However there are several other functions in networkx which create and return Graph objects (e.g. generate a random graph). I now need to turn these Graph objects into NewGraph objects so that I can use my new methods.

但是,networkx中还有一些其他函数可以创建和返回Graph对象(例如生成一个随机图)。我现在需要将这些Graph对象转换为NewGraph对象,以便我可以使用我的新方法。

What is the best way of doing this? Or should I be tackling the problem in a completely different manner?

这样做的最佳方式是什么?或者我应该以完全不同的方式解决问题?

6 个解决方案

#1


40  

If you are just adding behavior, and not depending on additional instance values, you can assign to the object's __class__:

如果您只是添加行为,而不依赖于其他实例值,则可以分配给对象的__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)

Prints:

10
314.159265359
<__main__.Circle object at 0x00A0E270>
20
62.8318530718
<__main__.CirclePlus object at 0x00A0E270>

This is as close to a "cast" as you can get in Python, and like casting in C, it is not to be done without giving the matter some thought. I've posted a fairly limited example, but if you can stay within the constraints (just add behavior, no new instance vars), then this might help address your problem.

这与你在Python中可以获得的“演员”非常接近,而且就像在C中进行演员一样,如果不考虑这个问题就不可能完成。我发布了一个相当有限的示例,但如果您可以保持在约束内(只添加行为,没有新的实例变量),那么这可能有助于解决您的问题。

#2


12  

Here's how to "magically" replace a class in a module with a custom-made subclass without touching the module. It's only a few extra lines from a normal subclassing procedure, and therefore gives you (almost) all the power and flexibility of subclassing as a bonus. For instance this allows you to add new attributes, if you wish.

以下是如何“神奇地”使用自定义子类替换模块中的类而不触及模块。它只是普通子类化过程中的一些额外行,因此为您提供(几乎)子类化的所有功能和灵活性作为奖励。例如,如果您愿意,这允许您添加新属性。

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

So far this is exactly like normal subclassing. Now we need to hook this subclass into the networkx module so that all instantiation of nx.Graph results in a NewGraph object instead. Here's what normally happens when you instantiate an nx.Graph object with nx.Graph()

到目前为止,这与正常的子类化完全相同。现在我们需要将这个子类挂钩到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

We will replace nx.Graph.__new__ and make it return NewGraph instead. In it, we call the __new__ method of object instead of the __new__ method of NewGraph, because the latter is just another way of calling the method we're replacing, and would therefore result in endless recursion.

我们将替换nx.Graph .__ new__并使其返回NewGraph。在其中,我们调用对象的__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()

In most cases this is all you need to know, but there is one gotcha. Our overriding of the __new__ method only affects nx.Graph, not its subclasses. For example, if you call nx.gn_graph, which returns an instance of nx.DiGraph, it will have none of our fancy extensions. You need to subclass each of the subclasses of nx.Graph that you wish to work with and add your required methods and attributes. Using mix-ins may make it easier to consistently extend the subclasses while obeying the DRY principle.

在大多数情况下,这是你需要知道的,但有一个问题。我们覆盖__new__方法只影响nx.Graph,而不影响它的子类。例如,如果你调用nx.gn_graph,它返回一个nx.DiGraph的实例,它将没有我们花哨的扩展。您需要子类化您希望使用的nx.Graph的每个子类,并添加所需的方法和属性。使用混合可以更容易在遵循DRY原则的同时一致地扩展子类。

Though this example may seem straightforward enough, this method of hooking into a module is hard to generalize in a way that covers all the little problems that may crop up. I believe it's easier to just tailor it to the problem at hand. For instance, if the class you're hooking into defines its own custom __new__ method, you need to store it before replacing it, and call this method instead of object.__new__.

虽然这个例子看起来很简单,但这种挂钩模块的方法难以概括,涵盖了可能出现的所有小问题。我相信更容易根据手头的问题进行定制。例如,如果您要挂入的类定义了自己的自定义__new__方法,则需要在替换它之前存储它,并调用此方法而不是object .__ new__。

#3


0  

If a function is creating Graph objects, you can't turn them into NewGraph objects.

如果函数正在创建Graph对象,则无法将它们转换为NewGraph对象。

Another option is for NewGraph is to have a Graph rather than being a Graph. You delegate the Graph methods to the Graph object you have, and you can wrap any Graph object into a new NewGraph object:

NewGraph的另一个选择是使用Graph而不是Graph。您将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):
        ....

#4


0  

For your simple case you could also write your subclass __init__ like this and assign the pointers from the Graph data structures to your subclass data.

对于您的简单情况,您也可以像这样编写子类__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)

You could also use copy() or deepcopy() in the assignments but if you are doing that you might as well use

您也可以在分配中使用copy()或deepcopy(),但如果您这样做,您也可以使用

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

to load your graph data.

加载图表数据。

#5


0  

You could simply create a new NewGraph derived from Graph object and have the __init__ function include something like self.__dict__.update(vars(incoming_graph)) as the first line, before you define your own properties. In this way you basically copy all the properties from the Graph you have onto a new object, derived from Graph, but with your special sauce.

您可以简单地创建一个从Graph对象派生的新NewGraph,并在定义自己的属性之前将__init__函数包含类似self .__ dict __。update(vars(incoming_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

Usage:

graph = function_that_returns_graph()
new_graph = NewGraph(graph)
cool_result = function_that_takes_new_graph(new_graph)

#6


-1  

Have you guys tried [Python] cast base class to derived class

你们有没有尝试[Python]将基类转换为派生类

I have tested it, and seems it works. Also I think this method is bit better than below one since below one does not execute init function of derived function.

我测试了它,似乎有效。另外我认为这个方法比下面的方法好一点,因为下面的方法不执行派生函数的init函数。

c.__class__ = CirclePlus

#1


40  

If you are just adding behavior, and not depending on additional instance values, you can assign to the object's __class__:

如果您只是添加行为,而不依赖于其他实例值,则可以分配给对象的__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)

Prints:

10
314.159265359
<__main__.Circle object at 0x00A0E270>
20
62.8318530718
<__main__.CirclePlus object at 0x00A0E270>

This is as close to a "cast" as you can get in Python, and like casting in C, it is not to be done without giving the matter some thought. I've posted a fairly limited example, but if you can stay within the constraints (just add behavior, no new instance vars), then this might help address your problem.

这与你在Python中可以获得的“演员”非常接近,而且就像在C中进行演员一样,如果不考虑这个问题就不可能完成。我发布了一个相当有限的示例,但如果您可以保持在约束内(只添加行为,没有新的实例变量),那么这可能有助于解决您的问题。

#2


12  

Here's how to "magically" replace a class in a module with a custom-made subclass without touching the module. It's only a few extra lines from a normal subclassing procedure, and therefore gives you (almost) all the power and flexibility of subclassing as a bonus. For instance this allows you to add new attributes, if you wish.

以下是如何“神奇地”使用自定义子类替换模块中的类而不触及模块。它只是普通子类化过程中的一些额外行,因此为您提供(几乎)子类化的所有功能和灵活性作为奖励。例如,如果您愿意,这允许您添加新属性。

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

So far this is exactly like normal subclassing. Now we need to hook this subclass into the networkx module so that all instantiation of nx.Graph results in a NewGraph object instead. Here's what normally happens when you instantiate an nx.Graph object with nx.Graph()

到目前为止,这与正常的子类化完全相同。现在我们需要将这个子类挂钩到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

We will replace nx.Graph.__new__ and make it return NewGraph instead. In it, we call the __new__ method of object instead of the __new__ method of NewGraph, because the latter is just another way of calling the method we're replacing, and would therefore result in endless recursion.

我们将替换nx.Graph .__ new__并使其返回NewGraph。在其中,我们调用对象的__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()

In most cases this is all you need to know, but there is one gotcha. Our overriding of the __new__ method only affects nx.Graph, not its subclasses. For example, if you call nx.gn_graph, which returns an instance of nx.DiGraph, it will have none of our fancy extensions. You need to subclass each of the subclasses of nx.Graph that you wish to work with and add your required methods and attributes. Using mix-ins may make it easier to consistently extend the subclasses while obeying the DRY principle.

在大多数情况下,这是你需要知道的,但有一个问题。我们覆盖__new__方法只影响nx.Graph,而不影响它的子类。例如,如果你调用nx.gn_graph,它返回一个nx.DiGraph的实例,它将没有我们花哨的扩展。您需要子类化您希望使用的nx.Graph的每个子类,并添加所需的方法和属性。使用混合可以更容易在遵循DRY原则的同时一致地扩展子类。

Though this example may seem straightforward enough, this method of hooking into a module is hard to generalize in a way that covers all the little problems that may crop up. I believe it's easier to just tailor it to the problem at hand. For instance, if the class you're hooking into defines its own custom __new__ method, you need to store it before replacing it, and call this method instead of object.__new__.

虽然这个例子看起来很简单,但这种挂钩模块的方法难以概括,涵盖了可能出现的所有小问题。我相信更容易根据手头的问题进行定制。例如,如果您要挂入的类定义了自己的自定义__new__方法,则需要在替换它之前存储它,并调用此方法而不是object .__ new__。

#3


0  

If a function is creating Graph objects, you can't turn them into NewGraph objects.

如果函数正在创建Graph对象,则无法将它们转换为NewGraph对象。

Another option is for NewGraph is to have a Graph rather than being a Graph. You delegate the Graph methods to the Graph object you have, and you can wrap any Graph object into a new NewGraph object:

NewGraph的另一个选择是使用Graph而不是Graph。您将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):
        ....

#4


0  

For your simple case you could also write your subclass __init__ like this and assign the pointers from the Graph data structures to your subclass data.

对于您的简单情况,您也可以像这样编写子类__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)

You could also use copy() or deepcopy() in the assignments but if you are doing that you might as well use

您也可以在分配中使用copy()或deepcopy(),但如果您这样做,您也可以使用

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

to load your graph data.

加载图表数据。

#5


0  

You could simply create a new NewGraph derived from Graph object and have the __init__ function include something like self.__dict__.update(vars(incoming_graph)) as the first line, before you define your own properties. In this way you basically copy all the properties from the Graph you have onto a new object, derived from Graph, but with your special sauce.

您可以简单地创建一个从Graph对象派生的新NewGraph,并在定义自己的属性之前将__init__函数包含类似self .__ dict __。update(vars(incoming_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

Usage:

graph = function_that_returns_graph()
new_graph = NewGraph(graph)
cool_result = function_that_takes_new_graph(new_graph)

#6


-1  

Have you guys tried [Python] cast base class to derived class

你们有没有尝试[Python]将基类转换为派生类

I have tested it, and seems it works. Also I think this method is bit better than below one since below one does not execute init function of derived function.

我测试了它,似乎有效。另外我认为这个方法比下面的方法好一点,因为下面的方法不执行派生函数的init函数。

c.__class__ = CirclePlus