| | """ |
| | View Classes provide node, edge and degree "views" of a graph. |
| | |
| | Views for nodes, edges and degree are provided for all base graph classes. |
| | A view means a read-only object that is quick to create, automatically |
| | updated when the graph changes, and provides basic access like `n in V`, |
| | `for n in V`, `V[n]` and sometimes set operations. |
| | |
| | The views are read-only iterable containers that are updated as the |
| | graph is updated. As with dicts, the graph should not be updated |
| | while iterating through the view. Views can be iterated multiple times. |
| | |
| | Edge and Node views also allow data attribute lookup. |
| | The resulting attribute dict is writable as `G.edges[3, 4]['color']='red'` |
| | Degree views allow lookup of degree values for single nodes. |
| | Weighted degree is supported with the `weight` argument. |
| | |
| | NodeView |
| | ======== |
| | |
| | `V = G.nodes` (or `V = G.nodes()`) allows `len(V)`, `n in V`, set |
| | operations e.g. "G.nodes & H.nodes", and `dd = G.nodes[n]`, where |
| | `dd` is the node data dict. Iteration is over the nodes by default. |
| | |
| | NodeDataView |
| | ============ |
| | |
| | To iterate over (node, data) pairs, use arguments to `G.nodes()` |
| | to create a DataView e.g. `DV = G.nodes(data='color', default='red')`. |
| | The DataView iterates as `for n, color in DV` and allows |
| | `(n, 'red') in DV`. Using `DV = G.nodes(data=True)`, the DataViews |
| | use the full datadict in writeable form also allowing contain testing as |
| | `(n, {'color': 'red'}) in VD`. DataViews allow set operations when |
| | data attributes are hashable. |
| | |
| | DegreeView |
| | ========== |
| | |
| | `V = G.degree` allows iteration over (node, degree) pairs as well |
| | as lookup: `deg=V[n]`. There are many flavors of DegreeView |
| | for In/Out/Directed/Multi. For Directed Graphs, `G.degree` |
| | counts both in and out going edges. `G.out_degree` and |
| | `G.in_degree` count only specific directions. |
| | Weighted degree using edge data attributes is provide via |
| | `V = G.degree(weight='attr_name')` where any string with the |
| | attribute name can be used. `weight=None` is the default. |
| | No set operations are implemented for degrees, use NodeView. |
| | |
| | The argument `nbunch` restricts iteration to nodes in nbunch. |
| | The DegreeView can still lookup any node even if nbunch is specified. |
| | |
| | EdgeView |
| | ======== |
| | |
| | `V = G.edges` or `V = G.edges()` allows iteration over edges as well as |
| | `e in V`, set operations and edge data lookup `dd = G.edges[2, 3]`. |
| | Iteration is over 2-tuples `(u, v)` for Graph/DiGraph. For multigraphs |
| | edges 3-tuples `(u, v, key)` are the default but 2-tuples can be obtained |
| | via `V = G.edges(keys=False)`. |
| | |
| | Set operations for directed graphs treat the edges as a set of 2-tuples. |
| | For undirected graphs, 2-tuples are not a unique representation of edges. |
| | So long as the set being compared to contains unique representations |
| | of its edges, the set operations will act as expected. If the other |
| | set contains both `(0, 1)` and `(1, 0)` however, the result of set |
| | operations may contain both representations of the same edge. |
| | |
| | EdgeDataView |
| | ============ |
| | |
| | Edge data can be reported using an EdgeDataView typically created |
| | by calling an EdgeView: `DV = G.edges(data='weight', default=1)`. |
| | The EdgeDataView allows iteration over edge tuples, membership checking |
| | but no set operations. |
| | |
| | Iteration depends on `data` and `default` and for multigraph `keys` |
| | If `data is False` (the default) then iterate over 2-tuples `(u, v)`. |
| | If `data is True` iterate over 3-tuples `(u, v, datadict)`. |
| | Otherwise iterate over `(u, v, datadict.get(data, default))`. |
| | For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key` |
| | to create 3-tuples and 4-tuples. |
| | |
| | The argument `nbunch` restricts edges to those incident to nodes in nbunch. |
| | """ |
| | from collections.abc import Mapping, Set |
| |
|
| | import networkx as nx |
| |
|
| | __all__ = [ |
| | "NodeView", |
| | "NodeDataView", |
| | "EdgeView", |
| | "OutEdgeView", |
| | "InEdgeView", |
| | "EdgeDataView", |
| | "OutEdgeDataView", |
| | "InEdgeDataView", |
| | "MultiEdgeView", |
| | "OutMultiEdgeView", |
| | "InMultiEdgeView", |
| | "MultiEdgeDataView", |
| | "OutMultiEdgeDataView", |
| | "InMultiEdgeDataView", |
| | "DegreeView", |
| | "DiDegreeView", |
| | "InDegreeView", |
| | "OutDegreeView", |
| | "MultiDegreeView", |
| | "DiMultiDegreeView", |
| | "InMultiDegreeView", |
| | "OutMultiDegreeView", |
| | ] |
| |
|
| |
|
| | |
| | class NodeView(Mapping, Set): |
| | """A NodeView class to act as G.nodes for a NetworkX Graph |
| | |
| | Set operations act on the nodes without considering data. |
| | Iteration is over nodes. Node data can be looked up like a dict. |
| | Use NodeDataView to iterate over node data or to specify a data |
| | attribute for lookup. NodeDataView is created by calling the NodeView. |
| | |
| | Parameters |
| | ---------- |
| | graph : NetworkX graph-like class |
| | |
| | Examples |
| | -------- |
| | >>> G = nx.path_graph(3) |
| | >>> NV = G.nodes() |
| | >>> 2 in NV |
| | True |
| | >>> for n in NV: |
| | ... print(n) |
| | 0 |
| | 1 |
| | 2 |
| | >>> assert NV & {1, 2, 3} == {1, 2} |
| | |
| | >>> G.add_node(2, color="blue") |
| | >>> NV[2] |
| | {'color': 'blue'} |
| | >>> G.add_node(8, color="red") |
| | >>> NDV = G.nodes(data=True) |
| | >>> (2, NV[2]) in NDV |
| | True |
| | >>> for n, dd in NDV: |
| | ... print((n, dd.get("color", "aqua"))) |
| | (0, 'aqua') |
| | (1, 'aqua') |
| | (2, 'blue') |
| | (8, 'red') |
| | >>> NDV[2] == NV[2] |
| | True |
| | |
| | >>> NVdata = G.nodes(data="color", default="aqua") |
| | >>> (2, NVdata[2]) in NVdata |
| | True |
| | >>> for n, dd in NVdata: |
| | ... print((n, dd)) |
| | (0, 'aqua') |
| | (1, 'aqua') |
| | (2, 'blue') |
| | (8, 'red') |
| | >>> NVdata[2] == NV[2] # NVdata gets 'color', NV gets datadict |
| | False |
| | """ |
| |
|
| | __slots__ = ("_nodes",) |
| |
|
| | def __getstate__(self): |
| | return {"_nodes": self._nodes} |
| |
|
| | def __setstate__(self, state): |
| | self._nodes = state["_nodes"] |
| |
|
| | def __init__(self, graph): |
| | self._nodes = graph._node |
| |
|
| | |
| | def __len__(self): |
| | return len(self._nodes) |
| |
|
| | def __iter__(self): |
| | return iter(self._nodes) |
| |
|
| | def __getitem__(self, n): |
| | if isinstance(n, slice): |
| | raise nx.NetworkXError( |
| | f"{type(self).__name__} does not support slicing, " |
| | f"try list(G.nodes)[{n.start}:{n.stop}:{n.step}]" |
| | ) |
| | return self._nodes[n] |
| |
|
| | |
| | def __contains__(self, n): |
| | return n in self._nodes |
| |
|
| | @classmethod |
| | def _from_iterable(cls, it): |
| | return set(it) |
| |
|
| | |
| | def __call__(self, data=False, default=None): |
| | if data is False: |
| | return self |
| | return NodeDataView(self._nodes, data, default) |
| |
|
| | def data(self, data=True, default=None): |
| | """ |
| | Return a read-only view of node data. |
| | |
| | Parameters |
| | ---------- |
| | data : bool or node data key, default=True |
| | If ``data=True`` (the default), return a `NodeDataView` object that |
| | maps each node to *all* of its attributes. `data` may also be an |
| | arbitrary key, in which case the `NodeDataView` maps each node to |
| | the value for the keyed attribute. In this case, if a node does |
| | not have the `data` attribute, the `default` value is used. |
| | default : object, default=None |
| | The value used when a node does not have a specific attribute. |
| | |
| | Returns |
| | ------- |
| | NodeDataView |
| | The layout of the returned NodeDataView depends on the value of the |
| | `data` parameter. |
| | |
| | Notes |
| | ----- |
| | If ``data=False``, returns a `NodeView` object without data. |
| | |
| | See Also |
| | -------- |
| | NodeDataView |
| | |
| | Examples |
| | -------- |
| | >>> G = nx.Graph() |
| | >>> G.add_nodes_from( |
| | ... [ |
| | ... (0, {"color": "red", "weight": 10}), |
| | ... (1, {"color": "blue"}), |
| | ... (2, {"color": "yellow", "weight": 2}), |
| | ... ] |
| | ... ) |
| | |
| | Accessing node data with ``data=True`` (the default) returns a |
| | NodeDataView mapping each node to all of its attributes: |
| | |
| | >>> G.nodes.data() |
| | NodeDataView({0: {'color': 'red', 'weight': 10}, 1: {'color': 'blue'}, 2: {'color': 'yellow', 'weight': 2}}) |
| | |
| | If `data` represents a key in the node attribute dict, a NodeDataView mapping |
| | the nodes to the value for that specific key is returned: |
| | |
| | >>> G.nodes.data("color") |
| | NodeDataView({0: 'red', 1: 'blue', 2: 'yellow'}, data='color') |
| | |
| | If a specific key is not found in an attribute dict, the value specified |
| | by `default` is returned: |
| | |
| | >>> G.nodes.data("weight", default=-999) |
| | NodeDataView({0: 10, 1: -999, 2: 2}, data='weight') |
| | |
| | Note that there is no check that the `data` key is in any of the |
| | node attribute dictionaries: |
| | |
| | >>> G.nodes.data("height") |
| | NodeDataView({0: None, 1: None, 2: None}, data='height') |
| | """ |
| | if data is False: |
| | return self |
| | return NodeDataView(self._nodes, data, default) |
| |
|
| | def __str__(self): |
| | return str(list(self)) |
| |
|
| | def __repr__(self): |
| | return f"{self.__class__.__name__}({tuple(self)})" |
| |
|
| |
|
| | class NodeDataView(Set): |
| | """A DataView class for nodes of a NetworkX Graph |
| | |
| | The main use for this class is to iterate through node-data pairs. |
| | The data can be the entire data-dictionary for each node, or it |
| | can be a specific attribute (with default) for each node. |
| | Set operations are enabled with NodeDataView, but don't work in |
| | cases where the data is not hashable. Use with caution. |
| | Typically, set operations on nodes use NodeView, not NodeDataView. |
| | That is, they use `G.nodes` instead of `G.nodes(data='foo')`. |
| | |
| | Parameters |
| | ========== |
| | graph : NetworkX graph-like class |
| | data : bool or string (default=False) |
| | default : object (default=None) |
| | """ |
| |
|
| | __slots__ = ("_nodes", "_data", "_default") |
| |
|
| | def __getstate__(self): |
| | return {"_nodes": self._nodes, "_data": self._data, "_default": self._default} |
| |
|
| | def __setstate__(self, state): |
| | self._nodes = state["_nodes"] |
| | self._data = state["_data"] |
| | self._default = state["_default"] |
| |
|
| | def __init__(self, nodedict, data=False, default=None): |
| | self._nodes = nodedict |
| | self._data = data |
| | self._default = default |
| |
|
| | @classmethod |
| | def _from_iterable(cls, it): |
| | try: |
| | return set(it) |
| | except TypeError as err: |
| | if "unhashable" in str(err): |
| | msg = " : Could be b/c data=True or your values are unhashable" |
| | raise TypeError(str(err) + msg) from err |
| | raise |
| |
|
| | def __len__(self): |
| | return len(self._nodes) |
| |
|
| | def __iter__(self): |
| | data = self._data |
| | if data is False: |
| | return iter(self._nodes) |
| | if data is True: |
| | return iter(self._nodes.items()) |
| | return ( |
| | (n, dd[data] if data in dd else self._default) |
| | for n, dd in self._nodes.items() |
| | ) |
| |
|
| | def __contains__(self, n): |
| | try: |
| | node_in = n in self._nodes |
| | except TypeError: |
| | n, d = n |
| | return n in self._nodes and self[n] == d |
| | if node_in is True: |
| | return node_in |
| | try: |
| | n, d = n |
| | except (TypeError, ValueError): |
| | return False |
| | return n in self._nodes and self[n] == d |
| |
|
| | def __getitem__(self, n): |
| | if isinstance(n, slice): |
| | raise nx.NetworkXError( |
| | f"{type(self).__name__} does not support slicing, " |
| | f"try list(G.nodes.data())[{n.start}:{n.stop}:{n.step}]" |
| | ) |
| | ddict = self._nodes[n] |
| | data = self._data |
| | if data is False or data is True: |
| | return ddict |
| | return ddict[data] if data in ddict else self._default |
| |
|
| | def __str__(self): |
| | return str(list(self)) |
| |
|
| | def __repr__(self): |
| | name = self.__class__.__name__ |
| | if self._data is False: |
| | return f"{name}({tuple(self)})" |
| | if self._data is True: |
| | return f"{name}({dict(self)})" |
| | return f"{name}({dict(self)}, data={self._data!r})" |
| |
|
| |
|
| | |
| | class DiDegreeView: |
| | """A View class for degree of nodes in a NetworkX Graph |
| | |
| | The functionality is like dict.items() with (node, degree) pairs. |
| | Additional functionality includes read-only lookup of node degree, |
| | and calling with optional features nbunch (for only a subset of nodes) |
| | and weight (use edge weights to compute degree). |
| | |
| | Parameters |
| | ========== |
| | graph : NetworkX graph-like class |
| | nbunch : node, container of nodes, or None meaning all nodes (default=None) |
| | weight : bool or string (default=None) |
| | |
| | Notes |
| | ----- |
| | DegreeView can still lookup any node even if nbunch is specified. |
| | |
| | Examples |
| | -------- |
| | >>> G = nx.path_graph(3) |
| | >>> DV = G.degree() |
| | >>> assert DV[2] == 1 |
| | >>> assert sum(deg for n, deg in DV) == 4 |
| | |
| | >>> DVweight = G.degree(weight="span") |
| | >>> G.add_edge(1, 2, span=34) |
| | >>> DVweight[2] |
| | 34 |
| | >>> DVweight[0] # default edge weight is 1 |
| | 1 |
| | >>> sum(span for n, span in DVweight) # sum weighted degrees |
| | 70 |
| | |
| | >>> DVnbunch = G.degree(nbunch=(1, 2)) |
| | >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only |
| | """ |
| |
|
| | def __init__(self, G, nbunch=None, weight=None): |
| | self._graph = G |
| | self._succ = G._succ if hasattr(G, "_succ") else G._adj |
| | self._pred = G._pred if hasattr(G, "_pred") else G._adj |
| | self._nodes = self._succ if nbunch is None else list(G.nbunch_iter(nbunch)) |
| | self._weight = weight |
| |
|
| | def __call__(self, nbunch=None, weight=None): |
| | if nbunch is None: |
| | if weight == self._weight: |
| | return self |
| | return self.__class__(self._graph, None, weight) |
| | try: |
| | if nbunch in self._nodes: |
| | if weight == self._weight: |
| | return self[nbunch] |
| | return self.__class__(self._graph, None, weight)[nbunch] |
| | except TypeError: |
| | pass |
| | return self.__class__(self._graph, nbunch, weight) |
| |
|
| | def __getitem__(self, n): |
| | weight = self._weight |
| | succs = self._succ[n] |
| | preds = self._pred[n] |
| | if weight is None: |
| | return len(succs) + len(preds) |
| | return sum(dd.get(weight, 1) for dd in succs.values()) + sum( |
| | dd.get(weight, 1) for dd in preds.values() |
| | ) |
| |
|
| | def __iter__(self): |
| | weight = self._weight |
| | if weight is None: |
| | for n in self._nodes: |
| | succs = self._succ[n] |
| | preds = self._pred[n] |
| | yield (n, len(succs) + len(preds)) |
| | else: |
| | for n in self._nodes: |
| | succs = self._succ[n] |
| | preds = self._pred[n] |
| | deg = sum(dd.get(weight, 1) for dd in succs.values()) + sum( |
| | dd.get(weight, 1) for dd in preds.values() |
| | ) |
| | yield (n, deg) |
| |
|
| | def __len__(self): |
| | return len(self._nodes) |
| |
|
| | def __str__(self): |
| | return str(list(self)) |
| |
|
| | def __repr__(self): |
| | return f"{self.__class__.__name__}({dict(self)})" |
| |
|
| |
|
| | class DegreeView(DiDegreeView): |
| | """A DegreeView class to act as G.degree for a NetworkX Graph |
| | |
| | Typical usage focuses on iteration over `(node, degree)` pairs. |
| | The degree is by default the number of edges incident to the node. |
| | Optional argument `weight` enables weighted degree using the edge |
| | attribute named in the `weight` argument. Reporting and iteration |
| | can also be restricted to a subset of nodes using `nbunch`. |
| | |
| | Additional functionality include node lookup so that `G.degree[n]` |
| | reported the (possibly weighted) degree of node `n`. Calling the |
| | view creates a view with different arguments `nbunch` or `weight`. |
| | |
| | Parameters |
| | ========== |
| | graph : NetworkX graph-like class |
| | nbunch : node, container of nodes, or None meaning all nodes (default=None) |
| | weight : string or None (default=None) |
| | |
| | Notes |
| | ----- |
| | DegreeView can still lookup any node even if nbunch is specified. |
| | |
| | Examples |
| | -------- |
| | >>> G = nx.path_graph(3) |
| | >>> DV = G.degree() |
| | >>> assert DV[2] == 1 |
| | >>> assert G.degree[2] == 1 |
| | >>> assert sum(deg for n, deg in DV) == 4 |
| | |
| | >>> DVweight = G.degree(weight="span") |
| | >>> G.add_edge(1, 2, span=34) |
| | >>> DVweight[2] |
| | 34 |
| | >>> DVweight[0] # default edge weight is 1 |
| | 1 |
| | >>> sum(span for n, span in DVweight) # sum weighted degrees |
| | 70 |
| | |
| | >>> DVnbunch = G.degree(nbunch=(1, 2)) |
| | >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only |
| | """ |
| |
|
| | def __getitem__(self, n): |
| | weight = self._weight |
| | nbrs = self._succ[n] |
| | if weight is None: |
| | return len(nbrs) + (n in nbrs) |
| | return sum(dd.get(weight, 1) for dd in nbrs.values()) + ( |
| | n in nbrs and nbrs[n].get(weight, 1) |
| | ) |
| |
|
| | def __iter__(self): |
| | weight = self._weight |
| | if weight is None: |
| | for n in self._nodes: |
| | nbrs = self._succ[n] |
| | yield (n, len(nbrs) + (n in nbrs)) |
| | else: |
| | for n in self._nodes: |
| | nbrs = self._succ[n] |
| | deg = sum(dd.get(weight, 1) for dd in nbrs.values()) + ( |
| | n in nbrs and nbrs[n].get(weight, 1) |
| | ) |
| | yield (n, deg) |
| |
|
| |
|
| | class OutDegreeView(DiDegreeView): |
| | """A DegreeView class to report out_degree for a DiGraph; See DegreeView""" |
| |
|
| | def __getitem__(self, n): |
| | weight = self._weight |
| | nbrs = self._succ[n] |
| | if self._weight is None: |
| | return len(nbrs) |
| | return sum(dd.get(self._weight, 1) for dd in nbrs.values()) |
| |
|
| | def __iter__(self): |
| | weight = self._weight |
| | if weight is None: |
| | for n in self._nodes: |
| | succs = self._succ[n] |
| | yield (n, len(succs)) |
| | else: |
| | for n in self._nodes: |
| | succs = self._succ[n] |
| | deg = sum(dd.get(weight, 1) for dd in succs.values()) |
| | yield (n, deg) |
| |
|
| |
|
| | class InDegreeView(DiDegreeView): |
| | """A DegreeView class to report in_degree for a DiGraph; See DegreeView""" |
| |
|
| | def __getitem__(self, n): |
| | weight = self._weight |
| | nbrs = self._pred[n] |
| | if weight is None: |
| | return len(nbrs) |
| | return sum(dd.get(weight, 1) for dd in nbrs.values()) |
| |
|
| | def __iter__(self): |
| | weight = self._weight |
| | if weight is None: |
| | for n in self._nodes: |
| | preds = self._pred[n] |
| | yield (n, len(preds)) |
| | else: |
| | for n in self._nodes: |
| | preds = self._pred[n] |
| | deg = sum(dd.get(weight, 1) for dd in preds.values()) |
| | yield (n, deg) |
| |
|
| |
|
| | class MultiDegreeView(DiDegreeView): |
| | """A DegreeView class for undirected multigraphs; See DegreeView""" |
| |
|
| | def __getitem__(self, n): |
| | weight = self._weight |
| | nbrs = self._succ[n] |
| | if weight is None: |
| | return sum(len(keys) for keys in nbrs.values()) + ( |
| | n in nbrs and len(nbrs[n]) |
| | ) |
| | |
| | deg = sum( |
| | d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values() |
| | ) |
| | if n in nbrs: |
| | deg += sum(d.get(weight, 1) for d in nbrs[n].values()) |
| | return deg |
| |
|
| | def __iter__(self): |
| | weight = self._weight |
| | if weight is None: |
| | for n in self._nodes: |
| | nbrs = self._succ[n] |
| | deg = sum(len(keys) for keys in nbrs.values()) + ( |
| | n in nbrs and len(nbrs[n]) |
| | ) |
| | yield (n, deg) |
| | else: |
| | for n in self._nodes: |
| | nbrs = self._succ[n] |
| | deg = sum( |
| | d.get(weight, 1) |
| | for key_dict in nbrs.values() |
| | for d in key_dict.values() |
| | ) |
| | if n in nbrs: |
| | deg += sum(d.get(weight, 1) for d in nbrs[n].values()) |
| | yield (n, deg) |
| |
|
| |
|
| | class DiMultiDegreeView(DiDegreeView): |
| | """A DegreeView class for MultiDiGraph; See DegreeView""" |
| |
|
| | def __getitem__(self, n): |
| | weight = self._weight |
| | succs = self._succ[n] |
| | preds = self._pred[n] |
| | if weight is None: |
| | return sum(len(keys) for keys in succs.values()) + sum( |
| | len(keys) for keys in preds.values() |
| | ) |
| | |
| | deg = sum( |
| | d.get(weight, 1) for key_dict in succs.values() for d in key_dict.values() |
| | ) + sum( |
| | d.get(weight, 1) for key_dict in preds.values() for d in key_dict.values() |
| | ) |
| | return deg |
| |
|
| | def __iter__(self): |
| | weight = self._weight |
| | if weight is None: |
| | for n in self._nodes: |
| | succs = self._succ[n] |
| | preds = self._pred[n] |
| | deg = sum(len(keys) for keys in succs.values()) + sum( |
| | len(keys) for keys in preds.values() |
| | ) |
| | yield (n, deg) |
| | else: |
| | for n in self._nodes: |
| | succs = self._succ[n] |
| | preds = self._pred[n] |
| | deg = sum( |
| | d.get(weight, 1) |
| | for key_dict in succs.values() |
| | for d in key_dict.values() |
| | ) + sum( |
| | d.get(weight, 1) |
| | for key_dict in preds.values() |
| | for d in key_dict.values() |
| | ) |
| | yield (n, deg) |
| |
|
| |
|
| | class InMultiDegreeView(DiDegreeView): |
| | """A DegreeView class for inward degree of MultiDiGraph; See DegreeView""" |
| |
|
| | def __getitem__(self, n): |
| | weight = self._weight |
| | nbrs = self._pred[n] |
| | if weight is None: |
| | return sum(len(data) for data in nbrs.values()) |
| | |
| | return sum( |
| | d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values() |
| | ) |
| |
|
| | def __iter__(self): |
| | weight = self._weight |
| | if weight is None: |
| | for n in self._nodes: |
| | nbrs = self._pred[n] |
| | deg = sum(len(data) for data in nbrs.values()) |
| | yield (n, deg) |
| | else: |
| | for n in self._nodes: |
| | nbrs = self._pred[n] |
| | deg = sum( |
| | d.get(weight, 1) |
| | for key_dict in nbrs.values() |
| | for d in key_dict.values() |
| | ) |
| | yield (n, deg) |
| |
|
| |
|
| | class OutMultiDegreeView(DiDegreeView): |
| | """A DegreeView class for outward degree of MultiDiGraph; See DegreeView""" |
| |
|
| | def __getitem__(self, n): |
| | weight = self._weight |
| | nbrs = self._succ[n] |
| | if weight is None: |
| | return sum(len(data) for data in nbrs.values()) |
| | |
| | return sum( |
| | d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values() |
| | ) |
| |
|
| | def __iter__(self): |
| | weight = self._weight |
| | if weight is None: |
| | for n in self._nodes: |
| | nbrs = self._succ[n] |
| | deg = sum(len(data) for data in nbrs.values()) |
| | yield (n, deg) |
| | else: |
| | for n in self._nodes: |
| | nbrs = self._succ[n] |
| | deg = sum( |
| | d.get(weight, 1) |
| | for key_dict in nbrs.values() |
| | for d in key_dict.values() |
| | ) |
| | yield (n, deg) |
| |
|
| |
|
| | |
| | class OutEdgeDataView: |
| | """EdgeDataView for outward edges of DiGraph; See EdgeDataView""" |
| |
|
| | __slots__ = ( |
| | "_viewer", |
| | "_nbunch", |
| | "_data", |
| | "_default", |
| | "_adjdict", |
| | "_nodes_nbrs", |
| | "_report", |
| | ) |
| |
|
| | def __getstate__(self): |
| | return { |
| | "viewer": self._viewer, |
| | "nbunch": self._nbunch, |
| | "data": self._data, |
| | "default": self._default, |
| | } |
| |
|
| | def __setstate__(self, state): |
| | self.__init__(**state) |
| |
|
| | def __init__(self, viewer, nbunch=None, data=False, *, default=None): |
| | self._viewer = viewer |
| | adjdict = self._adjdict = viewer._adjdict |
| | if nbunch is None: |
| | self._nodes_nbrs = adjdict.items |
| | else: |
| | |
| | nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch)) |
| | self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch] |
| | self._nbunch = nbunch |
| | self._data = data |
| | self._default = default |
| | |
| | if data is True: |
| | self._report = lambda n, nbr, dd: (n, nbr, dd) |
| | elif data is False: |
| | self._report = lambda n, nbr, dd: (n, nbr) |
| | else: |
| | self._report = ( |
| | lambda n, nbr, dd: (n, nbr, dd[data]) |
| | if data in dd |
| | else (n, nbr, default) |
| | ) |
| |
|
| | def __len__(self): |
| | return sum(len(nbrs) for n, nbrs in self._nodes_nbrs()) |
| |
|
| | def __iter__(self): |
| | return ( |
| | self._report(n, nbr, dd) |
| | for n, nbrs in self._nodes_nbrs() |
| | for nbr, dd in nbrs.items() |
| | ) |
| |
|
| | def __contains__(self, e): |
| | u, v = e[:2] |
| | if self._nbunch is not None and u not in self._nbunch: |
| | return False |
| | try: |
| | ddict = self._adjdict[u][v] |
| | except KeyError: |
| | return False |
| | return e == self._report(u, v, ddict) |
| |
|
| | def __str__(self): |
| | return str(list(self)) |
| |
|
| | def __repr__(self): |
| | return f"{self.__class__.__name__}({list(self)})" |
| |
|
| |
|
| | class EdgeDataView(OutEdgeDataView): |
| | """A EdgeDataView class for edges of Graph |
| | |
| | This view is primarily used to iterate over the edges reporting |
| | edges as node-tuples with edge data optionally reported. The |
| | argument `nbunch` allows restriction to edges incident to nodes |
| | in that container/singleton. The default (nbunch=None) |
| | reports all edges. The arguments `data` and `default` control |
| | what edge data is reported. The default `data is False` reports |
| | only node-tuples for each edge. If `data is True` the entire edge |
| | data dict is returned. Otherwise `data` is assumed to hold the name |
| | of the edge attribute to report with default `default` if that |
| | edge attribute is not present. |
| | |
| | Parameters |
| | ---------- |
| | nbunch : container of nodes, node or None (default None) |
| | data : False, True or string (default False) |
| | default : default value (default None) |
| | |
| | Examples |
| | -------- |
| | >>> G = nx.path_graph(3) |
| | >>> G.add_edge(1, 2, foo="bar") |
| | >>> list(G.edges(data="foo", default="biz")) |
| | [(0, 1, 'biz'), (1, 2, 'bar')] |
| | >>> assert (0, 1, "biz") in G.edges(data="foo", default="biz") |
| | """ |
| |
|
| | __slots__ = () |
| |
|
| | def __len__(self): |
| | return sum(1 for e in self) |
| |
|
| | def __iter__(self): |
| | seen = {} |
| | for n, nbrs in self._nodes_nbrs(): |
| | for nbr, dd in nbrs.items(): |
| | if nbr not in seen: |
| | yield self._report(n, nbr, dd) |
| | seen[n] = 1 |
| | del seen |
| |
|
| | def __contains__(self, e): |
| | u, v = e[:2] |
| | if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch: |
| | return False |
| | try: |
| | ddict = self._adjdict[u][v] |
| | except KeyError: |
| | return False |
| | return e == self._report(u, v, ddict) |
| |
|
| |
|
| | class InEdgeDataView(OutEdgeDataView): |
| | """An EdgeDataView class for outward edges of DiGraph; See EdgeDataView""" |
| |
|
| | __slots__ = () |
| |
|
| | def __iter__(self): |
| | return ( |
| | self._report(nbr, n, dd) |
| | for n, nbrs in self._nodes_nbrs() |
| | for nbr, dd in nbrs.items() |
| | ) |
| |
|
| | def __contains__(self, e): |
| | u, v = e[:2] |
| | if self._nbunch is not None and v not in self._nbunch: |
| | return False |
| | try: |
| | ddict = self._adjdict[v][u] |
| | except KeyError: |
| | return False |
| | return e == self._report(u, v, ddict) |
| |
|
| |
|
| | class OutMultiEdgeDataView(OutEdgeDataView): |
| | """An EdgeDataView for outward edges of MultiDiGraph; See EdgeDataView""" |
| |
|
| | __slots__ = ("keys",) |
| |
|
| | def __getstate__(self): |
| | return { |
| | "viewer": self._viewer, |
| | "nbunch": self._nbunch, |
| | "keys": self.keys, |
| | "data": self._data, |
| | "default": self._default, |
| | } |
| |
|
| | def __setstate__(self, state): |
| | self.__init__(**state) |
| |
|
| | def __init__(self, viewer, nbunch=None, data=False, *, default=None, keys=False): |
| | self._viewer = viewer |
| | adjdict = self._adjdict = viewer._adjdict |
| | self.keys = keys |
| | if nbunch is None: |
| | self._nodes_nbrs = adjdict.items |
| | else: |
| | |
| | nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch)) |
| | self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch] |
| | self._nbunch = nbunch |
| | self._data = data |
| | self._default = default |
| | |
| | if data is True: |
| | if keys is True: |
| | self._report = lambda n, nbr, k, dd: (n, nbr, k, dd) |
| | else: |
| | self._report = lambda n, nbr, k, dd: (n, nbr, dd) |
| | elif data is False: |
| | if keys is True: |
| | self._report = lambda n, nbr, k, dd: (n, nbr, k) |
| | else: |
| | self._report = lambda n, nbr, k, dd: (n, nbr) |
| | else: |
| | if keys is True: |
| | self._report = ( |
| | lambda n, nbr, k, dd: (n, nbr, k, dd[data]) |
| | if data in dd |
| | else (n, nbr, k, default) |
| | ) |
| | else: |
| | self._report = ( |
| | lambda n, nbr, k, dd: (n, nbr, dd[data]) |
| | if data in dd |
| | else (n, nbr, default) |
| | ) |
| |
|
| | def __len__(self): |
| | return sum(1 for e in self) |
| |
|
| | def __iter__(self): |
| | return ( |
| | self._report(n, nbr, k, dd) |
| | for n, nbrs in self._nodes_nbrs() |
| | for nbr, kd in nbrs.items() |
| | for k, dd in kd.items() |
| | ) |
| |
|
| | def __contains__(self, e): |
| | u, v = e[:2] |
| | if self._nbunch is not None and u not in self._nbunch: |
| | return False |
| | try: |
| | kdict = self._adjdict[u][v] |
| | except KeyError: |
| | return False |
| | if self.keys is True: |
| | k = e[2] |
| | try: |
| | dd = kdict[k] |
| | except KeyError: |
| | return False |
| | return e == self._report(u, v, k, dd) |
| | return any(e == self._report(u, v, k, dd) for k, dd in kdict.items()) |
| |
|
| |
|
| | class MultiEdgeDataView(OutMultiEdgeDataView): |
| | """An EdgeDataView class for edges of MultiGraph; See EdgeDataView""" |
| |
|
| | __slots__ = () |
| |
|
| | def __iter__(self): |
| | seen = {} |
| | for n, nbrs in self._nodes_nbrs(): |
| | for nbr, kd in nbrs.items(): |
| | if nbr not in seen: |
| | for k, dd in kd.items(): |
| | yield self._report(n, nbr, k, dd) |
| | seen[n] = 1 |
| | del seen |
| |
|
| | def __contains__(self, e): |
| | u, v = e[:2] |
| | if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch: |
| | return False |
| | try: |
| | kdict = self._adjdict[u][v] |
| | except KeyError: |
| | try: |
| | kdict = self._adjdict[v][u] |
| | except KeyError: |
| | return False |
| | if self.keys is True: |
| | k = e[2] |
| | try: |
| | dd = kdict[k] |
| | except KeyError: |
| | return False |
| | return e == self._report(u, v, k, dd) |
| | return any(e == self._report(u, v, k, dd) for k, dd in kdict.items()) |
| |
|
| |
|
| | class InMultiEdgeDataView(OutMultiEdgeDataView): |
| | """An EdgeDataView for inward edges of MultiDiGraph; See EdgeDataView""" |
| |
|
| | __slots__ = () |
| |
|
| | def __iter__(self): |
| | return ( |
| | self._report(nbr, n, k, dd) |
| | for n, nbrs in self._nodes_nbrs() |
| | for nbr, kd in nbrs.items() |
| | for k, dd in kd.items() |
| | ) |
| |
|
| | def __contains__(self, e): |
| | u, v = e[:2] |
| | if self._nbunch is not None and v not in self._nbunch: |
| | return False |
| | try: |
| | kdict = self._adjdict[v][u] |
| | except KeyError: |
| | return False |
| | if self.keys is True: |
| | k = e[2] |
| | dd = kdict[k] |
| | return e == self._report(u, v, k, dd) |
| | return any(e == self._report(u, v, k, dd) for k, dd in kdict.items()) |
| |
|
| |
|
| | |
| | class OutEdgeView(Set, Mapping): |
| | """A EdgeView class for outward edges of a DiGraph""" |
| |
|
| | __slots__ = ("_adjdict", "_graph", "_nodes_nbrs") |
| |
|
| | def __getstate__(self): |
| | return {"_graph": self._graph, "_adjdict": self._adjdict} |
| |
|
| | def __setstate__(self, state): |
| | self._graph = state["_graph"] |
| | self._adjdict = state["_adjdict"] |
| | self._nodes_nbrs = self._adjdict.items |
| |
|
| | @classmethod |
| | def _from_iterable(cls, it): |
| | return set(it) |
| |
|
| | dataview = OutEdgeDataView |
| |
|
| | def __init__(self, G): |
| | self._graph = G |
| | self._adjdict = G._succ if hasattr(G, "succ") else G._adj |
| | self._nodes_nbrs = self._adjdict.items |
| |
|
| | |
| | def __len__(self): |
| | return sum(len(nbrs) for n, nbrs in self._nodes_nbrs()) |
| |
|
| | def __iter__(self): |
| | for n, nbrs in self._nodes_nbrs(): |
| | for nbr in nbrs: |
| | yield (n, nbr) |
| |
|
| | def __contains__(self, e): |
| | try: |
| | u, v = e |
| | return v in self._adjdict[u] |
| | except KeyError: |
| | return False |
| |
|
| | |
| | def __getitem__(self, e): |
| | if isinstance(e, slice): |
| | raise nx.NetworkXError( |
| | f"{type(self).__name__} does not support slicing, " |
| | f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]" |
| | ) |
| | u, v = e |
| | try: |
| | return self._adjdict[u][v] |
| | except KeyError as ex: |
| | raise KeyError(f"The edge {e} is not in the graph.") |
| |
|
| | |
| | def __call__(self, nbunch=None, data=False, *, default=None): |
| | if nbunch is None and data is False: |
| | return self |
| | return self.dataview(self, nbunch, data, default=default) |
| |
|
| | def data(self, data=True, default=None, nbunch=None): |
| | """ |
| | Return a read-only view of edge data. |
| | |
| | Parameters |
| | ---------- |
| | data : bool or edge attribute key |
| | If ``data=True``, then the data view maps each edge to a dictionary |
| | containing all of its attributes. If `data` is a key in the edge |
| | dictionary, then the data view maps each edge to its value for |
| | the keyed attribute. In this case, if the edge doesn't have the |
| | attribute, the `default` value is returned. |
| | default : object, default=None |
| | The value used when an edge does not have a specific attribute |
| | nbunch : container of nodes, optional (default=None) |
| | Allows restriction to edges only involving certain nodes. All edges |
| | are considered by default. |
| | |
| | Returns |
| | ------- |
| | dataview |
| | Returns an `EdgeDataView` for undirected Graphs, `OutEdgeDataView` |
| | for DiGraphs, `MultiEdgeDataView` for MultiGraphs and |
| | `OutMultiEdgeDataView` for MultiDiGraphs. |
| | |
| | Notes |
| | ----- |
| | If ``data=False``, returns an `EdgeView` without any edge data. |
| | |
| | See Also |
| | -------- |
| | EdgeDataView |
| | OutEdgeDataView |
| | MultiEdgeDataView |
| | OutMultiEdgeDataView |
| | |
| | Examples |
| | -------- |
| | >>> G = nx.Graph() |
| | >>> G.add_edges_from( |
| | ... [ |
| | ... (0, 1, {"dist": 3, "capacity": 20}), |
| | ... (1, 2, {"dist": 4}), |
| | ... (2, 0, {"dist": 5}), |
| | ... ] |
| | ... ) |
| | |
| | Accessing edge data with ``data=True`` (the default) returns an |
| | edge data view object listing each edge with all of its attributes: |
| | |
| | >>> G.edges.data() |
| | EdgeDataView([(0, 1, {'dist': 3, 'capacity': 20}), (0, 2, {'dist': 5}), (1, 2, {'dist': 4})]) |
| | |
| | If `data` represents a key in the edge attribute dict, a dataview listing |
| | each edge with its value for that specific key is returned: |
| | |
| | >>> G.edges.data("dist") |
| | EdgeDataView([(0, 1, 3), (0, 2, 5), (1, 2, 4)]) |
| | |
| | `nbunch` can be used to limit the edges: |
| | |
| | >>> G.edges.data("dist", nbunch=[0]) |
| | EdgeDataView([(0, 1, 3), (0, 2, 5)]) |
| | |
| | If a specific key is not found in an edge attribute dict, the value |
| | specified by `default` is used: |
| | |
| | >>> G.edges.data("capacity") |
| | EdgeDataView([(0, 1, 20), (0, 2, None), (1, 2, None)]) |
| | |
| | Note that there is no check that the `data` key is present in any of |
| | the edge attribute dictionaries: |
| | |
| | >>> G.edges.data("speed") |
| | EdgeDataView([(0, 1, None), (0, 2, None), (1, 2, None)]) |
| | """ |
| | if nbunch is None and data is False: |
| | return self |
| | return self.dataview(self, nbunch, data, default=default) |
| |
|
| | |
| | def __str__(self): |
| | return str(list(self)) |
| |
|
| | def __repr__(self): |
| | return f"{self.__class__.__name__}({list(self)})" |
| |
|
| |
|
| | class EdgeView(OutEdgeView): |
| | """A EdgeView class for edges of a Graph |
| | |
| | This densely packed View allows iteration over edges, data lookup |
| | like a dict and set operations on edges represented by node-tuples. |
| | In addition, edge data can be controlled by calling this object |
| | possibly creating an EdgeDataView. Typically edges are iterated over |
| | and reported as `(u, v)` node tuples or `(u, v, key)` node/key tuples |
| | for multigraphs. Those edge representations can also be using to |
| | lookup the data dict for any edge. Set operations also are available |
| | where those tuples are the elements of the set. |
| | Calling this object with optional arguments `data`, `default` and `keys` |
| | controls the form of the tuple (see EdgeDataView). Optional argument |
| | `nbunch` allows restriction to edges only involving certain nodes. |
| | |
| | If `data is False` (the default) then iterate over 2-tuples `(u, v)`. |
| | If `data is True` iterate over 3-tuples `(u, v, datadict)`. |
| | Otherwise iterate over `(u, v, datadict.get(data, default))`. |
| | For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key` above. |
| | |
| | Parameters |
| | ========== |
| | graph : NetworkX graph-like class |
| | nbunch : (default= all nodes in graph) only report edges with these nodes |
| | keys : (only for MultiGraph. default=False) report edge key in tuple |
| | data : bool or string (default=False) see above |
| | default : object (default=None) |
| | |
| | Examples |
| | ======== |
| | >>> G = nx.path_graph(4) |
| | >>> EV = G.edges() |
| | >>> (2, 3) in EV |
| | True |
| | >>> for u, v in EV: |
| | ... print((u, v)) |
| | (0, 1) |
| | (1, 2) |
| | (2, 3) |
| | >>> assert EV & {(1, 2), (3, 4)} == {(1, 2)} |
| | |
| | >>> EVdata = G.edges(data="color", default="aqua") |
| | >>> G.add_edge(2, 3, color="blue") |
| | >>> assert (2, 3, "blue") in EVdata |
| | >>> for u, v, c in EVdata: |
| | ... print(f"({u}, {v}) has color: {c}") |
| | (0, 1) has color: aqua |
| | (1, 2) has color: aqua |
| | (2, 3) has color: blue |
| | |
| | >>> EVnbunch = G.edges(nbunch=2) |
| | >>> assert (2, 3) in EVnbunch |
| | >>> assert (0, 1) not in EVnbunch |
| | >>> for u, v in EVnbunch: |
| | ... assert u == 2 or v == 2 |
| | |
| | >>> MG = nx.path_graph(4, create_using=nx.MultiGraph) |
| | >>> EVmulti = MG.edges(keys=True) |
| | >>> (2, 3, 0) in EVmulti |
| | True |
| | >>> (2, 3) in EVmulti # 2-tuples work even when keys is True |
| | True |
| | >>> key = MG.add_edge(2, 3) |
| | >>> for u, v, k in EVmulti: |
| | ... print((u, v, k)) |
| | (0, 1, 0) |
| | (1, 2, 0) |
| | (2, 3, 0) |
| | (2, 3, 1) |
| | """ |
| |
|
| | __slots__ = () |
| |
|
| | dataview = EdgeDataView |
| |
|
| | def __len__(self): |
| | num_nbrs = (len(nbrs) + (n in nbrs) for n, nbrs in self._nodes_nbrs()) |
| | return sum(num_nbrs) // 2 |
| |
|
| | def __iter__(self): |
| | seen = {} |
| | for n, nbrs in self._nodes_nbrs(): |
| | for nbr in list(nbrs): |
| | if nbr not in seen: |
| | yield (n, nbr) |
| | seen[n] = 1 |
| | del seen |
| |
|
| | def __contains__(self, e): |
| | try: |
| | u, v = e[:2] |
| | return v in self._adjdict[u] or u in self._adjdict[v] |
| | except (KeyError, ValueError): |
| | return False |
| |
|
| |
|
| | class InEdgeView(OutEdgeView): |
| | """A EdgeView class for inward edges of a DiGraph""" |
| |
|
| | __slots__ = () |
| |
|
| | def __setstate__(self, state): |
| | self._graph = state["_graph"] |
| | self._adjdict = state["_adjdict"] |
| | self._nodes_nbrs = self._adjdict.items |
| |
|
| | dataview = InEdgeDataView |
| |
|
| | def __init__(self, G): |
| | self._graph = G |
| | self._adjdict = G._pred if hasattr(G, "pred") else G._adj |
| | self._nodes_nbrs = self._adjdict.items |
| |
|
| | def __iter__(self): |
| | for n, nbrs in self._nodes_nbrs(): |
| | for nbr in nbrs: |
| | yield (nbr, n) |
| |
|
| | def __contains__(self, e): |
| | try: |
| | u, v = e |
| | return u in self._adjdict[v] |
| | except KeyError: |
| | return False |
| |
|
| | def __getitem__(self, e): |
| | if isinstance(e, slice): |
| | raise nx.NetworkXError( |
| | f"{type(self).__name__} does not support slicing, " |
| | f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]" |
| | ) |
| | u, v = e |
| | return self._adjdict[v][u] |
| |
|
| |
|
| | class OutMultiEdgeView(OutEdgeView): |
| | """A EdgeView class for outward edges of a MultiDiGraph""" |
| |
|
| | __slots__ = () |
| |
|
| | dataview = OutMultiEdgeDataView |
| |
|
| | def __len__(self): |
| | return sum( |
| | len(kdict) for n, nbrs in self._nodes_nbrs() for nbr, kdict in nbrs.items() |
| | ) |
| |
|
| | def __iter__(self): |
| | for n, nbrs in self._nodes_nbrs(): |
| | for nbr, kdict in nbrs.items(): |
| | for key in kdict: |
| | yield (n, nbr, key) |
| |
|
| | def __contains__(self, e): |
| | N = len(e) |
| | if N == 3: |
| | u, v, k = e |
| | elif N == 2: |
| | u, v = e |
| | k = 0 |
| | else: |
| | raise ValueError("MultiEdge must have length 2 or 3") |
| | try: |
| | return k in self._adjdict[u][v] |
| | except KeyError: |
| | return False |
| |
|
| | def __getitem__(self, e): |
| | if isinstance(e, slice): |
| | raise nx.NetworkXError( |
| | f"{type(self).__name__} does not support slicing, " |
| | f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]" |
| | ) |
| | u, v, k = e |
| | return self._adjdict[u][v][k] |
| |
|
| | def __call__(self, nbunch=None, data=False, *, default=None, keys=False): |
| | if nbunch is None and data is False and keys is True: |
| | return self |
| | return self.dataview(self, nbunch, data, default=default, keys=keys) |
| |
|
| | def data(self, data=True, default=None, nbunch=None, keys=False): |
| | if nbunch is None and data is False and keys is True: |
| | return self |
| | return self.dataview(self, nbunch, data, default=default, keys=keys) |
| |
|
| |
|
| | class MultiEdgeView(OutMultiEdgeView): |
| | """A EdgeView class for edges of a MultiGraph""" |
| |
|
| | __slots__ = () |
| |
|
| | dataview = MultiEdgeDataView |
| |
|
| | def __len__(self): |
| | return sum(1 for e in self) |
| |
|
| | def __iter__(self): |
| | seen = {} |
| | for n, nbrs in self._nodes_nbrs(): |
| | for nbr, kd in nbrs.items(): |
| | if nbr not in seen: |
| | for k, dd in kd.items(): |
| | yield (n, nbr, k) |
| | seen[n] = 1 |
| | del seen |
| |
|
| |
|
| | class InMultiEdgeView(OutMultiEdgeView): |
| | """A EdgeView class for inward edges of a MultiDiGraph""" |
| |
|
| | __slots__ = () |
| |
|
| | def __setstate__(self, state): |
| | self._graph = state["_graph"] |
| | self._adjdict = state["_adjdict"] |
| | self._nodes_nbrs = self._adjdict.items |
| |
|
| | dataview = InMultiEdgeDataView |
| |
|
| | def __init__(self, G): |
| | self._graph = G |
| | self._adjdict = G._pred if hasattr(G, "pred") else G._adj |
| | self._nodes_nbrs = self._adjdict.items |
| |
|
| | def __iter__(self): |
| | for n, nbrs in self._nodes_nbrs(): |
| | for nbr, kdict in nbrs.items(): |
| | for key in kdict: |
| | yield (nbr, n, key) |
| |
|
| | def __contains__(self, e): |
| | N = len(e) |
| | if N == 3: |
| | u, v, k = e |
| | elif N == 2: |
| | u, v = e |
| | k = 0 |
| | else: |
| | raise ValueError("MultiEdge must have length 2 or 3") |
| | try: |
| | return k in self._adjdict[v][u] |
| | except KeyError: |
| | return False |
| |
|
| | def __getitem__(self, e): |
| | if isinstance(e, slice): |
| | raise nx.NetworkXError( |
| | f"{type(self).__name__} does not support slicing, " |
| | f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]" |
| | ) |
| | u, v, k = e |
| | return self._adjdict[v][u][k] |
| |
|