简体   繁体   中英

Python: Inheriting from Built-In Types

I have a question concerning subtypes of built-in types and their constructors. I want a class to inherit both from tuple and from a custom class.

Let me give you the concrete example. I work a lot with graphs, meaning nodes connected with edges. I am starting to do some work on my own graph framework.

There is a class Edge, which has its own attributes and methods. It should also inherit from a class GraphElement. (A GraphElement is every object that has no meaning outside the context of a specific graph.) But at the most basic level, an edge is just a tuple containing two nodes. It would be nice syntactic sugar if you could do the following:

edge = graph.create_edge("Spam","Eggs")
(u, v) = edge

So (u,v) would contain "Spam" and "Eggs". It would also support iteration like

for node in edge: ...

I hope you see why I would want to subtype tuple (or other basic types like set).

So here is my Edge class and its init :

class Edge(GraphElement, tuple):

def __init__(self, graph, (source, target)):
    GraphElement.__init__(self, graph)
    tuple.__init__((source, target))

When i call

Edge(aGraph, (source, target))

I get a TypeError: tuple() takes at most 1 argument (2 given). What am I doing wrong?

Since tuples are immutable, you need to override the __new__ method as well. See http://www.python.org/download/releases/2.2.3/descrintro/#__new__

class GraphElement:
    def __init__(self, graph):
        pass

class Edge(GraphElement, tuple):
    def __new__(cls, graph, (source, target)):
        return tuple.__new__(cls, (source, target))
    def __init__(self, graph, (source, target)):
        GraphElement.__init__(self, graph)

For what you need, I would avoid multiple inheritance and would implement an iterator using generator:

class GraphElement:
    def __init__(self, graph):
        pass

class Edge(GraphElement):
    def __init__(self, graph, (source, target)):
        GraphElement.__init__(self, graph)
        self.source = source
        self.target = target

    def __iter__(self):
        yield self.source
        yield self.target

In this case both usages work just fine:

e = Edge(None, ("Spam","Eggs"))
(s, t) = e
print s, t
for p in e:
    print p

你需要覆盖__new__ - 当前tuple.__new__被调用(因为你没有覆盖它)与你传递给Edge所有参数。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM