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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3464061/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 11:12:33  来源:igfitidea点击:

Cast base class to derived class python (or more pythonic way of extending classes)

pythoninheritancederived-classbase-class

提问by zenna

I need to extend the Networkx python package and add a few methods to the Graphclass 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.

我考虑这样做的方法是简单地派生一个新类 say NewGraph,并添加所需的方法。

However there are several other functions in networkx which create and return Graphobjects (e.g. generate a random graph). I now need to turn these Graphobjects into NewGraphobjects 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?

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

采纳答案by PaulMcG

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 中进行强制转换一样,如果不考虑这个问题就不能完成。我已经发布了一个相当有限的示例,但是如果您可以保持在约束范围内(只需添加行为,没有新的实例变量),那么这可能有助于解决您的问题。

回答by Ned Batchelder

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):
        ....

回答by Lauritz V. Thaulow

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 networkxmodule so that all instantiation of nx.Graphresults in a NewGraphobject instead. Here's what normally happens when you instantiate an nx.Graphobject 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 NewGraphinstead. In it, we call the __new__method of objectinstead 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__方法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()

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.Graphthat you wish to work with and add your required methods and attributes. Using mix-insmay make it easier to consistently extend the subclasses while obeying the DRYprinciple.

在大多数情况下,这就是您需要知道的全部内容,但有一个问题。我们对该__new__方法的覆盖仅影响nx.Graph,而不影响其子类。例如,如果您调用nx.gn_graph,它返回 的实例nx.DiGraph,它将没有我们花哨的扩展。您需要对nx.Graph您希望使用的每个子类进行子类化,并添加所需的方法和属性。使用mix-ins可以更容易地在遵守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__.

回答by Aric

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.

加载您的图形数据。

回答by YuanZheCSYZ

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 initfunction of derived function.

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

c.__class__ = CirclePlus

回答by cjbarth

You could simply create a new NewGraphderived from Graphobject 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 Graphyou have onto a new object, derived from Graph, but with your special sauce.

在定义自己的属性之前,您可以简单地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

Usage:

用法:

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