3

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?

3 Answers 3

10

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)
Sign up to request clarification or add additional context in comments.

Comments

6

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

1 Comment

I see. That would indeed give me all the syntactic niceties I want. This seems to be the better solution, thanks.
3

You need to override __new__ -- currently tuple.__new__ is getting called (as you don't override it) with all the arguments you're passing to Edge.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.