Most programming languages provide built-in data types (e.g., integers, floating point numbers, character strings), and many provide capabilities for adding new user-defined data types. Object-oriented (OO) languages allow programmers to define new data types that have behavior, i.e., functionality associated with them. Many OO languages - including Python - have a notion of a class, which is used to define new types of objects.
class
class
keyword to define this new type:
class UndirectedGraph:
# define undirected graph class within this block
# by the way, Python comments begin with a pound sign #
# also note that code blocks need to be consistently indented
def
def
(short for define). To our
UndirectedGraph class, we can begin to define methods to add nodes and
edges:
class UndirectedGraph:
def AddNode(self, node):
# provide code to add a node to this instance of a graph
# (which we call, by convention, "self")
def AddEdge(self, node1, node2):
# provide code to add an edge connecting node1 and node2
self
self
argument in the methods above may seem confusing.
In those methods, self
refers to a particular instance of the UndirectedGraph
class on which a method is being called.
Let's say we have two graphs, named g1 and g2. The following happens
when we execute methods on these graphs:
g1.AddNode(12) means UndirectedGraph.AddNode(g1, 12) # g1 is self, 12 is node
g2.AddNode('hello') means UndirectedGraph.AddNode(g2, 'hello') # g2 is self, 'hello' is node
There is nothing special about the word "self", however; i.e., it is not a
Python keyword. Any name would serve to identify the class instance in
question; "self" is merely used by convention.
If you were using def
to define a standalone
function that was not attached to a class definition, then you would not
use the self
argument.
If you forget to provide the self argument in a method definition,
you will see syntax errors that you have not provided the
correct number of arguments.
__init__
__init__
method, for example, is used to define a
constructor method for a class, specifying how a new instance of a class
is initialized.
class UndirectedGraph:
def __init__(self):
# provide code to initialize a new UndirectedGraph instance
A new instance g
of an UndirectedGraph would be constructed
as such: g = UndirectedGraph()
Lists and dictionaries are two built-in container data types in Python (i.e., those that can hold other data types), and form the basis of many more complicated objects.
list.append(newitem)
[]
operator; starts counting
at 0
alist = [3, 5.0, 'abc', [1, 2, 3]]
print alist[2], alist[3][0] # prints 'abc', 1
[]
as lists
adict = {} # make an empty dictionary
adict['a'] = 1 # associate key 'a' with value 1
print alist['a'] # prints 1
Think about what we need to be able to represent an UndirectedGraph. Sometimes graphs are represented via adjacency matrices, where a matrix element m[i][j] is 1 if nodes i and j are linked, and 0 otherwise. For graphs that are sparsely connected, however, most matrix elements are 0 and much storage is wasted. Another approach would be to hold a list of all pairs of nodes that define edges in a graph; but then a node with many edges is represented many times, and finding all the nodes connected to a given node is cumbersome. We will instead use dictionaries and lists to represent a graph: a dictionary can be used to associate a node ID with a list of neighboring node IDs:
class UndirectedGraph:
def __init__(self):
self.neighbor_dict = {} # create an empty neighbor_dict dictionary
# AddNode and AddEdge need to populate the dictionary
It should be noted that a dictionary which associates a key (node1) with a value (node2) implies a directionality of the edge. For an undirected graph, we should equally well be able to identify node1 as a neighbor of node2. Object-oriented programming (OOP) lets us encapsulate internal details of how an object is implemented without affecting the external interfaces describing how an object behaves.
Modules are units of Python code that can be used by other units, and form
the building blocks of larger Python programs.
Each module has a namespace, identifying the set of names
(of variables, functions, classes, modules, etc.) available
therein. One module becomes available for use within another when it is
imported with an import
statement. In the network problem,
you will be creating a module implementing an UndirectedGraph. Fortunately,
we have provided you with a separate module for displaying these graphs,
implemented in file NetGraphics.py.
All you need to do is import
that module in order to access the graph drawing functions defined there:
import NetGraphics # import module, but without the ".py" suffix
NetGraphics.DisplayCircleGraph(g) # display graph g, laid out in a circle