Reimplemented __hash__ and __eq__ in StrType, 'cause we want the comparison to be based on class name instead of instance's ID. Added a 'folding' feature in order to hide elements based on keys

This commit is contained in:
Cacahuete Lenovo Ubuntu 2018-09-17 22:11:46 +02:00
parent d1ea1f7317
commit df78a1ad2e

View file

@ -1,38 +1,71 @@
from collections import Iterable from collections import Iterable
from boltons.iterutils import remap
def unique(seq): def unique(seq):
"""Return a new sequence made of unique elements from seq.""" """Return a new sequence made of unique elements from seq.
Code borrowed from the Internet.
"""
uniques = [] uniques = []
[uniques.append(x) for x in seq if not uniques.count(x)] [uniques.append(x) for x in seq if not uniques.count(x)]
return type(seq)(uniques) return type(seq)(uniques)
class StrType(str): class StrType(str):
"""Helper class to print and compare object types. """Helper class to print and compare object types (used in function 'get_structure').
Provide the object whose type you want to print. When instanciating this class, provide the object whose type you want to print.
__hash__ and __eq__ implemented in order to filter duplicates and yet stay compatible with
uniqueness detection (to fit in sets for instance).
""" """
def __init__(self, obj): def __init__(self, obj):
self.value = type(obj).__name__ self.value = type(obj).__name__
def __hash__(self):
return hash(self.value)
def __eq__(self, other):
if not isinstance(other, StrType):
return False
else:
return self.value == other.value
def __str__(self): def __str__(self):
return f"<{self.value}>" return f"<{self.value}>"
__repr__ = __str__ __repr__ = __str__
def get_structure(data): def get_structure(data, folding_keys=[], folding_tag=" (folded)"):
"""Return a kind of map of the could-be nested data. """Return a kind of map of the could-be nested data.
Duplicates are removed from sequences. Duplicates are removed from sequences.
If 'folding_keys' are provided then the corresponding values in the data set will be folded,
that is to say replaced by their data type (thus transformed into leaves). The key will be
appended with 'folding_tag'.
""" """
if isinstance(data, dict): if folding_keys:
return {key:get_structure(value) for key, value in data.items()} def visit_fold(path, key, value):
elif (isinstance(data, Iterable) if key in folding_keys:
and not isinstance(data, str) # We only fold iterable types (except for strings and bytes).
and not isinstance(data, bytes)): if (isinstance(data, Iterable)
if data: and not isinstance(data, str)
and not isinstance(data, bytes)):
return "".join([key, folding_tag]), type(value)()
return True
data = remap(data, visit=visit_fold)
def extract_structure(data):
if data and isinstance(data, dict):
return {key:get_structure(value) for key, value in data.items()}
elif data and (isinstance(data, Iterable)
and not isinstance(data, str)
and not isinstance(data, bytes)):
# The iterable is not empty, we map each of its elements
return unique(type(data)([get_structure(item) for item in data])) return unique(type(data)([get_structure(item) for item in data]))
return StrType(data) # It's a leaf (or empty iterable),
# we return the name of its type
return StrType(data)
return extract_structure(data)
if __name__ == "__main__": if __name__ == "__main__":
@ -64,9 +97,11 @@ if __name__ == "__main__":
'2 same dict 1 other': [{'same':3, 'as':b'the other'}, '2 same dict 1 other': [{'same':3, 'as':b'the other'},
{'same':3, 'as':b'the other'}, {'same':3, 'as':b'the other'},
{'different':'from','the':'other'}], {'different':'from','the':'other'}],
'play me': MaClasse} 'play me': MaClasse,
'Two folded keys':{'Fold me :)':[1,2,3,4,5],
'Fold me too':"folded, can't see me!"}}
structure = get_structure(nested) structure = get_structure(nested, ['Fold me :)','Fold me too'], ' [+]')
print("The nested data:") print("The nested data:")
pprint(nested) pprint(nested)