reportviews.py 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431
  1. """
  2. View Classes provide node, edge and degree "views" of a graph.
  3. Views for nodes, edges and degree are provided for all base graph classes.
  4. A view means a read-only object that is quick to create, automatically
  5. updated when the graph changes, and provides basic access like `n in V`,
  6. `for n in V`, `V[n]` and sometimes set operations.
  7. The views are read-only iterable containers that are updated as the
  8. graph is updated. As with dicts, the graph should not be updated
  9. while iterating through the view. Views can be iterated multiple times.
  10. Edge and Node views also allow data attribute lookup.
  11. The resulting attribute dict is writable as `G.edges[3, 4]['color']='red'`
  12. Degree views allow lookup of degree values for single nodes.
  13. Weighted degree is supported with the `weight` argument.
  14. NodeView
  15. ========
  16. `V = G.nodes` (or `V = G.nodes()`) allows `len(V)`, `n in V`, set
  17. operations e.g. "G.nodes & H.nodes", and `dd = G.nodes[n]`, where
  18. `dd` is the node data dict. Iteration is over the nodes by default.
  19. NodeDataView
  20. ============
  21. To iterate over (node, data) pairs, use arguments to `G.nodes()`
  22. to create a DataView e.g. `DV = G.nodes(data='color', default='red')`.
  23. The DataView iterates as `for n, color in DV` and allows
  24. `(n, 'red') in DV`. Using `DV = G.nodes(data=True)`, the DataViews
  25. use the full datadict in writeable form also allowing contain testing as
  26. `(n, {'color': 'red'}) in VD`. DataViews allow set operations when
  27. data attributes are hashable.
  28. DegreeView
  29. ==========
  30. `V = G.degree` allows iteration over (node, degree) pairs as well
  31. as lookup: `deg=V[n]`. There are many flavors of DegreeView
  32. for In/Out/Directed/Multi. For Directed Graphs, `G.degree`
  33. counts both in and out going edges. `G.out_degree` and
  34. `G.in_degree` count only specific directions.
  35. Weighted degree using edge data attributes is provide via
  36. `V = G.degree(weight='attr_name')` where any string with the
  37. attribute name can be used. `weight=None` is the default.
  38. No set operations are implemented for degrees, use NodeView.
  39. The argument `nbunch` restricts iteration to nodes in nbunch.
  40. The DegreeView can still lookup any node even if nbunch is specified.
  41. EdgeView
  42. ========
  43. `V = G.edges` or `V = G.edges()` allows iteration over edges as well as
  44. `e in V`, set operations and edge data lookup `dd = G.edges[2, 3]`.
  45. Iteration is over 2-tuples `(u, v)` for Graph/DiGraph. For multigraphs
  46. edges 3-tuples `(u, v, key)` are the default but 2-tuples can be obtained
  47. via `V = G.edges(keys=False)`.
  48. Set operations for directed graphs treat the edges as a set of 2-tuples.
  49. For undirected graphs, 2-tuples are not a unique representation of edges.
  50. So long as the set being compared to contains unique representations
  51. of its edges, the set operations will act as expected. If the other
  52. set contains both `(0, 1)` and `(1, 0)` however, the result of set
  53. operations may contain both representations of the same edge.
  54. EdgeDataView
  55. ============
  56. Edge data can be reported using an EdgeDataView typically created
  57. by calling an EdgeView: `DV = G.edges(data='weight', default=1)`.
  58. The EdgeDataView allows iteration over edge tuples, membership checking
  59. but no set operations.
  60. Iteration depends on `data` and `default` and for multigraph `keys`
  61. If `data is False` (the default) then iterate over 2-tuples `(u, v)`.
  62. If `data is True` iterate over 3-tuples `(u, v, datadict)`.
  63. Otherwise iterate over `(u, v, datadict.get(data, default))`.
  64. For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key`
  65. to create 3-tuples and 4-tuples.
  66. The argument `nbunch` restricts edges to those incident to nodes in nbunch.
  67. """
  68. from collections.abc import Mapping, Set
  69. import networkx as nx
  70. __all__ = [
  71. "NodeView",
  72. "NodeDataView",
  73. "EdgeView",
  74. "OutEdgeView",
  75. "InEdgeView",
  76. "EdgeDataView",
  77. "OutEdgeDataView",
  78. "InEdgeDataView",
  79. "MultiEdgeView",
  80. "OutMultiEdgeView",
  81. "InMultiEdgeView",
  82. "MultiEdgeDataView",
  83. "OutMultiEdgeDataView",
  84. "InMultiEdgeDataView",
  85. "DegreeView",
  86. "DiDegreeView",
  87. "InDegreeView",
  88. "OutDegreeView",
  89. "MultiDegreeView",
  90. "DiMultiDegreeView",
  91. "InMultiDegreeView",
  92. "OutMultiDegreeView",
  93. ]
  94. # NodeViews
  95. class NodeView(Mapping, Set):
  96. """A NodeView class to act as G.nodes for a NetworkX Graph
  97. Set operations act on the nodes without considering data.
  98. Iteration is over nodes. Node data can be looked up like a dict.
  99. Use NodeDataView to iterate over node data or to specify a data
  100. attribute for lookup. NodeDataView is created by calling the NodeView.
  101. Parameters
  102. ----------
  103. graph : NetworkX graph-like class
  104. Examples
  105. --------
  106. >>> G = nx.path_graph(3)
  107. >>> NV = G.nodes()
  108. >>> 2 in NV
  109. True
  110. >>> for n in NV:
  111. ... print(n)
  112. 0
  113. 1
  114. 2
  115. >>> assert NV & {1, 2, 3} == {1, 2}
  116. >>> G.add_node(2, color="blue")
  117. >>> NV[2]
  118. {'color': 'blue'}
  119. >>> G.add_node(8, color="red")
  120. >>> NDV = G.nodes(data=True)
  121. >>> (2, NV[2]) in NDV
  122. True
  123. >>> for n, dd in NDV:
  124. ... print((n, dd.get("color", "aqua")))
  125. (0, 'aqua')
  126. (1, 'aqua')
  127. (2, 'blue')
  128. (8, 'red')
  129. >>> NDV[2] == NV[2]
  130. True
  131. >>> NVdata = G.nodes(data="color", default="aqua")
  132. >>> (2, NVdata[2]) in NVdata
  133. True
  134. >>> for n, dd in NVdata:
  135. ... print((n, dd))
  136. (0, 'aqua')
  137. (1, 'aqua')
  138. (2, 'blue')
  139. (8, 'red')
  140. >>> NVdata[2] == NV[2] # NVdata gets 'color', NV gets datadict
  141. False
  142. """
  143. __slots__ = ("_nodes",)
  144. def __getstate__(self):
  145. return {"_nodes": self._nodes}
  146. def __setstate__(self, state):
  147. self._nodes = state["_nodes"]
  148. def __init__(self, graph):
  149. self._nodes = graph._node
  150. # Mapping methods
  151. def __len__(self):
  152. return len(self._nodes)
  153. def __iter__(self):
  154. return iter(self._nodes)
  155. def __getitem__(self, n):
  156. if isinstance(n, slice):
  157. raise nx.NetworkXError(
  158. f"{type(self).__name__} does not support slicing, "
  159. f"try list(G.nodes)[{n.start}:{n.stop}:{n.step}]"
  160. )
  161. return self._nodes[n]
  162. # Set methods
  163. def __contains__(self, n):
  164. return n in self._nodes
  165. @classmethod
  166. def _from_iterable(cls, it):
  167. return set(it)
  168. # DataView method
  169. def __call__(self, data=False, default=None):
  170. if data is False:
  171. return self
  172. return NodeDataView(self._nodes, data, default)
  173. def data(self, data=True, default=None):
  174. """
  175. Return a read-only view of node data.
  176. Parameters
  177. ----------
  178. data : bool or node data key, default=True
  179. If ``data=True`` (the default), return a `NodeDataView` object that
  180. maps each node to *all* of its attributes. `data` may also be an
  181. arbitrary key, in which case the `NodeDataView` maps each node to
  182. the value for the keyed attribute. In this case, if a node does
  183. not have the `data` attribute, the `default` value is used.
  184. default : object, default=None
  185. The value used when a node does not have a specific attribute.
  186. Returns
  187. -------
  188. NodeDataView
  189. The layout of the returned NodeDataView depends on the value of the
  190. `data` parameter.
  191. Notes
  192. -----
  193. If ``data=False``, returns a `NodeView` object without data.
  194. See Also
  195. --------
  196. NodeDataView
  197. Examples
  198. --------
  199. >>> G = nx.Graph()
  200. >>> G.add_nodes_from([
  201. ... (0, {"color": "red", "weight": 10}),
  202. ... (1, {"color": "blue"}),
  203. ... (2, {"color": "yellow", "weight": 2})
  204. ... ])
  205. Accessing node data with ``data=True`` (the default) returns a
  206. NodeDataView mapping each node to all of its attributes:
  207. >>> G.nodes.data()
  208. NodeDataView({0: {'color': 'red', 'weight': 10}, 1: {'color': 'blue'}, 2: {'color': 'yellow', 'weight': 2}})
  209. If `data` represents a key in the node attribute dict, a NodeDataView mapping
  210. the nodes to the value for that specific key is returned:
  211. >>> G.nodes.data("color")
  212. NodeDataView({0: 'red', 1: 'blue', 2: 'yellow'}, data='color')
  213. If a specific key is not found in an attribute dict, the value specified
  214. by `default` is returned:
  215. >>> G.nodes.data("weight", default=-999)
  216. NodeDataView({0: 10, 1: -999, 2: 2}, data='weight')
  217. Note that there is no check that the `data` key is in any of the
  218. node attribute dictionaries:
  219. >>> G.nodes.data("height")
  220. NodeDataView({0: None, 1: None, 2: None}, data='height')
  221. """
  222. if data is False:
  223. return self
  224. return NodeDataView(self._nodes, data, default)
  225. def __str__(self):
  226. return str(list(self))
  227. def __repr__(self):
  228. return f"{self.__class__.__name__}({tuple(self)})"
  229. class NodeDataView(Set):
  230. """A DataView class for nodes of a NetworkX Graph
  231. The main use for this class is to iterate through node-data pairs.
  232. The data can be the entire data-dictionary for each node, or it
  233. can be a specific attribute (with default) for each node.
  234. Set operations are enabled with NodeDataView, but don't work in
  235. cases where the data is not hashable. Use with caution.
  236. Typically, set operations on nodes use NodeView, not NodeDataView.
  237. That is, they use `G.nodes` instead of `G.nodes(data='foo')`.
  238. Parameters
  239. ==========
  240. graph : NetworkX graph-like class
  241. data : bool or string (default=False)
  242. default : object (default=None)
  243. """
  244. __slots__ = ("_nodes", "_data", "_default")
  245. def __getstate__(self):
  246. return {"_nodes": self._nodes, "_data": self._data, "_default": self._default}
  247. def __setstate__(self, state):
  248. self._nodes = state["_nodes"]
  249. self._data = state["_data"]
  250. self._default = state["_default"]
  251. def __init__(self, nodedict, data=False, default=None):
  252. self._nodes = nodedict
  253. self._data = data
  254. self._default = default
  255. @classmethod
  256. def _from_iterable(cls, it):
  257. try:
  258. return set(it)
  259. except TypeError as err:
  260. if "unhashable" in str(err):
  261. msg = " : Could be b/c data=True or your values are unhashable"
  262. raise TypeError(str(err) + msg) from err
  263. raise
  264. def __len__(self):
  265. return len(self._nodes)
  266. def __iter__(self):
  267. data = self._data
  268. if data is False:
  269. return iter(self._nodes)
  270. if data is True:
  271. return iter(self._nodes.items())
  272. return (
  273. (n, dd[data] if data in dd else self._default)
  274. for n, dd in self._nodes.items()
  275. )
  276. def __contains__(self, n):
  277. try:
  278. node_in = n in self._nodes
  279. except TypeError:
  280. n, d = n
  281. return n in self._nodes and self[n] == d
  282. if node_in is True:
  283. return node_in
  284. try:
  285. n, d = n
  286. except (TypeError, ValueError):
  287. return False
  288. return n in self._nodes and self[n] == d
  289. def __getitem__(self, n):
  290. if isinstance(n, slice):
  291. raise nx.NetworkXError(
  292. f"{type(self).__name__} does not support slicing, "
  293. f"try list(G.nodes.data())[{n.start}:{n.stop}:{n.step}]"
  294. )
  295. ddict = self._nodes[n]
  296. data = self._data
  297. if data is False or data is True:
  298. return ddict
  299. return ddict[data] if data in ddict else self._default
  300. def __str__(self):
  301. return str(list(self))
  302. def __repr__(self):
  303. name = self.__class__.__name__
  304. if self._data is False:
  305. return f"{name}({tuple(self)})"
  306. if self._data is True:
  307. return f"{name}({dict(self)})"
  308. return f"{name}({dict(self)}, data={self._data!r})"
  309. # DegreeViews
  310. class DiDegreeView:
  311. """A View class for degree of nodes in a NetworkX Graph
  312. The functionality is like dict.items() with (node, degree) pairs.
  313. Additional functionality includes read-only lookup of node degree,
  314. and calling with optional features nbunch (for only a subset of nodes)
  315. and weight (use edge weights to compute degree).
  316. Parameters
  317. ==========
  318. graph : NetworkX graph-like class
  319. nbunch : node, container of nodes, or None meaning all nodes (default=None)
  320. weight : bool or string (default=None)
  321. Notes
  322. -----
  323. DegreeView can still lookup any node even if nbunch is specified.
  324. Examples
  325. --------
  326. >>> G = nx.path_graph(3)
  327. >>> DV = G.degree()
  328. >>> assert DV[2] == 1
  329. >>> assert sum(deg for n, deg in DV) == 4
  330. >>> DVweight = G.degree(weight="span")
  331. >>> G.add_edge(1, 2, span=34)
  332. >>> DVweight[2]
  333. 34
  334. >>> DVweight[0] # default edge weight is 1
  335. 1
  336. >>> sum(span for n, span in DVweight) # sum weighted degrees
  337. 70
  338. >>> DVnbunch = G.degree(nbunch=(1, 2))
  339. >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only
  340. """
  341. def __init__(self, G, nbunch=None, weight=None):
  342. self._graph = G
  343. self._succ = G._succ if hasattr(G, "_succ") else G._adj
  344. self._pred = G._pred if hasattr(G, "_pred") else G._adj
  345. self._nodes = self._succ if nbunch is None else list(G.nbunch_iter(nbunch))
  346. self._weight = weight
  347. def __call__(self, nbunch=None, weight=None):
  348. if nbunch is None:
  349. if weight == self._weight:
  350. return self
  351. return self.__class__(self._graph, None, weight)
  352. try:
  353. if nbunch in self._nodes:
  354. if weight == self._weight:
  355. return self[nbunch]
  356. return self.__class__(self._graph, None, weight)[nbunch]
  357. except TypeError:
  358. pass
  359. return self.__class__(self._graph, nbunch, weight)
  360. def __getitem__(self, n):
  361. weight = self._weight
  362. succs = self._succ[n]
  363. preds = self._pred[n]
  364. if weight is None:
  365. return len(succs) + len(preds)
  366. return sum(dd.get(weight, 1) for dd in succs.values()) + sum(
  367. dd.get(weight, 1) for dd in preds.values()
  368. )
  369. def __iter__(self):
  370. weight = self._weight
  371. if weight is None:
  372. for n in self._nodes:
  373. succs = self._succ[n]
  374. preds = self._pred[n]
  375. yield (n, len(succs) + len(preds))
  376. else:
  377. for n in self._nodes:
  378. succs = self._succ[n]
  379. preds = self._pred[n]
  380. deg = sum(dd.get(weight, 1) for dd in succs.values()) + sum(
  381. dd.get(weight, 1) for dd in preds.values()
  382. )
  383. yield (n, deg)
  384. def __len__(self):
  385. return len(self._nodes)
  386. def __str__(self):
  387. return str(list(self))
  388. def __repr__(self):
  389. return f"{self.__class__.__name__}({dict(self)})"
  390. class DegreeView(DiDegreeView):
  391. """A DegreeView class to act as G.degree for a NetworkX Graph
  392. Typical usage focuses on iteration over `(node, degree)` pairs.
  393. The degree is by default the number of edges incident to the node.
  394. Optional argument `weight` enables weighted degree using the edge
  395. attribute named in the `weight` argument. Reporting and iteration
  396. can also be restricted to a subset of nodes using `nbunch`.
  397. Additional functionality include node lookup so that `G.degree[n]`
  398. reported the (possibly weighted) degree of node `n`. Calling the
  399. view creates a view with different arguments `nbunch` or `weight`.
  400. Parameters
  401. ==========
  402. graph : NetworkX graph-like class
  403. nbunch : node, container of nodes, or None meaning all nodes (default=None)
  404. weight : string or None (default=None)
  405. Notes
  406. -----
  407. DegreeView can still lookup any node even if nbunch is specified.
  408. Examples
  409. --------
  410. >>> G = nx.path_graph(3)
  411. >>> DV = G.degree()
  412. >>> assert DV[2] == 1
  413. >>> assert G.degree[2] == 1
  414. >>> assert sum(deg for n, deg in DV) == 4
  415. >>> DVweight = G.degree(weight="span")
  416. >>> G.add_edge(1, 2, span=34)
  417. >>> DVweight[2]
  418. 34
  419. >>> DVweight[0] # default edge weight is 1
  420. 1
  421. >>> sum(span for n, span in DVweight) # sum weighted degrees
  422. 70
  423. >>> DVnbunch = G.degree(nbunch=(1, 2))
  424. >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only
  425. """
  426. def __getitem__(self, n):
  427. weight = self._weight
  428. nbrs = self._succ[n]
  429. if weight is None:
  430. return len(nbrs) + (n in nbrs)
  431. return sum(dd.get(weight, 1) for dd in nbrs.values()) + (
  432. n in nbrs and nbrs[n].get(weight, 1)
  433. )
  434. def __iter__(self):
  435. weight = self._weight
  436. if weight is None:
  437. for n in self._nodes:
  438. nbrs = self._succ[n]
  439. yield (n, len(nbrs) + (n in nbrs))
  440. else:
  441. for n in self._nodes:
  442. nbrs = self._succ[n]
  443. deg = sum(dd.get(weight, 1) for dd in nbrs.values()) + (
  444. n in nbrs and nbrs[n].get(weight, 1)
  445. )
  446. yield (n, deg)
  447. class OutDegreeView(DiDegreeView):
  448. """A DegreeView class to report out_degree for a DiGraph; See DegreeView"""
  449. def __getitem__(self, n):
  450. weight = self._weight
  451. nbrs = self._succ[n]
  452. if self._weight is None:
  453. return len(nbrs)
  454. return sum(dd.get(self._weight, 1) for dd in nbrs.values())
  455. def __iter__(self):
  456. weight = self._weight
  457. if weight is None:
  458. for n in self._nodes:
  459. succs = self._succ[n]
  460. yield (n, len(succs))
  461. else:
  462. for n in self._nodes:
  463. succs = self._succ[n]
  464. deg = sum(dd.get(weight, 1) for dd in succs.values())
  465. yield (n, deg)
  466. class InDegreeView(DiDegreeView):
  467. """A DegreeView class to report in_degree for a DiGraph; See DegreeView"""
  468. def __getitem__(self, n):
  469. weight = self._weight
  470. nbrs = self._pred[n]
  471. if weight is None:
  472. return len(nbrs)
  473. return sum(dd.get(weight, 1) for dd in nbrs.values())
  474. def __iter__(self):
  475. weight = self._weight
  476. if weight is None:
  477. for n in self._nodes:
  478. preds = self._pred[n]
  479. yield (n, len(preds))
  480. else:
  481. for n in self._nodes:
  482. preds = self._pred[n]
  483. deg = sum(dd.get(weight, 1) for dd in preds.values())
  484. yield (n, deg)
  485. class MultiDegreeView(DiDegreeView):
  486. """A DegreeView class for undirected multigraphs; See DegreeView"""
  487. def __getitem__(self, n):
  488. weight = self._weight
  489. nbrs = self._succ[n]
  490. if weight is None:
  491. return sum(len(keys) for keys in nbrs.values()) + (
  492. n in nbrs and len(nbrs[n])
  493. )
  494. # edge weighted graph - degree is sum of nbr edge weights
  495. deg = sum(
  496. d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values()
  497. )
  498. if n in nbrs:
  499. deg += sum(d.get(weight, 1) for d in nbrs[n].values())
  500. return deg
  501. def __iter__(self):
  502. weight = self._weight
  503. if weight is None:
  504. for n in self._nodes:
  505. nbrs = self._succ[n]
  506. deg = sum(len(keys) for keys in nbrs.values()) + (
  507. n in nbrs and len(nbrs[n])
  508. )
  509. yield (n, deg)
  510. else:
  511. for n in self._nodes:
  512. nbrs = self._succ[n]
  513. deg = sum(
  514. d.get(weight, 1)
  515. for key_dict in nbrs.values()
  516. for d in key_dict.values()
  517. )
  518. if n in nbrs:
  519. deg += sum(d.get(weight, 1) for d in nbrs[n].values())
  520. yield (n, deg)
  521. class DiMultiDegreeView(DiDegreeView):
  522. """A DegreeView class for MultiDiGraph; See DegreeView"""
  523. def __getitem__(self, n):
  524. weight = self._weight
  525. succs = self._succ[n]
  526. preds = self._pred[n]
  527. if weight is None:
  528. return sum(len(keys) for keys in succs.values()) + sum(
  529. len(keys) for keys in preds.values()
  530. )
  531. # edge weighted graph - degree is sum of nbr edge weights
  532. deg = sum(
  533. d.get(weight, 1) for key_dict in succs.values() for d in key_dict.values()
  534. ) + sum(
  535. d.get(weight, 1) for key_dict in preds.values() for d in key_dict.values()
  536. )
  537. return deg
  538. def __iter__(self):
  539. weight = self._weight
  540. if weight is None:
  541. for n in self._nodes:
  542. succs = self._succ[n]
  543. preds = self._pred[n]
  544. deg = sum(len(keys) for keys in succs.values()) + sum(
  545. len(keys) for keys in preds.values()
  546. )
  547. yield (n, deg)
  548. else:
  549. for n in self._nodes:
  550. succs = self._succ[n]
  551. preds = self._pred[n]
  552. deg = sum(
  553. d.get(weight, 1)
  554. for key_dict in succs.values()
  555. for d in key_dict.values()
  556. ) + sum(
  557. d.get(weight, 1)
  558. for key_dict in preds.values()
  559. for d in key_dict.values()
  560. )
  561. yield (n, deg)
  562. class InMultiDegreeView(DiDegreeView):
  563. """A DegreeView class for inward degree of MultiDiGraph; See DegreeView"""
  564. def __getitem__(self, n):
  565. weight = self._weight
  566. nbrs = self._pred[n]
  567. if weight is None:
  568. return sum(len(data) for data in nbrs.values())
  569. # edge weighted graph - degree is sum of nbr edge weights
  570. return sum(
  571. d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values()
  572. )
  573. def __iter__(self):
  574. weight = self._weight
  575. if weight is None:
  576. for n in self._nodes:
  577. nbrs = self._pred[n]
  578. deg = sum(len(data) for data in nbrs.values())
  579. yield (n, deg)
  580. else:
  581. for n in self._nodes:
  582. nbrs = self._pred[n]
  583. deg = sum(
  584. d.get(weight, 1)
  585. for key_dict in nbrs.values()
  586. for d in key_dict.values()
  587. )
  588. yield (n, deg)
  589. class OutMultiDegreeView(DiDegreeView):
  590. """A DegreeView class for outward degree of MultiDiGraph; See DegreeView"""
  591. def __getitem__(self, n):
  592. weight = self._weight
  593. nbrs = self._succ[n]
  594. if weight is None:
  595. return sum(len(data) for data in nbrs.values())
  596. # edge weighted graph - degree is sum of nbr edge weights
  597. return sum(
  598. d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values()
  599. )
  600. def __iter__(self):
  601. weight = self._weight
  602. if weight is None:
  603. for n in self._nodes:
  604. nbrs = self._succ[n]
  605. deg = sum(len(data) for data in nbrs.values())
  606. yield (n, deg)
  607. else:
  608. for n in self._nodes:
  609. nbrs = self._succ[n]
  610. deg = sum(
  611. d.get(weight, 1)
  612. for key_dict in nbrs.values()
  613. for d in key_dict.values()
  614. )
  615. yield (n, deg)
  616. # EdgeDataViews
  617. class OutEdgeDataView:
  618. """EdgeDataView for outward edges of DiGraph; See EdgeDataView"""
  619. __slots__ = (
  620. "_viewer",
  621. "_nbunch",
  622. "_data",
  623. "_default",
  624. "_adjdict",
  625. "_nodes_nbrs",
  626. "_report",
  627. )
  628. def __getstate__(self):
  629. return {
  630. "viewer": self._viewer,
  631. "nbunch": self._nbunch,
  632. "data": self._data,
  633. "default": self._default,
  634. }
  635. def __setstate__(self, state):
  636. self.__init__(**state)
  637. def __init__(self, viewer, nbunch=None, data=False, default=None):
  638. self._viewer = viewer
  639. adjdict = self._adjdict = viewer._adjdict
  640. if nbunch is None:
  641. self._nodes_nbrs = adjdict.items
  642. else:
  643. # dict retains order of nodes but acts like a set
  644. nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch))
  645. self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch]
  646. self._nbunch = nbunch
  647. self._data = data
  648. self._default = default
  649. # Set _report based on data and default
  650. if data is True:
  651. self._report = lambda n, nbr, dd: (n, nbr, dd)
  652. elif data is False:
  653. self._report = lambda n, nbr, dd: (n, nbr)
  654. else: # data is attribute name
  655. self._report = (
  656. lambda n, nbr, dd: (n, nbr, dd[data])
  657. if data in dd
  658. else (n, nbr, default)
  659. )
  660. def __len__(self):
  661. return sum(len(nbrs) for n, nbrs in self._nodes_nbrs())
  662. def __iter__(self):
  663. return (
  664. self._report(n, nbr, dd)
  665. for n, nbrs in self._nodes_nbrs()
  666. for nbr, dd in nbrs.items()
  667. )
  668. def __contains__(self, e):
  669. u, v = e[:2]
  670. if self._nbunch is not None and u not in self._nbunch:
  671. return False # this edge doesn't start in nbunch
  672. try:
  673. ddict = self._adjdict[u][v]
  674. except KeyError:
  675. return False
  676. return e == self._report(u, v, ddict)
  677. def __str__(self):
  678. return str(list(self))
  679. def __repr__(self):
  680. return f"{self.__class__.__name__}({list(self)})"
  681. class EdgeDataView(OutEdgeDataView):
  682. """A EdgeDataView class for edges of Graph
  683. This view is primarily used to iterate over the edges reporting
  684. edges as node-tuples with edge data optionally reported. The
  685. argument `nbunch` allows restriction to edges incident to nodes
  686. in that container/singleton. The default (nbunch=None)
  687. reports all edges. The arguments `data` and `default` control
  688. what edge data is reported. The default `data is False` reports
  689. only node-tuples for each edge. If `data is True` the entire edge
  690. data dict is returned. Otherwise `data` is assumed to hold the name
  691. of the edge attribute to report with default `default` if that
  692. edge attribute is not present.
  693. Parameters
  694. ----------
  695. nbunch : container of nodes, node or None (default None)
  696. data : False, True or string (default False)
  697. default : default value (default None)
  698. Examples
  699. --------
  700. >>> G = nx.path_graph(3)
  701. >>> G.add_edge(1, 2, foo="bar")
  702. >>> list(G.edges(data="foo", default="biz"))
  703. [(0, 1, 'biz'), (1, 2, 'bar')]
  704. >>> assert (0, 1, "biz") in G.edges(data="foo", default="biz")
  705. """
  706. __slots__ = ()
  707. def __len__(self):
  708. return sum(1 for e in self)
  709. def __iter__(self):
  710. seen = {}
  711. for n, nbrs in self._nodes_nbrs():
  712. for nbr, dd in nbrs.items():
  713. if nbr not in seen:
  714. yield self._report(n, nbr, dd)
  715. seen[n] = 1
  716. del seen
  717. def __contains__(self, e):
  718. u, v = e[:2]
  719. if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch:
  720. return False # this edge doesn't start and it doesn't end in nbunch
  721. try:
  722. ddict = self._adjdict[u][v]
  723. except KeyError:
  724. return False
  725. return e == self._report(u, v, ddict)
  726. class InEdgeDataView(OutEdgeDataView):
  727. """An EdgeDataView class for outward edges of DiGraph; See EdgeDataView"""
  728. __slots__ = ()
  729. def __iter__(self):
  730. return (
  731. self._report(nbr, n, dd)
  732. for n, nbrs in self._nodes_nbrs()
  733. for nbr, dd in nbrs.items()
  734. )
  735. def __contains__(self, e):
  736. u, v = e[:2]
  737. if self._nbunch is not None and v not in self._nbunch:
  738. return False # this edge doesn't end in nbunch
  739. try:
  740. ddict = self._adjdict[v][u]
  741. except KeyError:
  742. return False
  743. return e == self._report(u, v, ddict)
  744. class OutMultiEdgeDataView(OutEdgeDataView):
  745. """An EdgeDataView for outward edges of MultiDiGraph; See EdgeDataView"""
  746. __slots__ = ("keys",)
  747. def __getstate__(self):
  748. return {
  749. "viewer": self._viewer,
  750. "nbunch": self._nbunch,
  751. "keys": self.keys,
  752. "data": self._data,
  753. "default": self._default,
  754. }
  755. def __setstate__(self, state):
  756. self.__init__(**state)
  757. def __init__(self, viewer, nbunch=None, data=False, keys=False, default=None):
  758. self._viewer = viewer
  759. adjdict = self._adjdict = viewer._adjdict
  760. self.keys = keys
  761. if nbunch is None:
  762. self._nodes_nbrs = adjdict.items
  763. else:
  764. # dict retains order of nodes but acts like a set
  765. nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch))
  766. self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch]
  767. self._nbunch = nbunch
  768. self._data = data
  769. self._default = default
  770. # Set _report based on data and default
  771. if data is True:
  772. if keys is True:
  773. self._report = lambda n, nbr, k, dd: (n, nbr, k, dd)
  774. else:
  775. self._report = lambda n, nbr, k, dd: (n, nbr, dd)
  776. elif data is False:
  777. if keys is True:
  778. self._report = lambda n, nbr, k, dd: (n, nbr, k)
  779. else:
  780. self._report = lambda n, nbr, k, dd: (n, nbr)
  781. else: # data is attribute name
  782. if keys is True:
  783. self._report = (
  784. lambda n, nbr, k, dd: (n, nbr, k, dd[data])
  785. if data in dd
  786. else (n, nbr, k, default)
  787. )
  788. else:
  789. self._report = (
  790. lambda n, nbr, k, dd: (n, nbr, dd[data])
  791. if data in dd
  792. else (n, nbr, default)
  793. )
  794. def __len__(self):
  795. return sum(1 for e in self)
  796. def __iter__(self):
  797. return (
  798. self._report(n, nbr, k, dd)
  799. for n, nbrs in self._nodes_nbrs()
  800. for nbr, kd in nbrs.items()
  801. for k, dd in kd.items()
  802. )
  803. def __contains__(self, e):
  804. u, v = e[:2]
  805. if self._nbunch is not None and u not in self._nbunch:
  806. return False # this edge doesn't start in nbunch
  807. try:
  808. kdict = self._adjdict[u][v]
  809. except KeyError:
  810. return False
  811. if self.keys is True:
  812. k = e[2]
  813. try:
  814. dd = kdict[k]
  815. except KeyError:
  816. return False
  817. return e == self._report(u, v, k, dd)
  818. return any(e == self._report(u, v, k, dd) for k, dd in kdict.items())
  819. class MultiEdgeDataView(OutMultiEdgeDataView):
  820. """An EdgeDataView class for edges of MultiGraph; See EdgeDataView"""
  821. __slots__ = ()
  822. def __iter__(self):
  823. seen = {}
  824. for n, nbrs in self._nodes_nbrs():
  825. for nbr, kd in nbrs.items():
  826. if nbr not in seen:
  827. for k, dd in kd.items():
  828. yield self._report(n, nbr, k, dd)
  829. seen[n] = 1
  830. del seen
  831. def __contains__(self, e):
  832. u, v = e[:2]
  833. if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch:
  834. return False # this edge doesn't start and doesn't end in nbunch
  835. try:
  836. kdict = self._adjdict[u][v]
  837. except KeyError:
  838. try:
  839. kdict = self._adjdict[v][u]
  840. except KeyError:
  841. return False
  842. if self.keys is True:
  843. k = e[2]
  844. try:
  845. dd = kdict[k]
  846. except KeyError:
  847. return False
  848. return e == self._report(u, v, k, dd)
  849. return any(e == self._report(u, v, k, dd) for k, dd in kdict.items())
  850. class InMultiEdgeDataView(OutMultiEdgeDataView):
  851. """An EdgeDataView for inward edges of MultiDiGraph; See EdgeDataView"""
  852. __slots__ = ()
  853. def __iter__(self):
  854. return (
  855. self._report(nbr, n, k, dd)
  856. for n, nbrs in self._nodes_nbrs()
  857. for nbr, kd in nbrs.items()
  858. for k, dd in kd.items()
  859. )
  860. def __contains__(self, e):
  861. u, v = e[:2]
  862. if self._nbunch is not None and v not in self._nbunch:
  863. return False # this edge doesn't end in nbunch
  864. try:
  865. kdict = self._adjdict[v][u]
  866. except KeyError:
  867. return False
  868. if self.keys is True:
  869. k = e[2]
  870. dd = kdict[k]
  871. return e == self._report(u, v, k, dd)
  872. return any(e == self._report(u, v, k, dd) for k, dd in kdict.items())
  873. # EdgeViews have set operations and no data reported
  874. class OutEdgeView(Set, Mapping):
  875. """A EdgeView class for outward edges of a DiGraph"""
  876. __slots__ = ("_adjdict", "_graph", "_nodes_nbrs")
  877. def __getstate__(self):
  878. return {"_graph": self._graph, "_adjdict": self._adjdict}
  879. def __setstate__(self, state):
  880. self._graph = state["_graph"]
  881. self._adjdict = state["_adjdict"]
  882. self._nodes_nbrs = self._adjdict.items
  883. @classmethod
  884. def _from_iterable(cls, it):
  885. return set(it)
  886. dataview = OutEdgeDataView
  887. def __init__(self, G):
  888. self._graph = G
  889. self._adjdict = G._succ if hasattr(G, "succ") else G._adj
  890. self._nodes_nbrs = self._adjdict.items
  891. # Set methods
  892. def __len__(self):
  893. return sum(len(nbrs) for n, nbrs in self._nodes_nbrs())
  894. def __iter__(self):
  895. for n, nbrs in self._nodes_nbrs():
  896. for nbr in nbrs:
  897. yield (n, nbr)
  898. def __contains__(self, e):
  899. try:
  900. u, v = e
  901. return v in self._adjdict[u]
  902. except KeyError:
  903. return False
  904. # Mapping Methods
  905. def __getitem__(self, e):
  906. if isinstance(e, slice):
  907. raise nx.NetworkXError(
  908. f"{type(self).__name__} does not support slicing, "
  909. f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]"
  910. )
  911. u, v = e
  912. return self._adjdict[u][v]
  913. # EdgeDataView methods
  914. def __call__(self, nbunch=None, data=False, default=None):
  915. if nbunch is None and data is False:
  916. return self
  917. return self.dataview(self, nbunch, data, default)
  918. def data(self, data=True, default=None, nbunch=None):
  919. """
  920. Return a read-only view of edge data.
  921. Parameters
  922. ----------
  923. data : bool or edge attribute key
  924. If ``data=True``, then the data view maps each edge to a dictionary
  925. containing all of its attributes. If `data` is a key in the edge
  926. dictionary, then the data view maps each edge to its value for
  927. the keyed attribute. In this case, if the edge doesn't have the
  928. attribute, the `default` value is returned.
  929. default : object, default=None
  930. The value used when an edge does not have a specific attribute
  931. nbunch : container of nodes, optional (default=None)
  932. Allows restriction to edges only involving certain nodes. All edges
  933. are considered by default.
  934. Returns
  935. -------
  936. dataview
  937. Returns an `EdgeDataView` for undirected Graphs, `OutEdgeDataView`
  938. for DiGraphs, `MultiEdgeDataView` for MultiGraphs and
  939. `OutMultiEdgeDataView` for MultiDiGraphs.
  940. Notes
  941. -----
  942. If ``data=False``, returns an `EdgeView` without any edge data.
  943. See Also
  944. --------
  945. EdgeDataView
  946. OutEdgeDataView
  947. MultiEdgeDataView
  948. OutMultiEdgeDataView
  949. Examples
  950. --------
  951. >>> G = nx.Graph()
  952. >>> G.add_edges_from([
  953. ... (0, 1, {"dist": 3, "capacity": 20}),
  954. ... (1, 2, {"dist": 4}),
  955. ... (2, 0, {"dist": 5})
  956. ... ])
  957. Accessing edge data with ``data=True`` (the default) returns an
  958. edge data view object listing each edge with all of its attributes:
  959. >>> G.edges.data()
  960. EdgeDataView([(0, 1, {'dist': 3, 'capacity': 20}), (0, 2, {'dist': 5}), (1, 2, {'dist': 4})])
  961. If `data` represents a key in the edge attribute dict, a dataview listing
  962. each edge with its value for that specific key is returned:
  963. >>> G.edges.data("dist")
  964. EdgeDataView([(0, 1, 3), (0, 2, 5), (1, 2, 4)])
  965. `nbunch` can be used to limit the edges:
  966. >>> G.edges.data("dist", nbunch=[0])
  967. EdgeDataView([(0, 1, 3), (0, 2, 5)])
  968. If a specific key is not found in an edge attribute dict, the value
  969. specified by `default` is used:
  970. >>> G.edges.data("capacity")
  971. EdgeDataView([(0, 1, 20), (0, 2, None), (1, 2, None)])
  972. Note that there is no check that the `data` key is present in any of
  973. the edge attribute dictionaries:
  974. >>> G.edges.data("speed")
  975. EdgeDataView([(0, 1, None), (0, 2, None), (1, 2, None)])
  976. """
  977. if nbunch is None and data is False:
  978. return self
  979. return self.dataview(self, nbunch, data, default)
  980. # String Methods
  981. def __str__(self):
  982. return str(list(self))
  983. def __repr__(self):
  984. return f"{self.__class__.__name__}({list(self)})"
  985. class EdgeView(OutEdgeView):
  986. """A EdgeView class for edges of a Graph
  987. This densely packed View allows iteration over edges, data lookup
  988. like a dict and set operations on edges represented by node-tuples.
  989. In addition, edge data can be controlled by calling this object
  990. possibly creating an EdgeDataView. Typically edges are iterated over
  991. and reported as `(u, v)` node tuples or `(u, v, key)` node/key tuples
  992. for multigraphs. Those edge representations can also be using to
  993. lookup the data dict for any edge. Set operations also are available
  994. where those tuples are the elements of the set.
  995. Calling this object with optional arguments `data`, `default` and `keys`
  996. controls the form of the tuple (see EdgeDataView). Optional argument
  997. `nbunch` allows restriction to edges only involving certain nodes.
  998. If `data is False` (the default) then iterate over 2-tuples `(u, v)`.
  999. If `data is True` iterate over 3-tuples `(u, v, datadict)`.
  1000. Otherwise iterate over `(u, v, datadict.get(data, default))`.
  1001. For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key` above.
  1002. Parameters
  1003. ==========
  1004. graph : NetworkX graph-like class
  1005. nbunch : (default= all nodes in graph) only report edges with these nodes
  1006. keys : (only for MultiGraph. default=False) report edge key in tuple
  1007. data : bool or string (default=False) see above
  1008. default : object (default=None)
  1009. Examples
  1010. ========
  1011. >>> G = nx.path_graph(4)
  1012. >>> EV = G.edges()
  1013. >>> (2, 3) in EV
  1014. True
  1015. >>> for u, v in EV:
  1016. ... print((u, v))
  1017. (0, 1)
  1018. (1, 2)
  1019. (2, 3)
  1020. >>> assert EV & {(1, 2), (3, 4)} == {(1, 2)}
  1021. >>> EVdata = G.edges(data="color", default="aqua")
  1022. >>> G.add_edge(2, 3, color="blue")
  1023. >>> assert (2, 3, "blue") in EVdata
  1024. >>> for u, v, c in EVdata:
  1025. ... print(f"({u}, {v}) has color: {c}")
  1026. (0, 1) has color: aqua
  1027. (1, 2) has color: aqua
  1028. (2, 3) has color: blue
  1029. >>> EVnbunch = G.edges(nbunch=2)
  1030. >>> assert (2, 3) in EVnbunch
  1031. >>> assert (0, 1) not in EVnbunch
  1032. >>> for u, v in EVnbunch:
  1033. ... assert u == 2 or v == 2
  1034. >>> MG = nx.path_graph(4, create_using=nx.MultiGraph)
  1035. >>> EVmulti = MG.edges(keys=True)
  1036. >>> (2, 3, 0) in EVmulti
  1037. True
  1038. >>> (2, 3) in EVmulti # 2-tuples work even when keys is True
  1039. True
  1040. >>> key = MG.add_edge(2, 3)
  1041. >>> for u, v, k in EVmulti:
  1042. ... print((u, v, k))
  1043. (0, 1, 0)
  1044. (1, 2, 0)
  1045. (2, 3, 0)
  1046. (2, 3, 1)
  1047. """
  1048. __slots__ = ()
  1049. dataview = EdgeDataView
  1050. def __len__(self):
  1051. num_nbrs = (len(nbrs) + (n in nbrs) for n, nbrs in self._nodes_nbrs())
  1052. return sum(num_nbrs) // 2
  1053. def __iter__(self):
  1054. seen = {}
  1055. for n, nbrs in self._nodes_nbrs():
  1056. for nbr in list(nbrs):
  1057. if nbr not in seen:
  1058. yield (n, nbr)
  1059. seen[n] = 1
  1060. del seen
  1061. def __contains__(self, e):
  1062. try:
  1063. u, v = e[:2]
  1064. return v in self._adjdict[u] or u in self._adjdict[v]
  1065. except (KeyError, ValueError):
  1066. return False
  1067. class InEdgeView(OutEdgeView):
  1068. """A EdgeView class for inward edges of a DiGraph"""
  1069. __slots__ = ()
  1070. def __setstate__(self, state):
  1071. self._graph = state["_graph"]
  1072. self._adjdict = state["_adjdict"]
  1073. self._nodes_nbrs = self._adjdict.items
  1074. dataview = InEdgeDataView
  1075. def __init__(self, G):
  1076. self._graph = G
  1077. self._adjdict = G._pred if hasattr(G, "pred") else G._adj
  1078. self._nodes_nbrs = self._adjdict.items
  1079. def __iter__(self):
  1080. for n, nbrs in self._nodes_nbrs():
  1081. for nbr in nbrs:
  1082. yield (nbr, n)
  1083. def __contains__(self, e):
  1084. try:
  1085. u, v = e
  1086. return u in self._adjdict[v]
  1087. except KeyError:
  1088. return False
  1089. def __getitem__(self, e):
  1090. if isinstance(e, slice):
  1091. raise nx.NetworkXError(
  1092. f"{type(self).__name__} does not support slicing, "
  1093. f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]"
  1094. )
  1095. u, v = e
  1096. return self._adjdict[v][u]
  1097. class OutMultiEdgeView(OutEdgeView):
  1098. """A EdgeView class for outward edges of a MultiDiGraph"""
  1099. __slots__ = ()
  1100. dataview = OutMultiEdgeDataView
  1101. def __len__(self):
  1102. return sum(
  1103. len(kdict) for n, nbrs in self._nodes_nbrs() for nbr, kdict in nbrs.items()
  1104. )
  1105. def __iter__(self):
  1106. for n, nbrs in self._nodes_nbrs():
  1107. for nbr, kdict in nbrs.items():
  1108. for key in kdict:
  1109. yield (n, nbr, key)
  1110. def __contains__(self, e):
  1111. N = len(e)
  1112. if N == 3:
  1113. u, v, k = e
  1114. elif N == 2:
  1115. u, v = e
  1116. k = 0
  1117. else:
  1118. raise ValueError("MultiEdge must have length 2 or 3")
  1119. try:
  1120. return k in self._adjdict[u][v]
  1121. except KeyError:
  1122. return False
  1123. def __getitem__(self, e):
  1124. if isinstance(e, slice):
  1125. raise nx.NetworkXError(
  1126. f"{type(self).__name__} does not support slicing, "
  1127. f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]"
  1128. )
  1129. u, v, k = e
  1130. return self._adjdict[u][v][k]
  1131. def __call__(self, nbunch=None, data=False, keys=False, default=None):
  1132. if nbunch is None and data is False and keys is True:
  1133. return self
  1134. return self.dataview(self, nbunch, data, keys, default)
  1135. def data(self, data=True, keys=False, default=None, nbunch=None):
  1136. if nbunch is None and data is False and keys is True:
  1137. return self
  1138. return self.dataview(self, nbunch, data, keys, default)
  1139. class MultiEdgeView(OutMultiEdgeView):
  1140. """A EdgeView class for edges of a MultiGraph"""
  1141. __slots__ = ()
  1142. dataview = MultiEdgeDataView
  1143. def __len__(self):
  1144. return sum(1 for e in self)
  1145. def __iter__(self):
  1146. seen = {}
  1147. for n, nbrs in self._nodes_nbrs():
  1148. for nbr, kd in nbrs.items():
  1149. if nbr not in seen:
  1150. for k, dd in kd.items():
  1151. yield (n, nbr, k)
  1152. seen[n] = 1
  1153. del seen
  1154. class InMultiEdgeView(OutMultiEdgeView):
  1155. """A EdgeView class for inward edges of a MultiDiGraph"""
  1156. __slots__ = ()
  1157. def __setstate__(self, state):
  1158. self._graph = state["_graph"]
  1159. self._adjdict = state["_adjdict"]
  1160. self._nodes_nbrs = self._adjdict.items
  1161. dataview = InMultiEdgeDataView
  1162. def __init__(self, G):
  1163. self._graph = G
  1164. self._adjdict = G._pred if hasattr(G, "pred") else G._adj
  1165. self._nodes_nbrs = self._adjdict.items
  1166. def __iter__(self):
  1167. for n, nbrs in self._nodes_nbrs():
  1168. for nbr, kdict in nbrs.items():
  1169. for key in kdict:
  1170. yield (nbr, n, key)
  1171. def __contains__(self, e):
  1172. N = len(e)
  1173. if N == 3:
  1174. u, v, k = e
  1175. elif N == 2:
  1176. u, v = e
  1177. k = 0
  1178. else:
  1179. raise ValueError("MultiEdge must have length 2 or 3")
  1180. try:
  1181. return k in self._adjdict[v][u]
  1182. except KeyError:
  1183. return False
  1184. def __getitem__(self, e):
  1185. if isinstance(e, slice):
  1186. raise nx.NetworkXError(
  1187. f"{type(self).__name__} does not support slicing, "
  1188. f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]"
  1189. )
  1190. u, v, k = e
  1191. return self._adjdict[v][u][k]