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 boltons.iterutils import remap
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.append(x) for x in seq if not uniques.count(x)]
return type(seq)(uniques)
class StrType(str):
"""Helper class to print and compare object types.
Provide the object whose type you want to print.
"""Helper class to print and compare object types (used in function 'get_structure').
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):
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):
return f"<{self.value}>"
__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.
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):
return {key:get_structure(value) for key, value in data.items()}
elif (isinstance(data, Iterable)
and not isinstance(data, str)
and not isinstance(data, bytes)):
if data:
if folding_keys:
def visit_fold(path, key, value):
if key in folding_keys:
# We only fold iterable types (except for strings and bytes).
if (isinstance(data, Iterable)
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 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__":
@ -64,9 +97,11 @@ if __name__ == "__main__":
'2 same dict 1 other': [{'same':3, 'as':b'the other'},
{'same':3, 'as':b'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:")
pprint(nested)