python实现的一个抽象图像类

From , 3 Years ago, written in Python, viewed 208 times.
URL https://pastebin.vip/view/a58337d1
  1. #!/usr/bin/env python
  2. # (c) Mark Janssen, this file is licensed under the GNU General Public License v2 found at <http://www.gnu.org/licenses>
  3. # email: dreamingforward@gmail.com
  4.  
  5. """Graph class."""
  6.  
  7. #change a lot of these for loops to use faster map() function (see FAQ and QuickReference)
  8. #also: map/reduce/filter now work with any iterable object (including dictionaries!)
  9. #add persistence
  10. #XXX Use of exceptions for control flow may prevent seeing actual errors.  Perhaps catch exception, plus error string and assert string is as expected
  11.  
  12. from defdict import *
  13.  
  14. #EdgeBaseType = int
  15. VertexBaseType = dict
  16. GraphBaseType = defdict
  17.  
  18. _DEBUG = True
  19. _PROFILE = True
  20.  
  21. NumberType = (int, float, long, complex) #for WVertex validation
  22.  
  23. #should all non-verb methods (sum_in, in_degree, etc.) be properties??
  24.  
  25.  
  26. class VertexCommon(VertexBaseType):
  27.     """Various common vertex methods."""
  28.     #Add id property to determine id, given Vertex
  29.     #XXX should clear() also remove in_vertices()?
  30.  
  31.     __slots__ = ['_graph', '_id']  #functioning as a sort of "variable declaration" list
  32.  
  33.     EDGEVALUE = 1
  34.  
  35.     def __init__(self, graph, id, init={}):
  36.         """Create a vertex object in graph.  Assumes id already in graph.
  37.        Will add tails in init as necessary."""
  38.         self._graph = graph  #graph to which this vertex belongs
  39.         self._id = id
  40.         super(VertexCommon, self).__init__()
  41.         self.update(init)
  42.  
  43.     def update(self, tails, edge_value=EDGEVALUE): #XXX limit tails to list or dict?
  44.         """Add tails in sequence or mapping type to Vertex."""
  45.         if isinstance(tails, dict):
  46.             for key, value in tails.iteritems(): #derived classes may override setitem so don't use dict.update
  47.                 self[key] = value
  48.         else:  #given iterable
  49.             for key in tails:
  50.                 self[key] = edge_value
  51.  
  52.     add = update
  53.  
  54.     def discard(self, tail):
  55.         """Removes tail(s) if present, otherwise does nothing.
  56.  
  57.        >>> g = Graph()
  58.        >>> g.add(5, range(5))
  59.        >>> print g[5]
  60.        {0, 1, 2, 3, 4}
  61.        >>> g[5].discard(3)     #remove single edge
  62.        >>> g[5].discard(90)    #discard of non-existent edge ignored
  63.        >>> g[5].discard([0, 1, 90]) #discard multiple edges
  64.        >>> print g[5]
  65.        {2, 4}
  66.        """
  67.         try: #single tail removal
  68.             del self[tail]
  69.         except LookupError:  return     #ignore non-existent tails
  70.         except TypeError, error: #must have been given a tail list
  71.             if not isinstance(tail, list): raise TypeError(error)
  72.             for t in tail:  #XXX inefficient if self is near empty
  73.                 try:
  74.                     del self[t]
  75.                 except LookupError:
  76.                     if not self: break  #good place to check if self is empty yet...
  77.  
  78.     def in_vertices(self):  #O(n)
  79.         """Return iterator over the vertices that point to self.
  80.  
  81.        >>> g = Graph()
  82.        >>> g.add(1, [2, 3, 4])
  83.        >>> g.add(2, [3, 2])
  84.        >>> list(g[2].in_vertices())      #XXX arbitrary order
  85.        [1, 2]
  86.        """
  87.         for head in self._graph.itervalues():
  88.             if self._id in head:
  89.                 yield head._id
  90.  
  91.     def in_degree(self):    #O(n)
  92.         """Return number of edges pointing into vertex.
  93.  
  94.        >>> g = Graph()
  95.        >>> g.add(1, [2, 3, 4])
  96.        >>> g.add(2, [3, 2])
  97.        >>> g[1].in_degree(), g[2].in_degree(), g[4].in_degree()
  98.        (0, 2, 1)
  99.        """
  100.         return len(list(self.in_vertices()))
  101.  
  102.     out_vertices = VertexBaseType.iterkeys
  103.     out_degree = VertexBaseType.__len__
  104.  
  105.     def __getitem__(self, tail):
  106.         """Return edge value or False if tail non-existent.
  107.  
  108.        >>> g = Graph(VertexType=WVertex)
  109.        >>> g[1][3]
  110.        False
  111.        >>> g.add(1,3)
  112.        >>> g[1][3]
  113.        1
  114.        """
  115.         return dict.get(self, tail, False)
  116.  
  117.     def __setitem__(self, tail, value):
  118.         """Set edge value.  If tail does not exist, it is created and added to graph
  119.        if necessary.
  120.  
  121.        >>> g = Graph(VertexType=WVertex)
  122.        >>> g[1][2] = 1
  123.        >>> g[1][3] += 1    #new tail (3): starts as value False (0), now add 1.
  124.        >>> print g
  125.        {1: {2: 1, 3: 1}, 2: {}, 3: {}}
  126.        """
  127.         super(VertexCommon, self).__setitem__(tail, value)
  128.         if tail not in self._graph and tail!=self._id: #XXX ?do this first to preserve invariants in case vertex addition fails
  129.             self._graph.add(tail)
  130.  
  131.     def copy(self): raise NotImplementedError
  132.  
  133.     def __str__(self):
  134.         """Return string of tail vertices with edge weight values.
  135.  
  136.        >>> g = Graph(VertexType=WVertex)
  137.        >>> g.add(1, [1, 3, 4]); g.add(1, 3, 7)
  138.        >>> print g[1]
  139.        {1: 1, 3: 7, 4: 1}
  140.        """
  141.         if _DEBUG: self._validate()
  142.         if not self: return '{}'    #nothing to sort
  143.         keys = self.keys()
  144.         keys.sort()
  145.         return '{%s}' % ', '.join(["%r: %r" % (k, self[k]) for k in keys])
  146.  
  147.     def _validate(self):
  148.         """Assert Vertex invariants.
  149.  
  150.        >>> g = Graph()
  151.        >>> g.add(1,2)
  152.        >>> dict.__setitem__(g[1], 3, 1)  #tail value 3 not in graph
  153.        >>> g[1]._validate()
  154.        Traceback (most recent call last):
  155.        AssertionError: Non-existant tail 3 in vertex 1
  156.        >>> g.add(3,1)
  157.        >>> g[3]._id = 2
  158.        >>> g._validate()  #should call vertex validates too
  159.        Traceback (most recent call last):
  160.        AssertionError: _graph[_id] is not self
  161.        """
  162.         hash(self._id) #id should be hashable
  163.         assert isinstance(self._graph, Graph), "_graph attribute not a Graph"
  164.         assert self._graph[self._id] is self,  "_graph[_id] is not self"
  165.         for t in self:
  166.             assert t in self._graph, "Non-existant tail %r in vertex %r" % (t, self._id)
  167.  
  168.  
  169. class ReverseEdgeMixin(object): #could be used to make undirected graph?
  170.     """Mixin to allow O(1) access to in_vertices.  Inherit instead of or before VertexCommon."""
  171.     #XXX need doctests, also investigate slots issue (multiple bases have instance layout conflict)
  172.  
  173.     __slots__ = []
  174.  
  175.     def __init__(self, *args):
  176.         self.reverse = dict() #ReverseType()?
  177.         super(ReverseEdgeMixin, self).__init__(*args)
  178.  
  179.     def in_vertices(self):
  180.         """Return iterator over the vertices that point to self.
  181.  
  182.        >>> class myvertex(ReverseEdgeMixin, Vertex):
  183.        ...    pass
  184.        >>> g = Graph(VertexType=myvertex)
  185.        >>> g[1].add([2, 3, 4])
  186.        >>> g[2].add([3, 2])
  187.        >>> list(g[2].in_vertices())      #XXX arbitrary order
  188.        [1, 2]
  189.  
  190.        Can also use:
  191.        >>> list(g[2].reverse)
  192.        [1, 2]
  193.        """
  194.         return self.reverse.iterkeys()
  195.  
  196.     def in_degree(self):
  197.         """Return number of edges pointing into vertex.
  198.  
  199.        >>> class myvertex(ReverseEdgeMixin, Vertex):
  200.        ...    pass
  201.        >>> g = Graph(VertexType=myvertex)
  202.        >>> g[1].add([2, 3, 4])
  203.        >>> g[2].add([3, 2])
  204.        >>> g[1].in_degree(), g[2].in_degree(), g[4].in_degree()
  205.        (0, 2, 1)
  206.  
  207.        Can also use:
  208.        >>> len(g[1].reverse), len(g[2].reverse), len(g[4].reverse)
  209.        (0, 2, 1)
  210.        """
  211.         return len(self.reverse)
  212.  
  213.     def sum_in(self):
  214.         """Return sum of all edges that point to vertex.
  215.  
  216.        >>> class myvertex(ReverseEdgeMixin, WVertex):
  217.        ...    pass
  218.        >>> g = Graph(VertexType=myvertex)
  219.        >>> g[1].add([1, 2, 3])
  220.        >>> g[4][1] += 3
  221.        >>> g[1].sum_in(), g[3].sum_in(), g[4].sum_in()
  222.        (4, 1, 0)
  223.        """
  224.         return sum(self.reverse.itervalues())
  225.  
  226.     def __setitem__(self, tail, value):
  227.         """Set edge capacity.  Will also update other vertex.reverse.
  228.  
  229.        >>> class myvertex(ReverseEdgeMixin, WVertex):
  230.        ...    pass
  231.        >>> g = Graph(VertexType=myvertex)
  232.        >>> g[1][2] = 3
  233.        >>> g[1], g[2].reverse
  234.        ({2: 3}, {1: 3})
  235.        """
  236.         #print tail, value
  237.         super(ReverseEdgeMixin, self).__setitem__(tail, value)
  238.         value = self[tail] #value may have been modified by other classes
  239.         if tail != self._id:  #creates tail vertex so check value first
  240.             self._graph[tail].reverse[self._id] = value
  241.             #print self._graph[tail].reverse
  242.         else:
  243.             self.reverse[self._id] = value
  244.  
  245.     def __delitem__(self, tail):
  246.         """Removes tail from self and self._id from tail.reverse.
  247.  
  248.        >>> class myvertex(ReverseEdgeMixin, WVertex):
  249.        ...    pass
  250.        >>> g = Graph(VertexType=myvertex)
  251.        >>> g[1][2] = 3
  252.        >>> g[2].reverse
  253.        {1: 3}
  254.        >>> del g[1][2]
  255.        >>> g[2].reverse
  256.        {}
  257.        """
  258.         if tail in self._graph:
  259.             del self._graph[tail].reverse[self._id]  #creates tail vertex so check if tail in graph first
  260.         super(ReverseEdgeMixin, self).__delitem__(tail)
  261.  
  262.     def clear(self): #XXX should this clear self.reverse also?
  263.         """Removes all tails from self and all references to self._id in tail.reverse
  264.  
  265.        >>> class myvertex(ReverseEdgeMixin, Vertex):
  266.        ...    pass
  267.        >>> g = Graph({1: {1: 1, 2: 4, 3: 9}, 2: {3: 8}}, myvertex)
  268.        >>> g[1].clear()
  269.        >>> g[1].reverse, g[2].reverse, g[3].reverse
  270.        ({}, {}, {2: True})
  271.        """
  272.         g, vid = self._graph, self._id
  273.         for tail in self:
  274.             del g[tail].reverse[vid]
  275.         super(ReverseEdgeMixin, self).clear()
  276.  
  277.     def _validate(self):
  278.         super(ReverseEdgeMixin, self)._validate()
  279.         for tail in self:
  280.             assert self._graph[tail].reverse[self._id] == self[tail]
  281.  
  282.  
  283. class WVertex(VertexCommon):
  284.     """WVertex has directed, weighted edges."""
  285.  
  286.     __slots__ = []
  287.  
  288.     def sum_in(self):
  289.         """Return sum of all edges that point to vertex.
  290.  
  291.        >>> g = Graph(VertexType=WVertex)
  292.        >>> g.add(1, [1, 2, 3])
  293.        >>> g.add(4, 1, 3)
  294.        >>> g[1].sum_in(), g[3].sum_in(), g[4].sum_in()
  295.        (4, 1, 0)
  296.        """
  297.         g, t = self._graph, self._id
  298.         sum = 0
  299.         for h in self.in_vertices():
  300.             sum += g[h][t]
  301.         return sum
  302.  
  303.     def sum_out(self):
  304.         """Return sum of all edges that leave from vertex.
  305.  
  306.        >>> g = Graph(VertexType=WVertex)
  307.        >>> g.add(1, [1, 2, 3])
  308.        >>> g.add(1, 4, 3)
  309.        >>> g[1].sum_out(), g[2].sum_out()
  310.        (6, 0)
  311.        """
  312.         return sum(self.itervalues())
  313.  
  314.     def _validate(self):
  315.         super(WVertex, self)._validate() #Vertex._validate(self)
  316.         for weight in self.itervalues():
  317.             assert isinstance(weight, NumberType)
  318.  
  319.  
  320. class Vertex(VertexCommon):
  321.     """Vertex has directed, unweighted, edges."""
  322.  
  323.     __slots__ = []
  324.  
  325.     EDGEVALUE = True  #XXX doesn't get used -- Graph[v1][v2] returns 1 instead of True
  326.  
  327.     def __setitem__(self, tail, value):
  328.         """Set edge value.  If tail does not exist, it is created and added to graph
  329.        if necessary.
  330.  
  331.        >>> g = Graph()
  332.        >>> g[1][2] = 3  #edge values ignored on plain Vertex
  333.        >>> g[1][2]
  334.        True
  335.        """
  336.         super(Vertex, self).__setitem__(tail, True)
  337.  
  338.     def __str__(self):
  339.         """Return string of tail vertices in set notation.
  340.  
  341.        >>> g = Graph()
  342.        >>> g.add(1, [1, 3, 4])
  343.        >>> print g[1]
  344.        {1, 3, 4}
  345.        """
  346.         if _DEBUG: self._validate()
  347.         if not self: return '{}'    #nothing to sort
  348.         keys = self.keys()
  349.         keys.sort()
  350.         return '{%s}' % ', '.join(map(repr, keys))
  351.  
  352.  
  353. def MERGE_VERTEX(g, h, vert): g[h].update(vert)
  354.  
  355. class Graph(GraphBaseType):
  356.     """Basic graph class.  Graph features (directed, weighted, self-referencing, etc) determined by VertexType."""
  357.     #Basic data structure {vertex id: {t1: edge; t2: edge}}
  358.     #Add label to __init__ to attach description to graph?
  359.     #Perhaps make default vertex type WVertex and change doctests accordingly.
  360.     #Graph.fromkeys() override?
  361.  
  362.     __slots__ = ['VertexType']
  363.  
  364.     def __init__(self, init={}, VertexType=Vertex):
  365.         """Create the graph, optionally initializing from another graph.
  366.        Optional VertexType parameter can be passed to specify default vertex type.
  367.  
  368.        >>> g = Graph(VertexType=WVertex)
  369.        >>> len(g), g
  370.        (0, {})
  371.        >>> g.add([1, 2, 3], [2, 3], 5)
  372.        >>> print g
  373.        {1: {2: 5, 3: 5}, 2: {2: 5, 3: 5}, 3: {2: 5, 3: 5}}
  374.        >>> g2 = Graph(g, Vertex)       #can initialize with other Graph type, will convert to Vertex type
  375.        >>> print g2
  376.        {1: {2, 3}, 2: {2, 3}, 3: {2, 3}}
  377.        """
  378.         self.VertexType = VertexType
  379.         super(Graph, self).__init__(init, {}, MERGE_VERTEX)
  380.  
  381.     def update(self, other, default=USE_DEFAULT, collision=MERGE_VERTEX): #XXX could remove this if collision was attribute of defdict
  382.         """Merges one graph with another.  All vertices will be convertex to VertexType.  Takes union of edge lists.
  383.        >>> g1, g2 = Graph(VertexType=Vertex), Graph(VertexType=WVertex)
  384.        >>> g1.add(1, [1, 2])
  385.        >>> g2.add(3, [2, 3]); g2.add(1, 2, 3); g2.add(1, 4, 2)
  386.        >>> g1.update(g2)   #XXX weight values get set on plain Vertex.
  387.        >>> print g1
  388.        {1: {1, 2, 4}, 2: {}, 3: {2, 3}, 4: {}}
  389.        >>> g2.add(3, 5)  #changes to g2 should not affect g1
  390.        >>> g1._validate()
  391.        """
  392.         super(Graph, self).update(other, default, collision)
  393.  
  394.     def add(self, head, tail=[], edge_value=VertexCommon.EDGEVALUE):
  395.         """Add the vertices and/or edges.
  396.        Parameters can be single vertex or list of vertices.
  397.        If no second parameter given, assume vertex addition only.
  398.  
  399.        >>> g = Graph(VertexType=WVertex)
  400.        >>> g.add(1)            #single vertex addition
  401.        >>> g.add(1)            #adding existing vertex is ignored
  402.        >>> g.add([2, 3, 4])    #multiple vertex addition
  403.        >>> g.add([2])          #list containing only one vertex is allowed
  404.        >>> print g
  405.        {1: {}, 2: {}, 3: {}, 4: {}}
  406.  
  407.        If second parameter given, then edge addition is performed.
  408.        Vertices are added as necessary.  An optional edge value
  409.        is accepted as a third parameter.
  410.  
  411.        >>> g.add(2, 1)         #edge from vertex 2 to vertex 1
  412.        >>> g.add(1, 5, 100)    #edge from 1 to new vertex 5 with weight 100
  413.        >>> g.add(1, 5, 90)     #adding existing edge, edge value overwritten
  414.        >>> g.add(3, 3, 2)      #loops are allowed
  415.        >>> g.add(3, 3)         #edge weight overwritten by default if not specified
  416.        >>> print g
  417.        {1: {5: 90}, 2: {1: 1}, 3: {3: 1}, 4: {}, 5: {}}
  418.  
  419.        Vertex lists allowed on either parameter for multiple edge addition.
  420.  
  421.        >>> g.clear()                 #remove all vertices (and edges)
  422.        >>> g.add(1, [0, 2])          #add edges (1, 0) and (1, 2)
  423.        >>> g.add(1, [1])
  424.        >>> print g
  425.        {0: {}, 1: {0: 1, 1: 1, 2: 1}, 2: {}}
  426.        >>> g.add(range(3), range(3)) #fully-connected 3-vertex graph
  427.        >>> print g
  428.        {0: {0: 1, 1: 1, 2: 1}, 1: {0: 1, 1: 1, 2: 1}, 2: {0: 1, 1: 1, 2: 1}}
  429.        """
  430.         #XXX if no edge_value given, then value should not be overwritten
  431.         if not isinstance(tail, list): tail = [tail]
  432.         try:  #single head addition
  433.             self[head].add(tail, edge_value)
  434.         except TypeError, error:  #multiple head addition
  435.             if not isinstance(head, list): raise TypeError(error)
  436.             for h in head:  #XXX will add same tails multiple times
  437.                 self[h].add(tail, edge_value)
  438.  
  439.     def discard(self, head, tail=[]):
  440.         """Remove vertices and/or edges.  Parameters can be single vertex or list of vertices.
  441.        If tail is empty, then vertex deletions are made and any connected edges.
  442.  
  443.        >>> g = Graph()
  444.        >>> g.add(range(3), range(4))
  445.        >>> g.discard(1)                #remove vertex 1
  446.        >>> g.discard(10)               #discard of non-existent vertex ignored
  447.        >>> g.discard([1])              #list with single vertex is fine
  448.        >>> g.discard([1, 3])           #discards vertices in list
  449.        >>> print g
  450.        {0: {0, 2}, 2: {0, 2}}
  451.  
  452.        If tail is non-empty, then only edge deletions are made.
  453.        >>> g.discard(0, 2)             #discard edge
  454.        >>> g.discard(5, 0)             #non-existent edge ignored
  455.        >>> g.discard(2, [1, 0, 2, 2])  #will discard two actual edges
  456.        >>> print g
  457.        {0: {0}, 2: {}}
  458.        """
  459.         if tail==[]:    #vertex deletions
  460.             try:
  461.                 del self[head]
  462.             except LookupError: pass   #do nothing if given non-existent vertex
  463.             except TypeError, error:          #given head list
  464.                 if not isinstance(head, list): raise TypeError(error)
  465.                 for h in head[:]:       #must use copy since removing below
  466.                     if h in self:
  467.                         self[h].clear()
  468.                         super(Graph, self).__delitem__(h) #don't duplicate effort (will discard in_vertices below)
  469.                     else: head.remove(h) #for faster tail removal in next loop
  470.                 for h in self.itervalues():   #visit remaining vertices and remove occurances of head items in edge lists
  471.                     h.discard(head)
  472.         else:   #edge deletions only
  473.             if not isinstance(head, list): head = [head] #quick and dirty to avoid extra code
  474.             for h in head:
  475.                 if h in self:
  476.                     self[h].discard(tail)
  477.         if _DEBUG: self._validate()
  478.  
  479.     def __contains__(self, vid): #XXX probably slows things down for little value?
  480.         """Returns non-zero if v in self.  If a list is given, all
  481.        items are checked for containment.
  482.        >>> g = Graph()
  483.        >>> g[1].add([1, 2, 2, 3])
  484.        >>> 1 in g and 2 in g
  485.        True
  486.        >>> [1, 2, 3] in g
  487.        True
  488.        >>> [1, 4] in g
  489.        False
  490.        >>> [] in g
  491.        True
  492.        """
  493.         try:
  494.             return dict.__contains__(self, vid)
  495.         except TypeError, error:   #must have been given list
  496.             if not isinstance(vid, list): raise TypeError(error)
  497.             for v in vid:
  498.                 if not dict.__contains__(self, v):
  499.                     return False
  500.             return True
  501.  
  502.     def __getitem__(self, vid): #could just set equal to GraphBaseType.setdefault, but doctest module complains
  503.         """Return value of corresponding key.  If key does not exist, create it
  504.        with default value.
  505.  
  506.        >>> g = Graph()
  507.        >>> g[1].add([1,2])
  508.        >>> g[3][1]
  509.        False
  510.        >>> print g  #NOTE: Vertex(3) created!
  511.        {1: {1, 2}, 2: {}, 3: {}}
  512.        """
  513.         return self.setdefault(vid, {}) #will convert plain {} to VertexType if necessary
  514.  
  515.     def __setitem__(self, vid, value):
  516.         """Set graph[vid] to VertexType(value).
  517.  
  518.        >>> g = Graph(VertexType=WVertex)
  519.        >>> g[1] = {}  #set g[1] to empty vertex (no out edges)
  520.        >>> type(g[1]) is WVertex
  521.        True
  522.        >>> g[2] = {1: 4, 3: 9} #non-existent vertex values get created automatically
  523.        >>> print g             #XXX what if VertexType==Vertex -> how to specify no edge value???
  524.        {1: {}, 2: {1: 4, 3: 9}, 3: {}}
  525.        >>> g._validate()
  526.        >>>
  527.        """
  528.         if isinstance(value, self.VertexType) and value._id == vid and value._graph == self:
  529.             dict.__setitem__(self, vid, value)
  530.         else:        #convert to VertexType or create copy of VertexType
  531.             dict.__setitem__(self, vid, self.VertexType(self, vid, value)) #XXX shallow copy
  532.  
  533.     def __delitem__(self, head):
  534.         """Delete a single vertex and associated edges.
  535.        >>> g = Graph()
  536.        >>> g.add([1, 2], [1, 2, 3])
  537.        >>> del g[2]    #will remove vertex 2 and edges [(1, 2), (2, 1), (2, 2), (2, 3)]
  538.        >>> print g
  539.        {1: {1, 3}, 3: {}}
  540.  
  541.        Raises LookupError if given non-existant vertex.
  542.        >>> del g[2]
  543.        Traceback (most recent call last):
  544.        ...
  545.        KeyError: 2
  546.        """
  547.         dict.__getitem__(self, head).clear() #removes out vertices (bypass key creation with dict.__getitem__)
  548.         for v in list(self[head].in_vertices()): #create copy (via list()) since in_vertices contents may change during iteration
  549.             del self[v][head]
  550.         super(Graph, self).__delitem__(head)
  551.  
  552.     def __str__(self):
  553.         """Return graph in adjacency format.
  554.  
  555.        >>> g = Graph()
  556.        >>> g.add(range(3), range(3))
  557.        >>> str(g)
  558.        '{0: {0, 1, 2}, 1: {0, 1, 2}, 2: {0, 1, 2}}'
  559.        >>> g = Graph(VertexType=WVertex)
  560.        >>> g.add(range(3), range(3))
  561.        >>> str(g)
  562.        '{0: {0: 1, 1: 1, 2: 1}, 1: {0: 1, 1: 1, 2: 1}, 2: {0: 1, 1: 1, 2: 1}}'
  563.        """
  564.         if _DEBUG: self._validate()
  565.         return super(Graph, self).__str__()
  566.         #return '{%s}' % ', '.join(map(str, self.itervalues()))
  567.  
  568.     def display(self):
  569.         """Display adjacency list.
  570.  
  571.        >>> g=Graph()
  572.        >>> g.add(range(2), range(3))
  573.        >>> g.display()
  574.        0: {0, 1, 2}
  575.        1: {0, 1, 2}
  576.        2: {}
  577.        """
  578.         if _DEBUG: self._validate()
  579.         for vid, v in self.iteritems():
  580.             print "%s: %s" % (vid, v)
  581.  
  582.     #alternate syntax for various items
  583.     vertices = GraphBaseType.iterkeys
  584.     order = GraphBaseType.__len__
  585.  
  586.     def pop(self, key, default): raise NotImplementedError
  587.     def popitem(self): raise NotImplementedError
  588.     def copy(self): raise NotImplementedError
  589.  
  590.     def _validate(self):
  591.         """Check graph invariants.
  592.  
  593.        >>> g = Graph()
  594.        >>> g[1] = {2: 3, 3: 9}
  595.        >>> g._validate()
  596.        >>> dict.__setitem__(g, 1, {2: 3, 3: 9}) #bypass Graph
  597.        >>> g._validate()
  598.        Traceback (most recent call last):
  599.        AssertionError: vertex type not found on 1
  600.        """
  601.         #NOTE:  calling this after each add/discard slows things down considerably!
  602.         for vid, v in self.iteritems():
  603.             assert isinstance(v, self.VertexType), "vertex type not found on " + str(vid)
  604.             v._validate()
  605.  
  606.  
  607. def gprofile(g, size=100):
  608.     import time
  609.     print "Profiling (ignoring debug)..."
  610.     _DEBUG = 0
  611.     for i in [1,2]:
  612.         start=time.clock()
  613.         g.add(range(size),range(100,size + 100))
  614.         finish=time.clock()
  615.         print "Add %i, 100-(%i+100); pass %i: %5.2fs" %  (size, size, i, (finish-start))
  616.     for i in [1,2]:
  617.         start=time.clock()
  618.         g.discard(range(size + 50), range(100))#, range(1000))
  619.         finish=time.clock()
  620.         print "Discard (%i+50), 100; pass %i:  %5.2fs" % (size, i, (finish-start))
  621.     g.clear()
  622.     g.add(0)
  623.     for i in [1,2]:
  624.         start=time.clock()
  625.         g[0].update(range(size))
  626.         finish=time.clock()
  627.         print "Update %i, %i; pass %i:  %5.2fs" % (size, size, i, (finish-start))
  628.     g.clear()
  629.  
  630. def _test():
  631.     """Miscellaneous tests.
  632.    >>> g = Graph(VertexType=WVertex)
  633.    >>> g.add(5)        #add vertex with id=5 to graph, default edge value=1
  634.    >>> g.add(5, 5)     #edges pointing back to self are allowed
  635.    >>> g[5][7] = 42    #add single out-edge from vertex 5 to 7 with weight 42
  636.    >>> assert 7 in g   #vertex 7 automatically added to graph
  637.    >>> g[5].add([3, 2, 4])   #add 3 out edges from 5, default weight 1
  638.    >>> print g[5]      #show out-edges from vertex 5
  639.    {2: 1, 3: 1, 4: 1, 5: 1, 7: 42}
  640.    >>> g.add(5, 7, 24) #edge values are over-written
  641.    >>> g[5][7]
  642.    24
  643.    >>> g.add(5, 7)     #edge value over-written with default if not specified
  644.    >>> g[5][7]
  645.    1
  646.    """
  647.     import doctest
  648.     return doctest.testmod() #, isprivate=lambda *args: 0)
  649.  
  650. if __name__ == '__main__':
  651.     _test()
  652.  
  653. #//python/8164

Reply to "python实现的一个抽象图像类"

Here you can reply to the paste above

captcha

https://burned.cc - Burn After Reading Website