Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/networkx/classes/reportviews.py: 25%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

620 statements  

1""" 

2View Classes provide node, edge and degree "views" of a graph. 

3 

4As with dicts, the graph should not be updated while 

5iterating through the view. Views can be iterated multiple times. 

6 

7Views are quick-to-create, read-only iterable containers, 

8live (they reflect changes to the graph), and provide 

9Pythonic access patterns for all base graph classes: 

10 

11- set-like membership and set operations for nodes and edges (``n in G.nodes``, 

12 ``G.nodes & H.nodes``, ``(u, v) in G.edges``), 

13- iteration (``for n in G.nodes``, ``for u, v in G.edges``), 

14- mapping-style lookups (``G.nodes[n]`` returns the node attribute dict; 

15 ``G.edges[u, v]`` returns the edge attribute dict), 

16- data-filtered iteration via ``.data(...)`` and conversion to concrete 

17 containers via ``list(...)`` or ``dict(...)``. 

18 

19The resulting attribute dict is writable as ``G.edges[3, 4]['color'] = 'red'`` 

20Degree views allow lookup of degree values for single nodes. 

21Weighted degree is supported with the ``weight`` argument. 

22 

23NodeView 

24======== 

25 

26``V = G.nodes`` (or ``V = G.nodes()``) allows ``len(V)``, ``n in V``, set 

27operations e.g. ``G.nodes & H.nodes``, and ``dd = G.nodes[n]``, where 

28``dd`` is the node data dict. Iteration is over the nodes by default. 

29 

30NodeDataView 

31============ 

32 

33To iterate over ``(node, data)`` pairs, use arguments to ``G.nodes()`` 

34to create a DataView e.g. ``DV = G.nodes(data='color', default='red')``. 

35The DataView iterates as ``for n, color in DV`` and allows 

36``(n, 'red') in DV``. Using ``DV = G.nodes(data=True)``, the DataViews 

37use the full datadict in writeable form also allowing contain testing as 

38``(n, {'color': 'red'}) in VD``. DataViews allow set operations when 

39data attributes are hashable. 

40 

41DegreeView 

42========== 

43 

44``V = G.degree`` allows iteration over ``(node, degree)`` pairs as well 

45as lookup: ``deg = V[n]``. There are many flavors of DegreeView 

46for In/Out/Directed/Multi. For Directed Graphs, ``G.degree`` 

47counts both in and out going edges. ``G.out_degree`` and 

48``G.in_degree`` count only specific directions. 

49Weighted degree using edge data attributes is provide via 

50``V = G.degree(weight='attr_name')`` where any string with the 

51attribute name can be used. ``weight=None`` is the default. 

52No set operations are implemented for degrees, use NodeView. 

53 

54The argument ``nbunch`` restricts iteration to nodes in nbunch. 

55The DegreeView can still lookup any node even if nbunch is specified. 

56 

57EdgeView 

58======== 

59 

60``V = G.edges`` or ``V = G.edges()`` allows iteration over edges as well as 

61``e in V``, set operations and edge data lookup ``dd = G.edges[2, 3]``. 

62Iteration is over 2-tuples ``(u, v)`` for Graph/DiGraph. For multigraphs 

63edges 3-tuples ``(u, v, key)`` are the default but 2-tuples can be obtained 

64via ``V = G.edges(keys=False)``. 

65 

66Set operations for directed graphs treat the edges as a set of 2-tuples. 

67For undirected graphs, 2-tuples are not a unique representation of edges. 

68So long as the set being compared to contains unique representations 

69of its edges, the set operations will act as expected. If the other 

70set contains both ``(0, 1)`` and ``(1, 0)`` however, the result of set 

71operations may contain both representations of the same edge. 

72 

73EdgeDataView 

74============ 

75 

76Edge data can be reported using an EdgeDataView typically created 

77by calling an EdgeView: ``DV = G.edges(data='weight', default=1)``. 

78The EdgeDataView allows iteration over edge tuples, membership checking 

79but no set operations. 

80 

81Iteration depends on ``data`` and ``default`` and for multigraph ``keys`` 

82If ``data is False`` (the default) then iterate over 2-tuples ``(u, v)``. 

83If ``data is True`` iterate over 3-tuples ``(u, v, datadict)``. 

84Otherwise iterate over ``(u, v, datadict.get(data, default))``. 

85For Multigraphs, if ``keys is True``, replace ``u, v`` with `u, v, key` 

86to create 3-tuples and 4-tuples. 

87 

88The argument ``nbunch`` restricts edges to those incident to nodes in nbunch. 

89 

90Common usage patterns 

91===================== 

92 

93NodeView / NodeDataView 

94----------------------- 

95 

96- Use ``G.nodes`` when you need membership tests, set-like operations, or to 

97 look up a node's attribute dict with ``G.nodes[n]``. 

98- Use ``G.nodes(data=...)`` or ``G.nodes.data(attr_name, default=...)`` to 

99 iterate node/data pairs or to extract a single attribute for all nodes. 

100- Use ``list(G)`` or ``for node in G:`` to iterate over nodes. 

101 

102Example: 

103 

104.. code-block:: python 

105 

106 >>> G = nx.path_graph(3) 

107 >>> list(G.nodes) 

108 [0, 1, 2] 

109 >>> G.add_node(3, color="red") 

110 >>> list(G.nodes.data("color", default=None)) 

111 [(0, None), (1, None), (2, None), (3, 'red')] 

112 

113EdgeView / EdgeDataView 

114----------------------- 

115 

116- Use ``G.edges`` to iterate or test membership for edges. 

117- Call ``G.edges(data=...)`` or ``G.edges(nbunch=..., data=..., keys=...)`` to 

118 iterate edges with data, restrict to edges incident to a set of nodes, or 

119 include multigraph keys. 

120 

121.. note:: 

122 Iteration of ``G.edges()`` yields node pairs as 2-tuples even for multigraphs. 

123 Iteration of `G.edges` yields 3-tuples for multigraphs and 2-tuples otherwise. 

124 You can also use ``G.edges(keys=True, data=True)`` to receive 4-tuples 

125 ``(u, v, key, data)`` for multigraphs. 

126 

127Example: 

128 

129.. code-block:: python 

130 

131 >>> G = nx.Graph() 

132 >>> G.add_edge(0, 1, weight=3) 

133 >>> list(G.edges(data="weight", default=1)) 

134 [(0, 1, 3)] 

135 

136DegreeView 

137---------- 

138 

139- Use ``G.degree`` to iterate ``(node, degree)`` pairs or query a single node 

140 with ``G.degree[n]``. 

141- The function interface ``G.degree(nbunch=..., weight=...)`` allows: 

142 * ``weight="attr"`` to compute weighted degree using the named edge attribute, and 

143 * ``nbunch`` to restrict iteration to a subset of nodes while still allowing direct lookups. 

144 

145Example: 

146 

147.. code-block:: python 

148 

149 >>> G = nx.cycle_graph(4) 

150 >>> dict(G.degree()) 

151 {0: 2, 1: 2, 2: 2, 3: 2} 

152 >>> G.degree[0] 

153 2 

154 >>> # Add an edge with a weight attribute and compute weighted degrees: 

155 >>> G.add_edge(0, 1, weight=5) 

156 >>> dict(G.degree(weight="weight")) 

157 {0: 6, 1: 6, 2: 2, 3: 2} 

158 

159Degree Computation 

160================== 

161 

162NetworkX does not store a persistent degree value for each node. Instead, the 

163degree is computed when requested by examining the neighbor dictionary for a 

164node and counting or summing edge attributes as needed. For multigraphs the key 

165dict for each neighbor is scanned; for weighted degree the requested edge 

166attribute values are summed. 

167 

168Because computing degree accesses the adjacency structures, some applications 

169cache degree values in a separate dictionary to avoid repeated recomputation: 

170 

171.. code-block:: python 

172 

173 >>> G_degree = dict(G.degree) # make a cached snapshot of degrees 

174 

175If degrees are cached, update the cached dictionary when edges are modified. 

176 

177Performance & pitfalls 

178====================== 

179 

180- Views are **read-only** wrappers — they do not copy graph data. If you need a stable snapshot 

181 (for example when you will modify the graph while iterating), materialize the view using 

182 ``list(view)`` or ``dict(view)``. 

183- Avoid modifying the graph while iterating over a view (same rule as iterating a dict). 

184- DataViews that return full attribute dicts expose writable dicts (modifying those dicts 

185 modifies the underlying graph attributes). Use this intentionally. 

186- Edge set operations on undirected graphs use 2-tuple representations; be careful when 

187 comparing sets that may contain both ``(u, v)`` and ``(v, u)``. 

188- DegreeView with ``weight`` performs a sum over edge attributes and can be more expensive 

189 than unweighted degree calculations. 

190- When ``G`` will not change and you need many degree lookups, store 

191 the precomputed degrees using ``degrees = dict(G.degree)``. 

192""" 

193 

194from abc import ABC 

195from collections.abc import Mapping, Set 

196 

197import networkx as nx 

198 

199__all__ = [ 

200 "NodeView", 

201 "NodeDataView", 

202 "EdgeView", 

203 "OutEdgeView", 

204 "InEdgeView", 

205 "EdgeDataView", 

206 "OutEdgeDataView", 

207 "InEdgeDataView", 

208 "MultiEdgeView", 

209 "OutMultiEdgeView", 

210 "InMultiEdgeView", 

211 "MultiEdgeDataView", 

212 "OutMultiEdgeDataView", 

213 "InMultiEdgeDataView", 

214 "DegreeView", 

215 "DiDegreeView", 

216 "InDegreeView", 

217 "OutDegreeView", 

218 "MultiDegreeView", 

219 "DiMultiDegreeView", 

220 "InMultiDegreeView", 

221 "OutMultiDegreeView", 

222] 

223 

224 

225# NodeViews 

226class NodeView(Mapping, Set): 

227 """A NodeView class to act as G.nodes for a NetworkX Graph 

228 

229 Set operations act on the nodes without considering data. 

230 Iteration is over nodes. Node data can be looked up like a dict. 

231 Use NodeDataView to iterate over node data or to specify a data 

232 attribute for lookup. NodeDataView is created by calling the NodeView. 

233 

234 Parameters 

235 ---------- 

236 graph : NetworkX graph-like class 

237 

238 Examples 

239 -------- 

240 >>> G = nx.path_graph(3) 

241 >>> NV = G.nodes() 

242 >>> 2 in NV 

243 True 

244 >>> for n in NV: 

245 ... print(n) 

246 0 

247 1 

248 2 

249 >>> assert NV & {1, 2, 3} == {1, 2} 

250 

251 >>> G.add_node(2, color="blue") 

252 >>> NV[2] 

253 {'color': 'blue'} 

254 >>> G.add_node(8, color="red") 

255 >>> NDV = G.nodes(data=True) 

256 >>> (2, NV[2]) in NDV 

257 True 

258 >>> for n, dd in NDV: 

259 ... print((n, dd.get("color", "aqua"))) 

260 (0, 'aqua') 

261 (1, 'aqua') 

262 (2, 'blue') 

263 (8, 'red') 

264 >>> NDV[2] == NV[2] 

265 True 

266 

267 >>> NVdata = G.nodes(data="color", default="aqua") 

268 >>> (2, NVdata[2]) in NVdata 

269 True 

270 >>> for n, dd in NVdata: 

271 ... print((n, dd)) 

272 (0, 'aqua') 

273 (1, 'aqua') 

274 (2, 'blue') 

275 (8, 'red') 

276 >>> NVdata[2] == NV[2] # NVdata gets 'color', NV gets datadict 

277 False 

278 """ 

279 

280 __slots__ = ("_nodes",) 

281 

282 def __getstate__(self): 

283 return {"_nodes": self._nodes} 

284 

285 def __setstate__(self, state): 

286 self._nodes = state["_nodes"] 

287 

288 def __init__(self, graph): 

289 self._nodes = graph._node 

290 

291 # Mapping methods 

292 def __len__(self): 

293 return len(self._nodes) 

294 

295 def __iter__(self): 

296 return iter(self._nodes) 

297 

298 def __getitem__(self, n): 

299 if isinstance(n, slice): 

300 raise nx.NetworkXError( 

301 f"{type(self).__name__} does not support slicing, " 

302 f"try list(G.nodes)[{n.start}:{n.stop}:{n.step}]" 

303 ) 

304 return self._nodes[n] 

305 

306 # Set methods 

307 def __contains__(self, n): 

308 return n in self._nodes 

309 

310 @classmethod 

311 def _from_iterable(cls, it): 

312 return set(it) 

313 

314 # DataView method 

315 def __call__(self, data=False, default=None): 

316 if data is False: 

317 return self 

318 return NodeDataView(self._nodes, data, default) 

319 

320 def data(self, data=True, default=None): 

321 """ 

322 Return a read-only view of node data. 

323 

324 Parameters 

325 ---------- 

326 data : bool or node data key, default=True 

327 If ``data=True`` (the default), return a `NodeDataView` object that 

328 maps each node to *all* of its attributes. `data` may also be an 

329 arbitrary key, in which case the `NodeDataView` maps each node to 

330 the value for the keyed attribute. In this case, if a node does 

331 not have the `data` attribute, the `default` value is used. 

332 default : object, default=None 

333 The value used when a node does not have a specific attribute. 

334 

335 Returns 

336 ------- 

337 NodeDataView 

338 The layout of the returned NodeDataView depends on the value of the 

339 `data` parameter. 

340 

341 Notes 

342 ----- 

343 If ``data=False``, returns a `NodeView` object without data. 

344 

345 See Also 

346 -------- 

347 NodeDataView 

348 

349 Examples 

350 -------- 

351 >>> G = nx.Graph() 

352 >>> G.add_nodes_from( 

353 ... [ 

354 ... (0, {"color": "red", "weight": 10}), 

355 ... (1, {"color": "blue"}), 

356 ... (2, {"color": "yellow", "weight": 2}), 

357 ... ] 

358 ... ) 

359 

360 Accessing node data with ``data=True`` (the default) returns a 

361 NodeDataView mapping each node to all of its attributes: 

362 

363 >>> G.nodes.data() 

364 NodeDataView({0: {'color': 'red', 'weight': 10}, 1: {'color': 'blue'}, 2: {'color': 'yellow', 'weight': 2}}) 

365 

366 If `data` represents a key in the node attribute dict, a NodeDataView mapping 

367 the nodes to the value for that specific key is returned: 

368 

369 >>> G.nodes.data("color") 

370 NodeDataView({0: 'red', 1: 'blue', 2: 'yellow'}, data='color') 

371 

372 If a specific key is not found in an attribute dict, the value specified 

373 by `default` is returned: 

374 

375 >>> G.nodes.data("weight", default=-999) 

376 NodeDataView({0: 10, 1: -999, 2: 2}, data='weight') 

377 

378 Note that there is no check that the `data` key is in any of the 

379 node attribute dictionaries: 

380 

381 >>> G.nodes.data("height") 

382 NodeDataView({0: None, 1: None, 2: None}, data='height') 

383 """ 

384 if data is False: 

385 return self 

386 return NodeDataView(self._nodes, data, default) 

387 

388 def __str__(self): 

389 return str(list(self)) 

390 

391 def __repr__(self): 

392 return f"{self.__class__.__name__}({tuple(self)})" 

393 

394 

395class NodeDataView(Set): 

396 """A DataView class for nodes of a NetworkX Graph 

397 

398 The main use for this class is to iterate through node-data pairs. 

399 The data can be the entire data-dictionary for each node, or it 

400 can be a specific attribute (with default) for each node. 

401 Set operations are enabled with NodeDataView, but don't work in 

402 cases where the data is not hashable. Use with caution. 

403 Typically, set operations on nodes use NodeView, not NodeDataView. 

404 That is, they use `G.nodes` instead of `G.nodes(data='foo')`. 

405 

406 Parameters 

407 ========== 

408 graph : NetworkX graph-like class 

409 data : bool or string (default=False) 

410 default : object (default=None) 

411 """ 

412 

413 __slots__ = ("_nodes", "_data", "_default") 

414 

415 def __getstate__(self): 

416 return {"_nodes": self._nodes, "_data": self._data, "_default": self._default} 

417 

418 def __setstate__(self, state): 

419 self._nodes = state["_nodes"] 

420 self._data = state["_data"] 

421 self._default = state["_default"] 

422 

423 def __init__(self, nodedict, data=False, default=None): 

424 self._nodes = nodedict 

425 self._data = data 

426 self._default = default 

427 

428 @classmethod 

429 def _from_iterable(cls, it): 

430 try: 

431 return set(it) 

432 except TypeError as err: 

433 if "unhashable" in str(err): 

434 msg = " : Could be b/c data=True or your values are unhashable" 

435 raise TypeError(str(err) + msg) from err 

436 raise 

437 

438 def __len__(self): 

439 return len(self._nodes) 

440 

441 def __iter__(self): 

442 data = self._data 

443 if data is False: 

444 return iter(self._nodes) 

445 if data is True: 

446 return iter(self._nodes.items()) 

447 return ( 

448 (n, dd[data] if data in dd else self._default) 

449 for n, dd in self._nodes.items() 

450 ) 

451 

452 def __contains__(self, n): 

453 try: 

454 node_in = n in self._nodes 

455 except TypeError: 

456 n, d = n 

457 return n in self._nodes and self[n] == d 

458 if node_in is True: 

459 return node_in 

460 try: 

461 n, d = n 

462 except (TypeError, ValueError): 

463 return False 

464 return n in self._nodes and self[n] == d 

465 

466 def __getitem__(self, n): 

467 if isinstance(n, slice): 

468 raise nx.NetworkXError( 

469 f"{type(self).__name__} does not support slicing, " 

470 f"try list(G.nodes.data())[{n.start}:{n.stop}:{n.step}]" 

471 ) 

472 ddict = self._nodes[n] 

473 data = self._data 

474 if data is False or data is True: 

475 return ddict 

476 return ddict[data] if data in ddict else self._default 

477 

478 def __str__(self): 

479 return str(list(self)) 

480 

481 def __repr__(self): 

482 name = self.__class__.__name__ 

483 if self._data is False: 

484 return f"{name}({tuple(self)})" 

485 if self._data is True: 

486 return f"{name}({dict(self)})" 

487 return f"{name}({dict(self)}, data={self._data!r})" 

488 

489 

490# DegreeViews 

491class DiDegreeView: 

492 """A View class for degree of nodes in a NetworkX Graph 

493 

494 The functionality is like dict.items() with (node, degree) pairs. 

495 Additional functionality includes read-only lookup of node degree, 

496 and calling with optional features nbunch (for only a subset of nodes) 

497 and weight (use edge weights to compute degree). 

498 

499 Parameters 

500 ========== 

501 graph : NetworkX graph-like class 

502 nbunch : node, container of nodes, or None meaning all nodes (default=None) 

503 weight : bool or string (default=None) 

504 

505 Notes 

506 ----- 

507 DegreeView can still lookup any node even if nbunch is specified. 

508 

509 Examples 

510 -------- 

511 >>> G = nx.path_graph(3) 

512 >>> DV = G.degree() 

513 >>> assert DV[2] == 1 

514 >>> assert sum(deg for n, deg in DV) == 4 

515 

516 >>> DVweight = G.degree(weight="span") 

517 >>> G.add_edge(1, 2, span=34) 

518 >>> DVweight[2] 

519 34 

520 >>> DVweight[0] # default edge weight is 1 

521 1 

522 >>> sum(span for n, span in DVweight) # sum weighted degrees 

523 70 

524 

525 >>> DVnbunch = G.degree(nbunch=(1, 2)) 

526 >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only 

527 """ 

528 

529 def __init__(self, G, nbunch=None, weight=None): 

530 self._graph = G 

531 self._succ = G._succ if hasattr(G, "_succ") else G._adj 

532 self._pred = G._pred if hasattr(G, "_pred") else G._adj 

533 self._nodes = self._succ if nbunch is None else list(G.nbunch_iter(nbunch)) 

534 self._weight = weight 

535 

536 def __call__(self, nbunch=None, weight=None): 

537 if nbunch is None: 

538 if weight == self._weight: 

539 return self 

540 return self.__class__(self._graph, None, weight) 

541 try: 

542 if nbunch in self._nodes: 

543 if weight == self._weight: 

544 return self[nbunch] 

545 return self.__class__(self._graph, None, weight)[nbunch] 

546 except TypeError: 

547 pass 

548 return self.__class__(self._graph, nbunch, weight) 

549 

550 def __getitem__(self, n): 

551 weight = self._weight 

552 succs = self._succ[n] 

553 preds = self._pred[n] 

554 if weight is None: 

555 return len(succs) + len(preds) 

556 return sum(dd.get(weight, 1) for dd in succs.values()) + sum( 

557 dd.get(weight, 1) for dd in preds.values() 

558 ) 

559 

560 def __iter__(self): 

561 weight = self._weight 

562 if weight is None: 

563 for n in self._nodes: 

564 succs = self._succ[n] 

565 preds = self._pred[n] 

566 yield (n, len(succs) + len(preds)) 

567 else: 

568 for n in self._nodes: 

569 succs = self._succ[n] 

570 preds = self._pred[n] 

571 deg = sum(dd.get(weight, 1) for dd in succs.values()) + sum( 

572 dd.get(weight, 1) for dd in preds.values() 

573 ) 

574 yield (n, deg) 

575 

576 def __len__(self): 

577 return len(self._nodes) 

578 

579 def __str__(self): 

580 return str(list(self)) 

581 

582 def __repr__(self): 

583 return f"{self.__class__.__name__}({dict(self)})" 

584 

585 

586class DegreeView(DiDegreeView): 

587 """A DegreeView class to act as G.degree for a NetworkX Graph 

588 

589 Typical usage focuses on iteration over `(node, degree)` pairs. 

590 The degree is by default the number of edges incident to the node. 

591 Optional argument `weight` enables weighted degree using the edge 

592 attribute named in the `weight` argument. Reporting and iteration 

593 can also be restricted to a subset of nodes using `nbunch`. 

594 

595 Additional functionality include node lookup so that `G.degree[n]` 

596 reported the (possibly weighted) degree of node `n`. Calling the 

597 view creates a view with different arguments `nbunch` or `weight`. 

598 

599 Parameters 

600 ========== 

601 graph : NetworkX graph-like class 

602 nbunch : node, container of nodes, or None meaning all nodes (default=None) 

603 weight : string or None (default=None) 

604 

605 Notes 

606 ----- 

607 DegreeView can still lookup any node even if nbunch is specified. 

608 

609 Examples 

610 -------- 

611 >>> G = nx.path_graph(3) 

612 >>> DV = G.degree() 

613 >>> assert DV[2] == 1 

614 >>> assert G.degree[2] == 1 

615 >>> assert sum(deg for n, deg in DV) == 4 

616 

617 >>> DVweight = G.degree(weight="span") 

618 >>> G.add_edge(1, 2, span=34) 

619 >>> DVweight[2] 

620 34 

621 >>> DVweight[0] # default edge weight is 1 

622 1 

623 >>> sum(span for n, span in DVweight) # sum weighted degrees 

624 70 

625 

626 >>> DVnbunch = G.degree(nbunch=(1, 2)) 

627 >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only 

628 """ 

629 

630 def __getitem__(self, n): 

631 weight = self._weight 

632 nbrs = self._succ[n] 

633 if weight is None: 

634 return len(nbrs) + (n in nbrs) 

635 return sum(dd.get(weight, 1) for dd in nbrs.values()) + ( 

636 n in nbrs and nbrs[n].get(weight, 1) 

637 ) 

638 

639 def __iter__(self): 

640 weight = self._weight 

641 if weight is None: 

642 for n in self._nodes: 

643 nbrs = self._succ[n] 

644 yield (n, len(nbrs) + (n in nbrs)) 

645 else: 

646 for n in self._nodes: 

647 nbrs = self._succ[n] 

648 deg = sum(dd.get(weight, 1) for dd in nbrs.values()) + ( 

649 n in nbrs and nbrs[n].get(weight, 1) 

650 ) 

651 yield (n, deg) 

652 

653 

654class OutDegreeView(DiDegreeView): 

655 """A DegreeView class to report out_degree for a DiGraph; See DegreeView""" 

656 

657 def __getitem__(self, n): 

658 weight = self._weight 

659 nbrs = self._succ[n] 

660 if self._weight is None: 

661 return len(nbrs) 

662 return sum(dd.get(self._weight, 1) for dd in nbrs.values()) 

663 

664 def __iter__(self): 

665 weight = self._weight 

666 if weight is None: 

667 for n in self._nodes: 

668 succs = self._succ[n] 

669 yield (n, len(succs)) 

670 else: 

671 for n in self._nodes: 

672 succs = self._succ[n] 

673 deg = sum(dd.get(weight, 1) for dd in succs.values()) 

674 yield (n, deg) 

675 

676 

677class InDegreeView(DiDegreeView): 

678 """A DegreeView class to report in_degree for a DiGraph; See DegreeView""" 

679 

680 def __getitem__(self, n): 

681 weight = self._weight 

682 nbrs = self._pred[n] 

683 if weight is None: 

684 return len(nbrs) 

685 return sum(dd.get(weight, 1) for dd in nbrs.values()) 

686 

687 def __iter__(self): 

688 weight = self._weight 

689 if weight is None: 

690 for n in self._nodes: 

691 preds = self._pred[n] 

692 yield (n, len(preds)) 

693 else: 

694 for n in self._nodes: 

695 preds = self._pred[n] 

696 deg = sum(dd.get(weight, 1) for dd in preds.values()) 

697 yield (n, deg) 

698 

699 

700class MultiDegreeView(DiDegreeView): 

701 """A DegreeView class for undirected multigraphs; See DegreeView""" 

702 

703 def __getitem__(self, n): 

704 weight = self._weight 

705 nbrs = self._succ[n] 

706 if weight is None: 

707 return sum(len(keys) for keys in nbrs.values()) + ( 

708 n in nbrs and len(nbrs[n]) 

709 ) 

710 # edge weighted graph - degree is sum of nbr edge weights 

711 deg = sum( 

712 d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values() 

713 ) 

714 if n in nbrs: 

715 deg += sum(d.get(weight, 1) for d in nbrs[n].values()) 

716 return deg 

717 

718 def __iter__(self): 

719 weight = self._weight 

720 if weight is None: 

721 for n in self._nodes: 

722 nbrs = self._succ[n] 

723 deg = sum(len(keys) for keys in nbrs.values()) + ( 

724 n in nbrs and len(nbrs[n]) 

725 ) 

726 yield (n, deg) 

727 else: 

728 for n in self._nodes: 

729 nbrs = self._succ[n] 

730 deg = sum( 

731 d.get(weight, 1) 

732 for key_dict in nbrs.values() 

733 for d in key_dict.values() 

734 ) 

735 if n in nbrs: 

736 deg += sum(d.get(weight, 1) for d in nbrs[n].values()) 

737 yield (n, deg) 

738 

739 

740class DiMultiDegreeView(DiDegreeView): 

741 """A DegreeView class for MultiDiGraph; See DegreeView""" 

742 

743 def __getitem__(self, n): 

744 weight = self._weight 

745 succs = self._succ[n] 

746 preds = self._pred[n] 

747 if weight is None: 

748 return sum(len(keys) for keys in succs.values()) + sum( 

749 len(keys) for keys in preds.values() 

750 ) 

751 # edge weighted graph - degree is sum of nbr edge weights 

752 deg = sum( 

753 d.get(weight, 1) for key_dict in succs.values() for d in key_dict.values() 

754 ) + sum( 

755 d.get(weight, 1) for key_dict in preds.values() for d in key_dict.values() 

756 ) 

757 return deg 

758 

759 def __iter__(self): 

760 weight = self._weight 

761 if weight is None: 

762 for n in self._nodes: 

763 succs = self._succ[n] 

764 preds = self._pred[n] 

765 deg = sum(len(keys) for keys in succs.values()) + sum( 

766 len(keys) for keys in preds.values() 

767 ) 

768 yield (n, deg) 

769 else: 

770 for n in self._nodes: 

771 succs = self._succ[n] 

772 preds = self._pred[n] 

773 deg = sum( 

774 d.get(weight, 1) 

775 for key_dict in succs.values() 

776 for d in key_dict.values() 

777 ) + sum( 

778 d.get(weight, 1) 

779 for key_dict in preds.values() 

780 for d in key_dict.values() 

781 ) 

782 yield (n, deg) 

783 

784 

785class InMultiDegreeView(DiDegreeView): 

786 """A DegreeView class for inward degree of MultiDiGraph; See DegreeView""" 

787 

788 def __getitem__(self, n): 

789 weight = self._weight 

790 nbrs = self._pred[n] 

791 if weight is None: 

792 return sum(len(data) for data in nbrs.values()) 

793 # edge weighted graph - degree is sum of nbr edge weights 

794 return sum( 

795 d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values() 

796 ) 

797 

798 def __iter__(self): 

799 weight = self._weight 

800 if weight is None: 

801 for n in self._nodes: 

802 nbrs = self._pred[n] 

803 deg = sum(len(data) for data in nbrs.values()) 

804 yield (n, deg) 

805 else: 

806 for n in self._nodes: 

807 nbrs = self._pred[n] 

808 deg = sum( 

809 d.get(weight, 1) 

810 for key_dict in nbrs.values() 

811 for d in key_dict.values() 

812 ) 

813 yield (n, deg) 

814 

815 

816class OutMultiDegreeView(DiDegreeView): 

817 """A DegreeView class for outward degree of MultiDiGraph; See DegreeView""" 

818 

819 def __getitem__(self, n): 

820 weight = self._weight 

821 nbrs = self._succ[n] 

822 if weight is None: 

823 return sum(len(data) for data in nbrs.values()) 

824 # edge weighted graph - degree is sum of nbr edge weights 

825 return sum( 

826 d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values() 

827 ) 

828 

829 def __iter__(self): 

830 weight = self._weight 

831 if weight is None: 

832 for n in self._nodes: 

833 nbrs = self._succ[n] 

834 deg = sum(len(data) for data in nbrs.values()) 

835 yield (n, deg) 

836 else: 

837 for n in self._nodes: 

838 nbrs = self._succ[n] 

839 deg = sum( 

840 d.get(weight, 1) 

841 for key_dict in nbrs.values() 

842 for d in key_dict.values() 

843 ) 

844 yield (n, deg) 

845 

846 

847# A base class for all edge views. Ensures all edge view and edge data view 

848# objects/classes are captured by `isinstance(obj, EdgeViewABC)` and 

849# `issubclass(cls, EdgeViewABC)` respectively 

850class EdgeViewABC(ABC): 

851 pass 

852 

853 

854# EdgeDataViews 

855class OutEdgeDataView(EdgeViewABC): 

856 """EdgeDataView for outward edges of DiGraph; See EdgeDataView""" 

857 

858 __slots__ = ( 

859 "_viewer", 

860 "_nbunch", 

861 "_data", 

862 "_default", 

863 "_adjdict", 

864 "_nodes_nbrs", 

865 "_report", 

866 ) 

867 

868 def __getstate__(self): 

869 return { 

870 "viewer": self._viewer, 

871 "nbunch": self._nbunch, 

872 "data": self._data, 

873 "default": self._default, 

874 } 

875 

876 def __setstate__(self, state): 

877 self.__init__(**state) 

878 

879 def __init__(self, viewer, nbunch=None, data=False, *, default=None): 

880 self._viewer = viewer 

881 adjdict = self._adjdict = viewer._adjdict 

882 if nbunch is None: 

883 self._nodes_nbrs = adjdict.items 

884 else: 

885 # dict retains order of nodes but acts like a set 

886 nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch)) 

887 self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch] 

888 self._nbunch = nbunch 

889 self._data = data 

890 self._default = default 

891 # Set _report based on data and default 

892 if data is True: 

893 self._report = lambda n, nbr, dd: (n, nbr, dd) 

894 elif data is False: 

895 self._report = lambda n, nbr, dd: (n, nbr) 

896 else: # data is attribute name 

897 self._report = ( 

898 lambda n, nbr, dd: (n, nbr, dd[data]) 

899 if data in dd 

900 else (n, nbr, default) 

901 ) 

902 

903 def __len__(self): 

904 return sum(len(nbrs) for n, nbrs in self._nodes_nbrs()) 

905 

906 def __iter__(self): 

907 return ( 

908 self._report(n, nbr, dd) 

909 for n, nbrs in self._nodes_nbrs() 

910 for nbr, dd in nbrs.items() 

911 ) 

912 

913 def __contains__(self, e): 

914 u, v = e[:2] 

915 if self._nbunch is not None and u not in self._nbunch: 

916 return False # this edge doesn't start in nbunch 

917 try: 

918 ddict = self._adjdict[u][v] 

919 except KeyError: 

920 return False 

921 return e == self._report(u, v, ddict) 

922 

923 def __str__(self): 

924 return str(list(self)) 

925 

926 def __repr__(self): 

927 return f"{self.__class__.__name__}({list(self)})" 

928 

929 

930class EdgeDataView(OutEdgeDataView): 

931 """A EdgeDataView class for edges of Graph 

932 

933 This view is primarily used to iterate over the edges reporting 

934 edges as node-tuples with edge data optionally reported. The 

935 argument `nbunch` allows restriction to edges incident to nodes 

936 in that container/singleton. The default (nbunch=None) 

937 reports all edges. The arguments `data` and `default` control 

938 what edge data is reported. The default `data is False` reports 

939 only node-tuples for each edge. If `data is True` the entire edge 

940 data dict is returned. Otherwise `data` is assumed to hold the name 

941 of the edge attribute to report with default `default` if that 

942 edge attribute is not present. 

943 

944 Parameters 

945 ---------- 

946 nbunch : container of nodes, node or None (default None) 

947 data : False, True or string (default False) 

948 default : default value (default None) 

949 

950 Examples 

951 -------- 

952 >>> G = nx.path_graph(3) 

953 >>> G.add_edge(1, 2, foo="bar") 

954 >>> list(G.edges(data="foo", default="biz")) 

955 [(0, 1, 'biz'), (1, 2, 'bar')] 

956 >>> assert (0, 1, "biz") in G.edges(data="foo", default="biz") 

957 """ 

958 

959 __slots__ = () 

960 

961 def __len__(self): 

962 return sum(1 for e in self) 

963 

964 def __iter__(self): 

965 seen = {} 

966 for n, nbrs in self._nodes_nbrs(): 

967 for nbr, dd in nbrs.items(): 

968 if nbr not in seen: 

969 yield self._report(n, nbr, dd) 

970 seen[n] = 1 

971 del seen 

972 

973 def __contains__(self, e): 

974 u, v = e[:2] 

975 if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch: 

976 return False # this edge doesn't start and it doesn't end in nbunch 

977 try: 

978 ddict = self._adjdict[u][v] 

979 except KeyError: 

980 return False 

981 return e == self._report(u, v, ddict) 

982 

983 

984class InEdgeDataView(OutEdgeDataView): 

985 """An EdgeDataView class for outward edges of DiGraph; See EdgeDataView""" 

986 

987 __slots__ = () 

988 

989 def __iter__(self): 

990 return ( 

991 self._report(nbr, n, dd) 

992 for n, nbrs in self._nodes_nbrs() 

993 for nbr, dd in nbrs.items() 

994 ) 

995 

996 def __contains__(self, e): 

997 u, v = e[:2] 

998 if self._nbunch is not None and v not in self._nbunch: 

999 return False # this edge doesn't end in nbunch 

1000 try: 

1001 ddict = self._adjdict[v][u] 

1002 except KeyError: 

1003 return False 

1004 return e == self._report(u, v, ddict) 

1005 

1006 

1007class OutMultiEdgeDataView(OutEdgeDataView): 

1008 """An EdgeDataView for outward edges of MultiDiGraph; See EdgeDataView""" 

1009 

1010 __slots__ = ("keys",) 

1011 

1012 def __getstate__(self): 

1013 return { 

1014 "viewer": self._viewer, 

1015 "nbunch": self._nbunch, 

1016 "keys": self.keys, 

1017 "data": self._data, 

1018 "default": self._default, 

1019 } 

1020 

1021 def __setstate__(self, state): 

1022 self.__init__(**state) 

1023 

1024 def __init__(self, viewer, nbunch=None, data=False, *, default=None, keys=False): 

1025 self._viewer = viewer 

1026 adjdict = self._adjdict = viewer._adjdict 

1027 self.keys = keys 

1028 if nbunch is None: 

1029 self._nodes_nbrs = adjdict.items 

1030 else: 

1031 # dict retains order of nodes but acts like a set 

1032 nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch)) 

1033 self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch] 

1034 self._nbunch = nbunch 

1035 self._data = data 

1036 self._default = default 

1037 # Set _report based on data and default 

1038 if data is True: 

1039 if keys is True: 

1040 self._report = lambda n, nbr, k, dd: (n, nbr, k, dd) 

1041 else: 

1042 self._report = lambda n, nbr, k, dd: (n, nbr, dd) 

1043 elif data is False: 

1044 if keys is True: 

1045 self._report = lambda n, nbr, k, dd: (n, nbr, k) 

1046 else: 

1047 self._report = lambda n, nbr, k, dd: (n, nbr) 

1048 else: # data is attribute name 

1049 if keys is True: 

1050 self._report = ( 

1051 lambda n, nbr, k, dd: (n, nbr, k, dd[data]) 

1052 if data in dd 

1053 else (n, nbr, k, default) 

1054 ) 

1055 else: 

1056 self._report = ( 

1057 lambda n, nbr, k, dd: (n, nbr, dd[data]) 

1058 if data in dd 

1059 else (n, nbr, default) 

1060 ) 

1061 

1062 def __len__(self): 

1063 return sum(1 for e in self) 

1064 

1065 def __iter__(self): 

1066 return ( 

1067 self._report(n, nbr, k, dd) 

1068 for n, nbrs in self._nodes_nbrs() 

1069 for nbr, kd in nbrs.items() 

1070 for k, dd in kd.items() 

1071 ) 

1072 

1073 def __contains__(self, e): 

1074 u, v = e[:2] 

1075 if self._nbunch is not None and u not in self._nbunch: 

1076 return False # this edge doesn't start in nbunch 

1077 try: 

1078 kdict = self._adjdict[u][v] 

1079 except KeyError: 

1080 return False 

1081 if self.keys is True: 

1082 k = e[2] 

1083 try: 

1084 dd = kdict[k] 

1085 except KeyError: 

1086 return False 

1087 return e == self._report(u, v, k, dd) 

1088 return any(e == self._report(u, v, k, dd) for k, dd in kdict.items()) 

1089 

1090 

1091class MultiEdgeDataView(OutMultiEdgeDataView): 

1092 """An EdgeDataView class for edges of MultiGraph; See EdgeDataView""" 

1093 

1094 __slots__ = () 

1095 

1096 def __iter__(self): 

1097 seen = {} 

1098 for n, nbrs in self._nodes_nbrs(): 

1099 for nbr, kd in nbrs.items(): 

1100 if nbr not in seen: 

1101 for k, dd in kd.items(): 

1102 yield self._report(n, nbr, k, dd) 

1103 seen[n] = 1 

1104 del seen 

1105 

1106 def __contains__(self, e): 

1107 u, v = e[:2] 

1108 if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch: 

1109 return False # this edge doesn't start and doesn't end in nbunch 

1110 try: 

1111 kdict = self._adjdict[u][v] 

1112 except KeyError: 

1113 try: 

1114 kdict = self._adjdict[v][u] 

1115 except KeyError: 

1116 return False 

1117 if self.keys is True: 

1118 k = e[2] 

1119 try: 

1120 dd = kdict[k] 

1121 except KeyError: 

1122 return False 

1123 return e == self._report(u, v, k, dd) 

1124 return any(e == self._report(u, v, k, dd) for k, dd in kdict.items()) 

1125 

1126 

1127class InMultiEdgeDataView(OutMultiEdgeDataView): 

1128 """An EdgeDataView for inward edges of MultiDiGraph; See EdgeDataView""" 

1129 

1130 __slots__ = () 

1131 

1132 def __iter__(self): 

1133 return ( 

1134 self._report(nbr, n, k, dd) 

1135 for n, nbrs in self._nodes_nbrs() 

1136 for nbr, kd in nbrs.items() 

1137 for k, dd in kd.items() 

1138 ) 

1139 

1140 def __contains__(self, e): 

1141 u, v = e[:2] 

1142 if self._nbunch is not None and v not in self._nbunch: 

1143 return False # this edge doesn't end in nbunch 

1144 try: 

1145 kdict = self._adjdict[v][u] 

1146 except KeyError: 

1147 return False 

1148 if self.keys is True: 

1149 k = e[2] 

1150 dd = kdict[k] 

1151 return e == self._report(u, v, k, dd) 

1152 return any(e == self._report(u, v, k, dd) for k, dd in kdict.items()) 

1153 

1154 

1155# EdgeViews have set operations and no data reported 

1156class OutEdgeView(Set, Mapping, EdgeViewABC): 

1157 """A EdgeView class for outward edges of a DiGraph""" 

1158 

1159 __slots__ = ("_adjdict", "_graph", "_nodes_nbrs") 

1160 

1161 def __getstate__(self): 

1162 return {"_graph": self._graph, "_adjdict": self._adjdict} 

1163 

1164 def __setstate__(self, state): 

1165 self._graph = state["_graph"] 

1166 self._adjdict = state["_adjdict"] 

1167 self._nodes_nbrs = self._adjdict.items 

1168 

1169 @classmethod 

1170 def _from_iterable(cls, it): 

1171 return set(it) 

1172 

1173 dataview = OutEdgeDataView 

1174 

1175 def __init__(self, G): 

1176 self._graph = G 

1177 self._adjdict = G._succ if hasattr(G, "succ") else G._adj 

1178 self._nodes_nbrs = self._adjdict.items 

1179 

1180 # Set methods 

1181 def __len__(self): 

1182 return sum(len(nbrs) for n, nbrs in self._nodes_nbrs()) 

1183 

1184 def __iter__(self): 

1185 for n, nbrs in self._nodes_nbrs(): 

1186 for nbr in nbrs: 

1187 yield (n, nbr) 

1188 

1189 def __contains__(self, e): 

1190 try: 

1191 u, v = e 

1192 return v in self._adjdict[u] 

1193 except KeyError: 

1194 return False 

1195 

1196 # Mapping Methods 

1197 def __getitem__(self, e): 

1198 if isinstance(e, slice): 

1199 raise nx.NetworkXError( 

1200 f"{type(self).__name__} does not support slicing, " 

1201 f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]" 

1202 ) 

1203 u, v = e 

1204 try: 

1205 return self._adjdict[u][v] 

1206 except KeyError as ex: # Customize msg to indicate exception origin 

1207 raise KeyError(f"The edge {e} is not in the graph.") 

1208 

1209 # EdgeDataView methods 

1210 def __call__(self, nbunch=None, data=False, *, default=None): 

1211 if nbunch is None and data is False: 

1212 return self 

1213 return self.dataview(self, nbunch, data, default=default) 

1214 

1215 def data(self, data=True, default=None, nbunch=None): 

1216 """ 

1217 Return a read-only view of edge data. 

1218 

1219 Parameters 

1220 ---------- 

1221 data : bool or edge attribute key 

1222 If ``data=True``, then the data view maps each edge to a dictionary 

1223 containing all of its attributes. If `data` is a key in the edge 

1224 dictionary, then the data view maps each edge to its value for 

1225 the keyed attribute. In this case, if the edge doesn't have the 

1226 attribute, the `default` value is returned. 

1227 default : object, default=None 

1228 The value used when an edge does not have a specific attribute 

1229 nbunch : container of nodes, optional (default=None) 

1230 Allows restriction to edges only involving certain nodes. All edges 

1231 are considered by default. 

1232 

1233 Returns 

1234 ------- 

1235 dataview 

1236 Returns an `EdgeDataView` for undirected Graphs, `OutEdgeDataView` 

1237 for DiGraphs, `MultiEdgeDataView` for MultiGraphs and 

1238 `OutMultiEdgeDataView` for MultiDiGraphs. 

1239 

1240 Notes 

1241 ----- 

1242 If ``data=False``, returns an `EdgeView` without any edge data. 

1243 

1244 See Also 

1245 -------- 

1246 EdgeDataView 

1247 OutEdgeDataView 

1248 MultiEdgeDataView 

1249 OutMultiEdgeDataView 

1250 

1251 Examples 

1252 -------- 

1253 >>> G = nx.Graph() 

1254 >>> G.add_edges_from( 

1255 ... [ 

1256 ... (0, 1, {"dist": 3, "capacity": 20}), 

1257 ... (1, 2, {"dist": 4}), 

1258 ... (2, 0, {"dist": 5}), 

1259 ... ] 

1260 ... ) 

1261 

1262 Accessing edge data with ``data=True`` (the default) returns an 

1263 edge data view object listing each edge with all of its attributes: 

1264 

1265 >>> G.edges.data() 

1266 EdgeDataView([(0, 1, {'dist': 3, 'capacity': 20}), (0, 2, {'dist': 5}), (1, 2, {'dist': 4})]) 

1267 

1268 If `data` represents a key in the edge attribute dict, a dataview listing 

1269 each edge with its value for that specific key is returned: 

1270 

1271 >>> G.edges.data("dist") 

1272 EdgeDataView([(0, 1, 3), (0, 2, 5), (1, 2, 4)]) 

1273 

1274 `nbunch` can be used to limit the edges: 

1275 

1276 >>> G.edges.data("dist", nbunch=[0]) 

1277 EdgeDataView([(0, 1, 3), (0, 2, 5)]) 

1278 

1279 If a specific key is not found in an edge attribute dict, the value 

1280 specified by `default` is used: 

1281 

1282 >>> G.edges.data("capacity") 

1283 EdgeDataView([(0, 1, 20), (0, 2, None), (1, 2, None)]) 

1284 

1285 Note that there is no check that the `data` key is present in any of 

1286 the edge attribute dictionaries: 

1287 

1288 >>> G.edges.data("speed") 

1289 EdgeDataView([(0, 1, None), (0, 2, None), (1, 2, None)]) 

1290 """ 

1291 if nbunch is None and data is False: 

1292 return self 

1293 return self.dataview(self, nbunch, data, default=default) 

1294 

1295 # String Methods 

1296 def __str__(self): 

1297 return str(list(self)) 

1298 

1299 def __repr__(self): 

1300 return f"{self.__class__.__name__}({list(self)})" 

1301 

1302 

1303class EdgeView(OutEdgeView): 

1304 """A EdgeView class for edges of a Graph 

1305 

1306 This densely packed View allows iteration over edges, data lookup 

1307 like a dict and set operations on edges represented by node-tuples. 

1308 In addition, edge data can be controlled by calling this object 

1309 possibly creating an EdgeDataView. Typically edges are iterated over 

1310 and reported as `(u, v)` node tuples or `(u, v, key)` node/key tuples 

1311 for multigraphs. Those edge representations can also be using to 

1312 lookup the data dict for any edge. Set operations also are available 

1313 where those tuples are the elements of the set. 

1314 Calling this object with optional arguments `data`, `default` and `keys` 

1315 controls the form of the tuple (see EdgeDataView). Optional argument 

1316 `nbunch` allows restriction to edges only involving certain nodes. 

1317 

1318 If `data is False` (the default) then iterate over 2-tuples `(u, v)`. 

1319 If `data is True` iterate over 3-tuples `(u, v, datadict)`. 

1320 Otherwise iterate over `(u, v, datadict.get(data, default))`. 

1321 For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key` above. 

1322 

1323 Parameters 

1324 ========== 

1325 graph : NetworkX graph-like class 

1326 nbunch : (default= all nodes in graph) only report edges with these nodes 

1327 keys : (only for MultiGraph. default=False) report edge key in tuple 

1328 data : bool or string (default=False) see above 

1329 default : object (default=None) 

1330 

1331 Examples 

1332 ======== 

1333 >>> G = nx.path_graph(4) 

1334 >>> EV = G.edges() 

1335 >>> (2, 3) in EV 

1336 True 

1337 >>> for u, v in EV: 

1338 ... print((u, v)) 

1339 (0, 1) 

1340 (1, 2) 

1341 (2, 3) 

1342 >>> assert EV & {(1, 2), (3, 4)} == {(1, 2)} 

1343 

1344 >>> EVdata = G.edges(data="color", default="aqua") 

1345 >>> G.add_edge(2, 3, color="blue") 

1346 >>> assert (2, 3, "blue") in EVdata 

1347 >>> for u, v, c in EVdata: 

1348 ... print(f"({u}, {v}) has color: {c}") 

1349 (0, 1) has color: aqua 

1350 (1, 2) has color: aqua 

1351 (2, 3) has color: blue 

1352 

1353 >>> EVnbunch = G.edges(nbunch=2) 

1354 >>> assert (2, 3) in EVnbunch 

1355 >>> assert (0, 1) not in EVnbunch 

1356 >>> for u, v in EVnbunch: 

1357 ... assert u == 2 or v == 2 

1358 

1359 >>> MG = nx.path_graph(4, create_using=nx.MultiGraph) 

1360 >>> EVmulti = MG.edges(keys=True) 

1361 >>> (2, 3, 0) in EVmulti 

1362 True 

1363 >>> (2, 3) in EVmulti # 2-tuples work even when keys is True 

1364 True 

1365 >>> key = MG.add_edge(2, 3) 

1366 >>> for u, v, k in EVmulti: 

1367 ... print((u, v, k)) 

1368 (0, 1, 0) 

1369 (1, 2, 0) 

1370 (2, 3, 0) 

1371 (2, 3, 1) 

1372 """ 

1373 

1374 __slots__ = () 

1375 

1376 dataview = EdgeDataView 

1377 

1378 def __len__(self): 

1379 num_nbrs = (len(nbrs) + (n in nbrs) for n, nbrs in self._nodes_nbrs()) 

1380 return sum(num_nbrs) // 2 

1381 

1382 def __iter__(self): 

1383 seen = {} 

1384 for n, nbrs in self._nodes_nbrs(): 

1385 for nbr in list(nbrs): 

1386 if nbr not in seen: 

1387 yield (n, nbr) 

1388 seen[n] = 1 

1389 del seen 

1390 

1391 def __contains__(self, e): 

1392 try: 

1393 u, v = e[:2] 

1394 return v in self._adjdict[u] or u in self._adjdict[v] 

1395 except (KeyError, ValueError): 

1396 return False 

1397 

1398 

1399class InEdgeView(OutEdgeView): 

1400 """A EdgeView class for inward edges of a DiGraph""" 

1401 

1402 __slots__ = () 

1403 

1404 def __setstate__(self, state): 

1405 self._graph = state["_graph"] 

1406 self._adjdict = state["_adjdict"] 

1407 self._nodes_nbrs = self._adjdict.items 

1408 

1409 dataview = InEdgeDataView 

1410 

1411 def __init__(self, G): 

1412 self._graph = G 

1413 self._adjdict = G._pred if hasattr(G, "pred") else G._adj 

1414 self._nodes_nbrs = self._adjdict.items 

1415 

1416 def __iter__(self): 

1417 for n, nbrs in self._nodes_nbrs(): 

1418 for nbr in nbrs: 

1419 yield (nbr, n) 

1420 

1421 def __contains__(self, e): 

1422 try: 

1423 u, v = e 

1424 return u in self._adjdict[v] 

1425 except KeyError: 

1426 return False 

1427 

1428 def __getitem__(self, e): 

1429 if isinstance(e, slice): 

1430 raise nx.NetworkXError( 

1431 f"{type(self).__name__} does not support slicing, " 

1432 f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]" 

1433 ) 

1434 u, v = e 

1435 return self._adjdict[v][u] 

1436 

1437 

1438class OutMultiEdgeView(OutEdgeView): 

1439 """A EdgeView class for outward edges of a MultiDiGraph""" 

1440 

1441 __slots__ = () 

1442 

1443 dataview = OutMultiEdgeDataView 

1444 

1445 def __len__(self): 

1446 return sum( 

1447 len(kdict) for n, nbrs in self._nodes_nbrs() for nbr, kdict in nbrs.items() 

1448 ) 

1449 

1450 def __iter__(self): 

1451 for n, nbrs in self._nodes_nbrs(): 

1452 for nbr, kdict in nbrs.items(): 

1453 for key in kdict: 

1454 yield (n, nbr, key) 

1455 

1456 def __contains__(self, e): 

1457 N = len(e) 

1458 if N == 3: 

1459 u, v, k = e 

1460 elif N == 2: 

1461 u, v = e 

1462 k = 0 

1463 else: 

1464 raise ValueError("MultiEdge must have length 2 or 3") 

1465 try: 

1466 return k in self._adjdict[u][v] 

1467 except KeyError: 

1468 return False 

1469 

1470 def __getitem__(self, e): 

1471 if isinstance(e, slice): 

1472 raise nx.NetworkXError( 

1473 f"{type(self).__name__} does not support slicing, " 

1474 f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]" 

1475 ) 

1476 u, v, k = e 

1477 return self._adjdict[u][v][k] 

1478 

1479 def __call__(self, nbunch=None, data=False, *, default=None, keys=False): 

1480 if nbunch is None and data is False and keys is True: 

1481 return self 

1482 return self.dataview(self, nbunch, data, default=default, keys=keys) 

1483 

1484 def data(self, data=True, default=None, nbunch=None, keys=False): 

1485 if nbunch is None and data is False and keys is True: 

1486 return self 

1487 return self.dataview(self, nbunch, data, default=default, keys=keys) 

1488 

1489 

1490class MultiEdgeView(OutMultiEdgeView): 

1491 """A EdgeView class for edges of a MultiGraph""" 

1492 

1493 __slots__ = () 

1494 

1495 dataview = MultiEdgeDataView 

1496 

1497 def __len__(self): 

1498 return sum(1 for e in self) 

1499 

1500 def __iter__(self): 

1501 seen = {} 

1502 for n, nbrs in self._nodes_nbrs(): 

1503 for nbr, kd in nbrs.items(): 

1504 if nbr not in seen: 

1505 for k, dd in kd.items(): 

1506 yield (n, nbr, k) 

1507 seen[n] = 1 

1508 del seen 

1509 

1510 

1511class InMultiEdgeView(OutMultiEdgeView): 

1512 """A EdgeView class for inward edges of a MultiDiGraph""" 

1513 

1514 __slots__ = () 

1515 

1516 def __setstate__(self, state): 

1517 self._graph = state["_graph"] 

1518 self._adjdict = state["_adjdict"] 

1519 self._nodes_nbrs = self._adjdict.items 

1520 

1521 dataview = InMultiEdgeDataView 

1522 

1523 def __init__(self, G): 

1524 self._graph = G 

1525 self._adjdict = G._pred if hasattr(G, "pred") else G._adj 

1526 self._nodes_nbrs = self._adjdict.items 

1527 

1528 def __iter__(self): 

1529 for n, nbrs in self._nodes_nbrs(): 

1530 for nbr, kdict in nbrs.items(): 

1531 for key in kdict: 

1532 yield (nbr, n, key) 

1533 

1534 def __contains__(self, e): 

1535 N = len(e) 

1536 if N == 3: 

1537 u, v, k = e 

1538 elif N == 2: 

1539 u, v = e 

1540 k = 0 

1541 else: 

1542 raise ValueError("MultiEdge must have length 2 or 3") 

1543 try: 

1544 return k in self._adjdict[v][u] 

1545 except KeyError: 

1546 return False 

1547 

1548 def __getitem__(self, e): 

1549 if isinstance(e, slice): 

1550 raise nx.NetworkXError( 

1551 f"{type(self).__name__} does not support slicing, " 

1552 f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]" 

1553 ) 

1554 u, v, k = e 

1555 return self._adjdict[v][u][k]