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

613 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-20 07:00 +0000

1""" 

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

3 

4Views for nodes, edges and degree are provided for all base graph classes. 

5A view means a read-only object that is quick to create, automatically 

6updated when the graph changes, and provides basic access like `n in V`, 

7`for n in V`, `V[n]` and sometimes set operations. 

8 

9The views are read-only iterable containers that are updated as the 

10graph is updated. As with dicts, the graph should not be updated 

11while iterating through the view. Views can be iterated multiple times. 

12 

13Edge and Node views also allow data attribute lookup. 

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

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

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

17 

18NodeView 

19======== 

20 

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

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

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

24 

25NodeDataView 

26============ 

27 

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

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

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

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

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

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

34 data attributes are hashable. 

35 

36DegreeView 

37========== 

38 

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

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

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

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

43 `G.in_degree` count only specific directions. 

44 Weighted degree using edge data attributes is provide via 

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

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

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

48 

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

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

51 

52EdgeView 

53======== 

54 

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

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

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

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

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

60 

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

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

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

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

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

66 operations may contain both representations of the same edge. 

67 

68EdgeDataView 

69============ 

70 

71 Edge data can be reported using an EdgeDataView typically created 

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

73 The EdgeDataView allows iteration over edge tuples, membership checking 

74 but no set operations. 

75 

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

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

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

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

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

81 to create 3-tuples and 4-tuples. 

82 

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

84""" 

85from collections.abc import Mapping, Set 

86 

87import networkx as nx 

88 

89__all__ = [ 

90 "NodeView", 

91 "NodeDataView", 

92 "EdgeView", 

93 "OutEdgeView", 

94 "InEdgeView", 

95 "EdgeDataView", 

96 "OutEdgeDataView", 

97 "InEdgeDataView", 

98 "MultiEdgeView", 

99 "OutMultiEdgeView", 

100 "InMultiEdgeView", 

101 "MultiEdgeDataView", 

102 "OutMultiEdgeDataView", 

103 "InMultiEdgeDataView", 

104 "DegreeView", 

105 "DiDegreeView", 

106 "InDegreeView", 

107 "OutDegreeView", 

108 "MultiDegreeView", 

109 "DiMultiDegreeView", 

110 "InMultiDegreeView", 

111 "OutMultiDegreeView", 

112] 

113 

114 

115# NodeViews 

116class NodeView(Mapping, Set): 

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

118 

119 Set operations act on the nodes without considering data. 

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

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

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

123 

124 Parameters 

125 ---------- 

126 graph : NetworkX graph-like class 

127 

128 Examples 

129 -------- 

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

131 >>> NV = G.nodes() 

132 >>> 2 in NV 

133 True 

134 >>> for n in NV: 

135 ... print(n) 

136 0 

137 1 

138 2 

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

140 

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

142 >>> NV[2] 

143 {'color': 'blue'} 

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

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

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

147 True 

148 >>> for n, dd in NDV: 

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

150 (0, 'aqua') 

151 (1, 'aqua') 

152 (2, 'blue') 

153 (8, 'red') 

154 >>> NDV[2] == NV[2] 

155 True 

156 

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

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

159 True 

160 >>> for n, dd in NVdata: 

161 ... print((n, dd)) 

162 (0, 'aqua') 

163 (1, 'aqua') 

164 (2, 'blue') 

165 (8, 'red') 

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

167 False 

168 """ 

169 

170 __slots__ = ("_nodes",) 

171 

172 def __getstate__(self): 

173 return {"_nodes": self._nodes} 

174 

175 def __setstate__(self, state): 

176 self._nodes = state["_nodes"] 

177 

178 def __init__(self, graph): 

179 self._nodes = graph._node 

180 

181 # Mapping methods 

182 def __len__(self): 

183 return len(self._nodes) 

184 

185 def __iter__(self): 

186 return iter(self._nodes) 

187 

188 def __getitem__(self, n): 

189 if isinstance(n, slice): 

190 raise nx.NetworkXError( 

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

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

193 ) 

194 return self._nodes[n] 

195 

196 # Set methods 

197 def __contains__(self, n): 

198 return n in self._nodes 

199 

200 @classmethod 

201 def _from_iterable(cls, it): 

202 return set(it) 

203 

204 # DataView method 

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

206 if data is False: 

207 return self 

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

209 

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

211 """ 

212 Return a read-only view of node data. 

213 

214 Parameters 

215 ---------- 

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

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

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

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

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

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

222 default : object, default=None 

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

224 

225 Returns 

226 ------- 

227 NodeDataView 

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

229 `data` parameter. 

230 

231 Notes 

232 ----- 

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

234 

235 See Also 

236 -------- 

237 NodeDataView 

238 

239 Examples 

240 -------- 

241 >>> G = nx.Graph() 

242 >>> G.add_nodes_from([ 

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

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

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

246 ... ]) 

247 

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

249 NodeDataView mapping each node to all of its attributes: 

250 

251 >>> G.nodes.data() 

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

253 

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

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

256 

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

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

259 

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

261 by `default` is returned: 

262 

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

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

265 

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

267 node attribute dictionaries: 

268 

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

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

271 """ 

272 if data is False: 

273 return self 

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

275 

276 def __str__(self): 

277 return str(list(self)) 

278 

279 def __repr__(self): 

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

281 

282 

283class NodeDataView(Set): 

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

285 

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

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

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

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

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

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

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

293 

294 Parameters 

295 ========== 

296 graph : NetworkX graph-like class 

297 data : bool or string (default=False) 

298 default : object (default=None) 

299 """ 

300 

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

302 

303 def __getstate__(self): 

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

305 

306 def __setstate__(self, state): 

307 self._nodes = state["_nodes"] 

308 self._data = state["_data"] 

309 self._default = state["_default"] 

310 

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

312 self._nodes = nodedict 

313 self._data = data 

314 self._default = default 

315 

316 @classmethod 

317 def _from_iterable(cls, it): 

318 try: 

319 return set(it) 

320 except TypeError as err: 

321 if "unhashable" in str(err): 

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

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

324 raise 

325 

326 def __len__(self): 

327 return len(self._nodes) 

328 

329 def __iter__(self): 

330 data = self._data 

331 if data is False: 

332 return iter(self._nodes) 

333 if data is True: 

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

335 return ( 

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

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

338 ) 

339 

340 def __contains__(self, n): 

341 try: 

342 node_in = n in self._nodes 

343 except TypeError: 

344 n, d = n 

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

346 if node_in is True: 

347 return node_in 

348 try: 

349 n, d = n 

350 except (TypeError, ValueError): 

351 return False 

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

353 

354 def __getitem__(self, n): 

355 if isinstance(n, slice): 

356 raise nx.NetworkXError( 

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

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

359 ) 

360 ddict = self._nodes[n] 

361 data = self._data 

362 if data is False or data is True: 

363 return ddict 

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

365 

366 def __str__(self): 

367 return str(list(self)) 

368 

369 def __repr__(self): 

370 name = self.__class__.__name__ 

371 if self._data is False: 

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

373 if self._data is True: 

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

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

376 

377 

378# DegreeViews 

379class DiDegreeView: 

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

381 

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

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

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

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

386 

387 Parameters 

388 ========== 

389 graph : NetworkX graph-like class 

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

391 weight : bool or string (default=None) 

392 

393 Notes 

394 ----- 

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

396 

397 Examples 

398 -------- 

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

400 >>> DV = G.degree() 

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

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

403 

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

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

406 >>> DVweight[2] 

407 34 

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

409 1 

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

411 70 

412 

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

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

415 """ 

416 

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

418 self._graph = G 

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

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

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

422 self._weight = weight 

423 

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

425 if nbunch is None: 

426 if weight == self._weight: 

427 return self 

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

429 try: 

430 if nbunch in self._nodes: 

431 if weight == self._weight: 

432 return self[nbunch] 

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

434 except TypeError: 

435 pass 

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

437 

438 def __getitem__(self, n): 

439 weight = self._weight 

440 succs = self._succ[n] 

441 preds = self._pred[n] 

442 if weight is None: 

443 return len(succs) + len(preds) 

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

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

446 ) 

447 

448 def __iter__(self): 

449 weight = self._weight 

450 if weight is None: 

451 for n in self._nodes: 

452 succs = self._succ[n] 

453 preds = self._pred[n] 

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

455 else: 

456 for n in self._nodes: 

457 succs = self._succ[n] 

458 preds = self._pred[n] 

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

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

461 ) 

462 yield (n, deg) 

463 

464 def __len__(self): 

465 return len(self._nodes) 

466 

467 def __str__(self): 

468 return str(list(self)) 

469 

470 def __repr__(self): 

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

472 

473 

474class DegreeView(DiDegreeView): 

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

476 

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

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

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

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

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

482 

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

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

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

486 

487 Parameters 

488 ========== 

489 graph : NetworkX graph-like class 

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

491 weight : string or None (default=None) 

492 

493 Notes 

494 ----- 

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

496 

497 Examples 

498 -------- 

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

500 >>> DV = G.degree() 

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

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

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

504 

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

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

507 >>> DVweight[2] 

508 34 

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

510 1 

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

512 70 

513 

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

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

516 """ 

517 

518 def __getitem__(self, n): 

519 weight = self._weight 

520 nbrs = self._succ[n] 

521 if weight is None: 

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

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

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

525 ) 

526 

527 def __iter__(self): 

528 weight = self._weight 

529 if weight is None: 

530 for n in self._nodes: 

531 nbrs = self._succ[n] 

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

533 else: 

534 for n in self._nodes: 

535 nbrs = self._succ[n] 

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

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

538 ) 

539 yield (n, deg) 

540 

541 

542class OutDegreeView(DiDegreeView): 

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

544 

545 def __getitem__(self, n): 

546 weight = self._weight 

547 nbrs = self._succ[n] 

548 if self._weight is None: 

549 return len(nbrs) 

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

551 

552 def __iter__(self): 

553 weight = self._weight 

554 if weight is None: 

555 for n in self._nodes: 

556 succs = self._succ[n] 

557 yield (n, len(succs)) 

558 else: 

559 for n in self._nodes: 

560 succs = self._succ[n] 

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

562 yield (n, deg) 

563 

564 

565class InDegreeView(DiDegreeView): 

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

567 

568 def __getitem__(self, n): 

569 weight = self._weight 

570 nbrs = self._pred[n] 

571 if weight is None: 

572 return len(nbrs) 

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

574 

575 def __iter__(self): 

576 weight = self._weight 

577 if weight is None: 

578 for n in self._nodes: 

579 preds = self._pred[n] 

580 yield (n, len(preds)) 

581 else: 

582 for n in self._nodes: 

583 preds = self._pred[n] 

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

585 yield (n, deg) 

586 

587 

588class MultiDegreeView(DiDegreeView): 

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

590 

591 def __getitem__(self, n): 

592 weight = self._weight 

593 nbrs = self._succ[n] 

594 if weight is None: 

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

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

597 ) 

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

599 deg = sum( 

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

601 ) 

602 if n in nbrs: 

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

604 return deg 

605 

606 def __iter__(self): 

607 weight = self._weight 

608 if weight is None: 

609 for n in self._nodes: 

610 nbrs = self._succ[n] 

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

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

613 ) 

614 yield (n, deg) 

615 else: 

616 for n in self._nodes: 

617 nbrs = self._succ[n] 

618 deg = sum( 

619 d.get(weight, 1) 

620 for key_dict in nbrs.values() 

621 for d in key_dict.values() 

622 ) 

623 if n in nbrs: 

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

625 yield (n, deg) 

626 

627 

628class DiMultiDegreeView(DiDegreeView): 

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

630 

631 def __getitem__(self, n): 

632 weight = self._weight 

633 succs = self._succ[n] 

634 preds = self._pred[n] 

635 if weight is None: 

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

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

638 ) 

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

640 deg = sum( 

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

642 ) + sum( 

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

644 ) 

645 return deg 

646 

647 def __iter__(self): 

648 weight = self._weight 

649 if weight is None: 

650 for n in self._nodes: 

651 succs = self._succ[n] 

652 preds = self._pred[n] 

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

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

655 ) 

656 yield (n, deg) 

657 else: 

658 for n in self._nodes: 

659 succs = self._succ[n] 

660 preds = self._pred[n] 

661 deg = sum( 

662 d.get(weight, 1) 

663 for key_dict in succs.values() 

664 for d in key_dict.values() 

665 ) + sum( 

666 d.get(weight, 1) 

667 for key_dict in preds.values() 

668 for d in key_dict.values() 

669 ) 

670 yield (n, deg) 

671 

672 

673class InMultiDegreeView(DiDegreeView): 

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

675 

676 def __getitem__(self, n): 

677 weight = self._weight 

678 nbrs = self._pred[n] 

679 if weight is None: 

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

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

682 return sum( 

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

684 ) 

685 

686 def __iter__(self): 

687 weight = self._weight 

688 if weight is None: 

689 for n in self._nodes: 

690 nbrs = self._pred[n] 

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

692 yield (n, deg) 

693 else: 

694 for n in self._nodes: 

695 nbrs = self._pred[n] 

696 deg = sum( 

697 d.get(weight, 1) 

698 for key_dict in nbrs.values() 

699 for d in key_dict.values() 

700 ) 

701 yield (n, deg) 

702 

703 

704class OutMultiDegreeView(DiDegreeView): 

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

706 

707 def __getitem__(self, n): 

708 weight = self._weight 

709 nbrs = self._succ[n] 

710 if weight is None: 

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

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

713 return sum( 

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

715 ) 

716 

717 def __iter__(self): 

718 weight = self._weight 

719 if weight is None: 

720 for n in self._nodes: 

721 nbrs = self._succ[n] 

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

723 yield (n, deg) 

724 else: 

725 for n in self._nodes: 

726 nbrs = self._succ[n] 

727 deg = sum( 

728 d.get(weight, 1) 

729 for key_dict in nbrs.values() 

730 for d in key_dict.values() 

731 ) 

732 yield (n, deg) 

733 

734 

735# EdgeDataViews 

736class OutEdgeDataView: 

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

738 

739 __slots__ = ( 

740 "_viewer", 

741 "_nbunch", 

742 "_data", 

743 "_default", 

744 "_adjdict", 

745 "_nodes_nbrs", 

746 "_report", 

747 ) 

748 

749 def __getstate__(self): 

750 return { 

751 "viewer": self._viewer, 

752 "nbunch": self._nbunch, 

753 "data": self._data, 

754 "default": self._default, 

755 } 

756 

757 def __setstate__(self, state): 

758 self.__init__(**state) 

759 

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

761 self._viewer = viewer 

762 adjdict = self._adjdict = viewer._adjdict 

763 if nbunch is None: 

764 self._nodes_nbrs = adjdict.items 

765 else: 

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

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

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

769 self._nbunch = nbunch 

770 self._data = data 

771 self._default = default 

772 # Set _report based on data and default 

773 if data is True: 

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

775 elif data is False: 

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

777 else: # data is attribute name 

778 self._report = ( 

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

780 if data in dd 

781 else (n, nbr, default) 

782 ) 

783 

784 def __len__(self): 

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

786 

787 def __iter__(self): 

788 return ( 

789 self._report(n, nbr, dd) 

790 for n, nbrs in self._nodes_nbrs() 

791 for nbr, dd in nbrs.items() 

792 ) 

793 

794 def __contains__(self, e): 

795 u, v = e[:2] 

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

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

798 try: 

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

800 except KeyError: 

801 return False 

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

803 

804 def __str__(self): 

805 return str(list(self)) 

806 

807 def __repr__(self): 

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

809 

810 

811class EdgeDataView(OutEdgeDataView): 

812 """A EdgeDataView class for edges of Graph 

813 

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

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

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

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

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

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

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

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

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

823 edge attribute is not present. 

824 

825 Parameters 

826 ---------- 

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

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

829 default : default value (default None) 

830 

831 Examples 

832 -------- 

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

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

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

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

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

838 """ 

839 

840 __slots__ = () 

841 

842 def __len__(self): 

843 return sum(1 for e in self) 

844 

845 def __iter__(self): 

846 seen = {} 

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

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

849 if nbr not in seen: 

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

851 seen[n] = 1 

852 del seen 

853 

854 def __contains__(self, e): 

855 u, v = e[:2] 

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

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

858 try: 

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

860 except KeyError: 

861 return False 

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

863 

864 

865class InEdgeDataView(OutEdgeDataView): 

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

867 

868 __slots__ = () 

869 

870 def __iter__(self): 

871 return ( 

872 self._report(nbr, n, dd) 

873 for n, nbrs in self._nodes_nbrs() 

874 for nbr, dd in nbrs.items() 

875 ) 

876 

877 def __contains__(self, e): 

878 u, v = e[:2] 

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

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

881 try: 

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

883 except KeyError: 

884 return False 

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

886 

887 

888class OutMultiEdgeDataView(OutEdgeDataView): 

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

890 

891 __slots__ = ("keys",) 

892 

893 def __getstate__(self): 

894 return { 

895 "viewer": self._viewer, 

896 "nbunch": self._nbunch, 

897 "keys": self.keys, 

898 "data": self._data, 

899 "default": self._default, 

900 } 

901 

902 def __setstate__(self, state): 

903 self.__init__(**state) 

904 

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

906 self._viewer = viewer 

907 adjdict = self._adjdict = viewer._adjdict 

908 self.keys = keys 

909 if nbunch is None: 

910 self._nodes_nbrs = adjdict.items 

911 else: 

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

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

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

915 self._nbunch = nbunch 

916 self._data = data 

917 self._default = default 

918 # Set _report based on data and default 

919 if data is True: 

920 if keys is True: 

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

922 else: 

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

924 elif data is False: 

925 if keys is True: 

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

927 else: 

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

929 else: # data is attribute name 

930 if keys is True: 

931 self._report = ( 

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

933 if data in dd 

934 else (n, nbr, k, default) 

935 ) 

936 else: 

937 self._report = ( 

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

939 if data in dd 

940 else (n, nbr, default) 

941 ) 

942 

943 def __len__(self): 

944 return sum(1 for e in self) 

945 

946 def __iter__(self): 

947 return ( 

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

949 for n, nbrs in self._nodes_nbrs() 

950 for nbr, kd in nbrs.items() 

951 for k, dd in kd.items() 

952 ) 

953 

954 def __contains__(self, e): 

955 u, v = e[:2] 

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

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

958 try: 

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

960 except KeyError: 

961 return False 

962 if self.keys is True: 

963 k = e[2] 

964 try: 

965 dd = kdict[k] 

966 except KeyError: 

967 return False 

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

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

970 

971 

972class MultiEdgeDataView(OutMultiEdgeDataView): 

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

974 

975 __slots__ = () 

976 

977 def __iter__(self): 

978 seen = {} 

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

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

981 if nbr not in seen: 

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

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

984 seen[n] = 1 

985 del seen 

986 

987 def __contains__(self, e): 

988 u, v = e[:2] 

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

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

991 try: 

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

993 except KeyError: 

994 try: 

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

996 except KeyError: 

997 return False 

998 if self.keys is True: 

999 k = e[2] 

1000 try: 

1001 dd = kdict[k] 

1002 except KeyError: 

1003 return False 

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

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

1006 

1007 

1008class InMultiEdgeDataView(OutMultiEdgeDataView): 

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

1010 

1011 __slots__ = () 

1012 

1013 def __iter__(self): 

1014 return ( 

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

1016 for n, nbrs in self._nodes_nbrs() 

1017 for nbr, kd in nbrs.items() 

1018 for k, dd in kd.items() 

1019 ) 

1020 

1021 def __contains__(self, e): 

1022 u, v = e[:2] 

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

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

1025 try: 

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

1027 except KeyError: 

1028 return False 

1029 if self.keys is True: 

1030 k = e[2] 

1031 dd = kdict[k] 

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

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

1034 

1035 

1036# EdgeViews have set operations and no data reported 

1037class OutEdgeView(Set, Mapping): 

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

1039 

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

1041 

1042 def __getstate__(self): 

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

1044 

1045 def __setstate__(self, state): 

1046 self._graph = state["_graph"] 

1047 self._adjdict = state["_adjdict"] 

1048 self._nodes_nbrs = self._adjdict.items 

1049 

1050 @classmethod 

1051 def _from_iterable(cls, it): 

1052 return set(it) 

1053 

1054 dataview = OutEdgeDataView 

1055 

1056 def __init__(self, G): 

1057 self._graph = G 

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

1059 self._nodes_nbrs = self._adjdict.items 

1060 

1061 # Set methods 

1062 def __len__(self): 

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

1064 

1065 def __iter__(self): 

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

1067 for nbr in nbrs: 

1068 yield (n, nbr) 

1069 

1070 def __contains__(self, e): 

1071 try: 

1072 u, v = e 

1073 return v in self._adjdict[u] 

1074 except KeyError: 

1075 return False 

1076 

1077 # Mapping Methods 

1078 def __getitem__(self, e): 

1079 if isinstance(e, slice): 

1080 raise nx.NetworkXError( 

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

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

1083 ) 

1084 u, v = e 

1085 return self._adjdict[u][v] 

1086 

1087 # EdgeDataView methods 

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

1089 if nbunch is None and data is False: 

1090 return self 

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

1092 

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

1094 """ 

1095 Return a read-only view of edge data. 

1096 

1097 Parameters 

1098 ---------- 

1099 data : bool or edge attribute key 

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

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

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

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

1104 attribute, the `default` value is returned. 

1105 default : object, default=None 

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

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

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

1109 are considered by default. 

1110 

1111 Returns 

1112 ------- 

1113 dataview 

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

1115 for DiGraphs, `MultiEdgeDataView` for MultiGraphs and 

1116 `OutMultiEdgeDataView` for MultiDiGraphs. 

1117 

1118 Notes 

1119 ----- 

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

1121 

1122 See Also 

1123 -------- 

1124 EdgeDataView 

1125 OutEdgeDataView 

1126 MultiEdgeDataView 

1127 OutMultiEdgeDataView 

1128 

1129 Examples 

1130 -------- 

1131 >>> G = nx.Graph() 

1132 >>> G.add_edges_from([ 

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

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

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

1136 ... ]) 

1137 

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

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

1140 

1141 >>> G.edges.data() 

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

1143 

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

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

1146 

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

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

1149 

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

1151 

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

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

1154 

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

1156 specified by `default` is used: 

1157 

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

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

1160 

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

1162 the edge attribute dictionaries: 

1163 

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

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

1166 """ 

1167 if nbunch is None and data is False: 

1168 return self 

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

1170 

1171 # String Methods 

1172 def __str__(self): 

1173 return str(list(self)) 

1174 

1175 def __repr__(self): 

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

1177 

1178 

1179class EdgeView(OutEdgeView): 

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

1181 

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

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

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

1185 possibly creating an EdgeDataView. Typically edges are iterated over 

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

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

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

1189 where those tuples are the elements of the set. 

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

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

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

1193 

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

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

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

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

1198 

1199 Parameters 

1200 ========== 

1201 graph : NetworkX graph-like class 

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

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

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

1205 default : object (default=None) 

1206 

1207 Examples 

1208 ======== 

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

1210 >>> EV = G.edges() 

1211 >>> (2, 3) in EV 

1212 True 

1213 >>> for u, v in EV: 

1214 ... print((u, v)) 

1215 (0, 1) 

1216 (1, 2) 

1217 (2, 3) 

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

1219 

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

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

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

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

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

1225 (0, 1) has color: aqua 

1226 (1, 2) has color: aqua 

1227 (2, 3) has color: blue 

1228 

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

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

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

1232 >>> for u, v in EVnbunch: 

1233 ... assert u == 2 or v == 2 

1234 

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

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

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

1238 True 

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

1240 True 

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

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

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

1244 (0, 1, 0) 

1245 (1, 2, 0) 

1246 (2, 3, 0) 

1247 (2, 3, 1) 

1248 """ 

1249 

1250 __slots__ = () 

1251 

1252 dataview = EdgeDataView 

1253 

1254 def __len__(self): 

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

1256 return sum(num_nbrs) // 2 

1257 

1258 def __iter__(self): 

1259 seen = {} 

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

1261 for nbr in list(nbrs): 

1262 if nbr not in seen: 

1263 yield (n, nbr) 

1264 seen[n] = 1 

1265 del seen 

1266 

1267 def __contains__(self, e): 

1268 try: 

1269 u, v = e[:2] 

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

1271 except (KeyError, ValueError): 

1272 return False 

1273 

1274 

1275class InEdgeView(OutEdgeView): 

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

1277 

1278 __slots__ = () 

1279 

1280 def __setstate__(self, state): 

1281 self._graph = state["_graph"] 

1282 self._adjdict = state["_adjdict"] 

1283 self._nodes_nbrs = self._adjdict.items 

1284 

1285 dataview = InEdgeDataView 

1286 

1287 def __init__(self, G): 

1288 self._graph = G 

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

1290 self._nodes_nbrs = self._adjdict.items 

1291 

1292 def __iter__(self): 

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

1294 for nbr in nbrs: 

1295 yield (nbr, n) 

1296 

1297 def __contains__(self, e): 

1298 try: 

1299 u, v = e 

1300 return u in self._adjdict[v] 

1301 except KeyError: 

1302 return False 

1303 

1304 def __getitem__(self, e): 

1305 if isinstance(e, slice): 

1306 raise nx.NetworkXError( 

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

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

1309 ) 

1310 u, v = e 

1311 return self._adjdict[v][u] 

1312 

1313 

1314class OutMultiEdgeView(OutEdgeView): 

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

1316 

1317 __slots__ = () 

1318 

1319 dataview = OutMultiEdgeDataView 

1320 

1321 def __len__(self): 

1322 return sum( 

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

1324 ) 

1325 

1326 def __iter__(self): 

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

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

1329 for key in kdict: 

1330 yield (n, nbr, key) 

1331 

1332 def __contains__(self, e): 

1333 N = len(e) 

1334 if N == 3: 

1335 u, v, k = e 

1336 elif N == 2: 

1337 u, v = e 

1338 k = 0 

1339 else: 

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

1341 try: 

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

1343 except KeyError: 

1344 return False 

1345 

1346 def __getitem__(self, e): 

1347 if isinstance(e, slice): 

1348 raise nx.NetworkXError( 

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

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

1351 ) 

1352 u, v, k = e 

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

1354 

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

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

1357 return self 

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

1359 

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

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

1362 return self 

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

1364 

1365 

1366class MultiEdgeView(OutMultiEdgeView): 

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

1368 

1369 __slots__ = () 

1370 

1371 dataview = MultiEdgeDataView 

1372 

1373 def __len__(self): 

1374 return sum(1 for e in self) 

1375 

1376 def __iter__(self): 

1377 seen = {} 

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

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

1380 if nbr not in seen: 

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

1382 yield (n, nbr, k) 

1383 seen[n] = 1 

1384 del seen 

1385 

1386 

1387class InMultiEdgeView(OutMultiEdgeView): 

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

1389 

1390 __slots__ = () 

1391 

1392 def __setstate__(self, state): 

1393 self._graph = state["_graph"] 

1394 self._adjdict = state["_adjdict"] 

1395 self._nodes_nbrs = self._adjdict.items 

1396 

1397 dataview = InMultiEdgeDataView 

1398 

1399 def __init__(self, G): 

1400 self._graph = G 

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

1402 self._nodes_nbrs = self._adjdict.items 

1403 

1404 def __iter__(self): 

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

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

1407 for key in kdict: 

1408 yield (nbr, n, key) 

1409 

1410 def __contains__(self, e): 

1411 N = len(e) 

1412 if N == 3: 

1413 u, v, k = e 

1414 elif N == 2: 

1415 u, v = e 

1416 k = 0 

1417 else: 

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

1419 try: 

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

1421 except KeyError: 

1422 return False 

1423 

1424 def __getitem__(self, e): 

1425 if isinstance(e, slice): 

1426 raise nx.NetworkXError( 

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

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

1429 ) 

1430 u, v, k = e 

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