123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- """Views of core data structures such as nested Mappings (e.g. dict-of-dicts).
- These ``Views`` often restrict element access, with either the entire view or
- layers of nested mappings being read-only.
- """
- from collections.abc import Mapping
- __all__ = [
- "AtlasView",
- "AdjacencyView",
- "MultiAdjacencyView",
- "UnionAtlas",
- "UnionAdjacency",
- "UnionMultiInner",
- "UnionMultiAdjacency",
- "FilterAtlas",
- "FilterAdjacency",
- "FilterMultiInner",
- "FilterMultiAdjacency",
- ]
- class AtlasView(Mapping):
- """An AtlasView is a Read-only Mapping of Mappings.
- It is a View into a dict-of-dict data structure.
- The inner level of dict is read-write. But the
- outer level is read-only.
- See Also
- ========
- AdjacencyView: View into dict-of-dict-of-dict
- MultiAdjacencyView: View into dict-of-dict-of-dict-of-dict
- """
- __slots__ = ("_atlas",)
- def __getstate__(self):
- return {"_atlas": self._atlas}
- def __setstate__(self, state):
- self._atlas = state["_atlas"]
- def __init__(self, d):
- self._atlas = d
- def __len__(self):
- return len(self._atlas)
- def __iter__(self):
- return iter(self._atlas)
- def __getitem__(self, key):
- return self._atlas[key]
- def copy(self):
- return {n: self[n].copy() for n in self._atlas}
- def __str__(self):
- return str(self._atlas) # {nbr: self[nbr] for nbr in self})
- def __repr__(self):
- return f"{self.__class__.__name__}({self._atlas!r})"
- class AdjacencyView(AtlasView):
- """An AdjacencyView is a Read-only Map of Maps of Maps.
- It is a View into a dict-of-dict-of-dict data structure.
- The inner level of dict is read-write. But the
- outer levels are read-only.
- See Also
- ========
- AtlasView: View into dict-of-dict
- MultiAdjacencyView: View into dict-of-dict-of-dict-of-dict
- """
- __slots__ = () # Still uses AtlasView slots names _atlas
- def __getitem__(self, name):
- return AtlasView(self._atlas[name])
- def copy(self):
- return {n: self[n].copy() for n in self._atlas}
- class MultiAdjacencyView(AdjacencyView):
- """An MultiAdjacencyView is a Read-only Map of Maps of Maps of Maps.
- It is a View into a dict-of-dict-of-dict-of-dict data structure.
- The inner level of dict is read-write. But the
- outer levels are read-only.
- See Also
- ========
- AtlasView: View into dict-of-dict
- AdjacencyView: View into dict-of-dict-of-dict
- """
- __slots__ = () # Still uses AtlasView slots names _atlas
- def __getitem__(self, name):
- return AdjacencyView(self._atlas[name])
- def copy(self):
- return {n: self[n].copy() for n in self._atlas}
- class UnionAtlas(Mapping):
- """A read-only union of two atlases (dict-of-dict).
- The two dict-of-dicts represent the inner dict of
- an Adjacency: `G.succ[node]` and `G.pred[node]`.
- The inner level of dict of both hold attribute key:value
- pairs and is read-write. But the outer level is read-only.
- See Also
- ========
- UnionAdjacency: View into dict-of-dict-of-dict
- UnionMultiAdjacency: View into dict-of-dict-of-dict-of-dict
- """
- __slots__ = ("_succ", "_pred")
- def __getstate__(self):
- return {"_succ": self._succ, "_pred": self._pred}
- def __setstate__(self, state):
- self._succ = state["_succ"]
- self._pred = state["_pred"]
- def __init__(self, succ, pred):
- self._succ = succ
- self._pred = pred
- def __len__(self):
- return len(self._succ.keys() | self._pred.keys())
- def __iter__(self):
- return iter(set(self._succ.keys()) | set(self._pred.keys()))
- def __getitem__(self, key):
- try:
- return self._succ[key]
- except KeyError:
- return self._pred[key]
- def copy(self):
- result = {nbr: dd.copy() for nbr, dd in self._succ.items()}
- for nbr, dd in self._pred.items():
- if nbr in result:
- result[nbr].update(dd)
- else:
- result[nbr] = dd.copy()
- return result
- def __str__(self):
- return str({nbr: self[nbr] for nbr in self})
- def __repr__(self):
- return f"{self.__class__.__name__}({self._succ!r}, {self._pred!r})"
- class UnionAdjacency(Mapping):
- """A read-only union of dict Adjacencies as a Map of Maps of Maps.
- The two input dict-of-dict-of-dicts represent the union of
- `G.succ` and `G.pred`. Return values are UnionAtlas
- The inner level of dict is read-write. But the
- middle and outer levels are read-only.
- succ : a dict-of-dict-of-dict {node: nbrdict}
- pred : a dict-of-dict-of-dict {node: nbrdict}
- The keys for the two dicts should be the same
- See Also
- ========
- UnionAtlas: View into dict-of-dict
- UnionMultiAdjacency: View into dict-of-dict-of-dict-of-dict
- """
- __slots__ = ("_succ", "_pred")
- def __getstate__(self):
- return {"_succ": self._succ, "_pred": self._pred}
- def __setstate__(self, state):
- self._succ = state["_succ"]
- self._pred = state["_pred"]
- def __init__(self, succ, pred):
- # keys must be the same for two input dicts
- assert len(set(succ.keys()) ^ set(pred.keys())) == 0
- self._succ = succ
- self._pred = pred
- def __len__(self):
- return len(self._succ) # length of each dict should be the same
- def __iter__(self):
- return iter(self._succ)
- def __getitem__(self, nbr):
- return UnionAtlas(self._succ[nbr], self._pred[nbr])
- def copy(self):
- return {n: self[n].copy() for n in self._succ}
- def __str__(self):
- return str({nbr: self[nbr] for nbr in self})
- def __repr__(self):
- return f"{self.__class__.__name__}({self._succ!r}, {self._pred!r})"
- class UnionMultiInner(UnionAtlas):
- """A read-only union of two inner dicts of MultiAdjacencies.
- The two input dict-of-dict-of-dicts represent the union of
- `G.succ[node]` and `G.pred[node]` for MultiDiGraphs.
- Return values are UnionAtlas.
- The inner level of dict is read-write. But the outer levels are read-only.
- See Also
- ========
- UnionAtlas: View into dict-of-dict
- UnionAdjacency: View into dict-of-dict-of-dict
- UnionMultiAdjacency: View into dict-of-dict-of-dict-of-dict
- """
- __slots__ = () # Still uses UnionAtlas slots names _succ, _pred
- def __getitem__(self, node):
- in_succ = node in self._succ
- in_pred = node in self._pred
- if in_succ:
- if in_pred:
- return UnionAtlas(self._succ[node], self._pred[node])
- return UnionAtlas(self._succ[node], {})
- return UnionAtlas({}, self._pred[node])
- def copy(self):
- nodes = set(self._succ.keys()) | set(self._pred.keys())
- return {n: self[n].copy() for n in nodes}
- class UnionMultiAdjacency(UnionAdjacency):
- """A read-only union of two dict MultiAdjacencies.
- The two input dict-of-dict-of-dict-of-dicts represent the union of
- `G.succ` and `G.pred` for MultiDiGraphs. Return values are UnionAdjacency.
- The inner level of dict is read-write. But the outer levels are read-only.
- See Also
- ========
- UnionAtlas: View into dict-of-dict
- UnionMultiInner: View into dict-of-dict-of-dict
- """
- __slots__ = () # Still uses UnionAdjacency slots names _succ, _pred
- def __getitem__(self, node):
- return UnionMultiInner(self._succ[node], self._pred[node])
- class FilterAtlas(Mapping): # nodedict, nbrdict, keydict
- def __init__(self, d, NODE_OK):
- self._atlas = d
- self.NODE_OK = NODE_OK
- def __len__(self):
- return sum(1 for n in self)
- def __iter__(self):
- try: # check that NODE_OK has attr 'nodes'
- node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
- except AttributeError:
- node_ok_shorter = False
- if node_ok_shorter:
- return (n for n in self.NODE_OK.nodes if n in self._atlas)
- return (n for n in self._atlas if self.NODE_OK(n))
- def __getitem__(self, key):
- if key in self._atlas and self.NODE_OK(key):
- return self._atlas[key]
- raise KeyError(f"Key {key} not found")
- def __str__(self):
- return str({nbr: self[nbr] for nbr in self})
- def __repr__(self):
- return f"{self.__class__.__name__}({self._atlas!r}, {self.NODE_OK!r})"
- class FilterAdjacency(Mapping): # edgedict
- def __init__(self, d, NODE_OK, EDGE_OK):
- self._atlas = d
- self.NODE_OK = NODE_OK
- self.EDGE_OK = EDGE_OK
- def __len__(self):
- return sum(1 for n in self)
- def __iter__(self):
- try: # check that NODE_OK has attr 'nodes'
- node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
- except AttributeError:
- node_ok_shorter = False
- if node_ok_shorter:
- return (n for n in self.NODE_OK.nodes if n in self._atlas)
- return (n for n in self._atlas if self.NODE_OK(n))
- def __getitem__(self, node):
- if node in self._atlas and self.NODE_OK(node):
- def new_node_ok(nbr):
- return self.NODE_OK(nbr) and self.EDGE_OK(node, nbr)
- return FilterAtlas(self._atlas[node], new_node_ok)
- raise KeyError(f"Key {node} not found")
- def __str__(self):
- return str({nbr: self[nbr] for nbr in self})
- def __repr__(self):
- name = self.__class__.__name__
- return f"{name}({self._atlas!r}, {self.NODE_OK!r}, {self.EDGE_OK!r})"
- class FilterMultiInner(FilterAdjacency): # muliedge_seconddict
- def __iter__(self):
- try: # check that NODE_OK has attr 'nodes'
- node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas)
- except AttributeError:
- node_ok_shorter = False
- if node_ok_shorter:
- my_nodes = (n for n in self.NODE_OK.nodes if n in self._atlas)
- else:
- my_nodes = (n for n in self._atlas if self.NODE_OK(n))
- for n in my_nodes:
- some_keys_ok = False
- for key in self._atlas[n]:
- if self.EDGE_OK(n, key):
- some_keys_ok = True
- break
- if some_keys_ok is True:
- yield n
- def __getitem__(self, nbr):
- if nbr in self._atlas and self.NODE_OK(nbr):
- def new_node_ok(key):
- return self.EDGE_OK(nbr, key)
- return FilterAtlas(self._atlas[nbr], new_node_ok)
- raise KeyError(f"Key {nbr} not found")
- class FilterMultiAdjacency(FilterAdjacency): # multiedgedict
- def __getitem__(self, node):
- if node in self._atlas and self.NODE_OK(node):
- def edge_ok(nbr, key):
- return self.NODE_OK(nbr) and self.EDGE_OK(node, nbr, key)
- return FilterMultiInner(self._atlas[node], self.NODE_OK, edge_ok)
- raise KeyError(f"Key {node} not found")
|