Coding an extendable class

T

Tobias Pfeiffer

Hi!

I want to write a package of modules that deal with mathematical
graphs. So my plan is to have a main.py file where the basic operations
are done, like reading the file and creating an adjacence list and all
that. In this main.py file, there is created a Graph class with several
methods, such as deg(v) or delvertex(v).
And then, for all of the other things one can do with a graph, I want
to code other modules, e.g. a file euler.py for finding an Euler way
through the graph, that extends the class by the method eulerway(). I
know that I can do something like

# graphtools/euler.py
import main
class Graph(main.Graph):
def eulerway(self):
[...]

and then the Graph class imported from euler.py will have both the old
and the new methods. But what can I do if I want the end user to be
able to combine several modules and I don't know in which order he will
do? For example, by
import graphtools.main, graphtools.hamilton
I can do the thingy described above, but what is with something like
import graphtools.hamilton, graphtools.euler
? In that case I want the class to have all of the methods defined in
the modules. How do I realize that in the best way?

Bye
Tobias
 
J

Jeff Epler

Python sometimes uses "mixin" classes -- see the SocketServer module for
one example in a core module.

So main.Graph would be the real graph class, and
graphtools.hamilton.HamiltonMixin would be one mixin:
class MyGraph(main.Graph, hamilton.HamiltonMixin):
pass
You could use new.classobj to create a class object with a given list
of mixins at runtime (untested):
# In Main
_graph_classes = []

def add_mixin(m):
_graph_classes.append(m)
global Graph
Graph = new.classobj("Graph", _graph_classes, {})

add_mixin(main._Graph)

# In each module that defines a new mixin
class HamiltonianMixin:
pass
main.add_mixin(HamiltonianMixin)
however, having the exact nature of main.Graph depend on what other
modules have been imported is not what most Python users would expect.
I prefer the solution of having the user list mixins by creating a
"personalized" graph class.

Jeff
 
P

Peter Otten

Tobias said:
I want to write a package of modules that deal with mathematical
graphs. So my plan is to have a main.py file where the basic operations
are done, like reading the file and creating an adjacence list and all
that. In this main.py file, there is created a Graph class with several
methods, such as deg(v) or delvertex(v).
And then, for all of the other things one can do with a graph, I want
to code other modules, e.g. a file euler.py for finding an Euler way
through the graph, that extends the class by the method eulerway(). I
know that I can do something like

# graphtools/euler.py
import main
class Graph(main.Graph):
def eulerway(self):
[...]

and then the Graph class imported from euler.py will have both the old
and the new methods. But what can I do if I want the end user to be
able to combine several modules and I don't know in which order he will
do? For example, by
import graphtools.main, graphtools.hamilton
I can do the thingy described above, but what is with something like
import graphtools.hamilton, graphtools.euler
? In that case I want the class to have all of the methods defined in
the modules. How do I realize that in the best way?

#__init__.py
class Graph(object):
def deg(self, v):
# your code

#euler.py
class EulerMixin(object):
def eulerway(self):
# your code

#hamilton.py
class HamiltonMixin(object):
def whatever(self):
# your code

The above are all in the graphtools package directory.
Now a sample usage:

#application.py
import graphtools
from graphtools import euler, hamilton

class Graph(graphtools.Graph, euler.EulerMixin, hamilton.HamiltonMixin):
pass

The XXXMixin classes are not for standalone use, they should only call but
not override the Graph methods. As long as the different mixins are
orthogonal, i. e. do not affect each other all should be fine.

Alternatively you can derive the hamilton/euler variants from
graphtools.Graph. You must then design the different classes with
cooperation in mind, as demonstrated below with three initialization
routines which are assumed to be run only once:

#__init__.py
class Graph(object):
def __init__(self):
self.init()
def init(self):
print "init main"
def deg(self, v):
print "deg"

#euler.py
import graphtools
class EulerGraph(graphtools.Graph):
def eulerway(self):
print "eulerway"
def init(self):
print "init euler"
super(EulerGraph, self).init()

#hamilton.py
import graphtools
class HamiltonGraph(graphtools.Graph):
def whatever(self):
print "whatever"
def init(self):
print "init hamilton"
super(HamiltonGraph, self).init()

#usegraphtools.py
import graphtools
from graphtools import euler, hamilton


class Graph(euler.EulerGraph, hamilton.HamiltonGraph):
pass

g = Graph()
g.eulerway()
g.whatever()
g.deg(1)

Output:

init euler
init hamilton
init main
eulerway
whatever
deg

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Coding question/help 0
Small Coding Fear 3
Insight on a coding project. 1
Malicious Coding Help Please 5
Neopets coding help 4
Path to success in coding 0
Code is not outputting 0
Low level block coding 0

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top