|
|
from collections import OrderedDict |
|
|
|
|
|
__all__ = ["raises", "expand_tuples", "reverse_dict", "groupby", "typename"] |
|
|
|
|
|
def raises(err, lamda): |
|
|
try: |
|
|
lamda() |
|
|
return False |
|
|
except err: |
|
|
return True |
|
|
|
|
|
|
|
|
def expand_tuples(L): |
|
|
""" |
|
|
>>> expand_tuples([1, (2, 3)]) |
|
|
[(1, 2), (1, 3)] |
|
|
>>> expand_tuples([1, 2]) |
|
|
[(1, 2)] |
|
|
""" |
|
|
if not L: |
|
|
return [()] |
|
|
elif not isinstance(L[0], tuple): |
|
|
rest = expand_tuples(L[1:]) |
|
|
return [(L[0],) + t for t in rest] |
|
|
else: |
|
|
rest = expand_tuples(L[1:]) |
|
|
return [(item,) + t for t in rest for item in L[0]] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _toposort(edges): |
|
|
""" Topological sort algorithm by Kahn [1] - O(nodes + vertices) |
|
|
inputs: |
|
|
edges - a dict of the form {a: {b, c}} where b and c depend on a |
|
|
outputs: |
|
|
L - an ordered list of nodes that satisfy the dependencies of edges |
|
|
>>> _toposort({1: (2, 3), 2: (3, )}) |
|
|
[1, 2, 3] |
|
|
>>> # Closely follows the wikipedia page [2] |
|
|
>>> # [1] Kahn, Arthur B. (1962), "Topological sorting of large networks", |
|
|
>>> # Communications of the ACM |
|
|
>>> # [2] http://en.wikipedia.org/wiki/Toposort#Algorithms |
|
|
""" |
|
|
incoming_edges = reverse_dict(edges) |
|
|
incoming_edges = OrderedDict((k, set(val)) |
|
|
for k, val in incoming_edges.items()) |
|
|
S = OrderedDict.fromkeys(v for v in edges if v not in incoming_edges) |
|
|
L = [] |
|
|
|
|
|
while S: |
|
|
n, _ = S.popitem() |
|
|
L.append(n) |
|
|
for m in edges.get(n, ()): |
|
|
assert n in incoming_edges[m] |
|
|
incoming_edges[m].remove(n) |
|
|
if not incoming_edges[m]: |
|
|
S[m] = None |
|
|
if any(incoming_edges.get(v, None) for v in edges): |
|
|
raise ValueError("Input has cycles") |
|
|
return L |
|
|
|
|
|
|
|
|
def reverse_dict(d): |
|
|
"""Reverses direction of dependence dict |
|
|
>>> d = {'a': (1, 2), 'b': (2, 3), 'c':()} |
|
|
>>> reverse_dict(d) # doctest: +SKIP |
|
|
{1: ('a',), 2: ('a', 'b'), 3: ('b',)} |
|
|
:note: dict order are not deterministic. As we iterate on the |
|
|
input dict, it make the output of this function depend on the |
|
|
dict order. So this function output order should be considered |
|
|
as undeterministic. |
|
|
""" |
|
|
result = OrderedDict() |
|
|
for key in d: |
|
|
for val in d[key]: |
|
|
result[val] = result.get(val, tuple()) + (key, ) |
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def groupby(func, seq): |
|
|
""" Group a collection by a key function |
|
|
>>> names = ['Alice', 'Bob', 'Charlie', 'Dan', 'Edith', 'Frank'] |
|
|
>>> groupby(len, names) # doctest: +SKIP |
|
|
{3: ['Bob', 'Dan'], 5: ['Alice', 'Edith', 'Frank'], 7: ['Charlie']} |
|
|
>>> iseven = lambda x: x % 2 == 0 |
|
|
>>> groupby(iseven, [1, 2, 3, 4, 5, 6, 7, 8]) # doctest: +SKIP |
|
|
{False: [1, 3, 5, 7], True: [2, 4, 6, 8]} |
|
|
See Also: |
|
|
``countby`` |
|
|
""" |
|
|
|
|
|
d = OrderedDict() |
|
|
for item in seq: |
|
|
key = func(item) |
|
|
if key not in d: |
|
|
d[key] = list() |
|
|
d[key].append(item) |
|
|
return d |
|
|
|
|
|
|
|
|
def typename(type): |
|
|
"""Get the name of `type`. |
|
|
Parameters |
|
|
---------- |
|
|
type : Union[Type, Tuple[Type]] |
|
|
Returns |
|
|
------- |
|
|
str |
|
|
The name of `type` or a tuple of the names of the types in `type`. |
|
|
Examples |
|
|
-------- |
|
|
>>> typename(int) |
|
|
'int' |
|
|
>>> typename((int, float)) |
|
|
'(int, float)' |
|
|
""" |
|
|
try: |
|
|
return type.__name__ |
|
|
except AttributeError: |
|
|
if len(type) == 1: |
|
|
return typename(*type) |
|
|
return '(%s)' % ', '.join(map(typename, type)) |
|
|
|