Search code examples
pythonprintingformatfrozenset

Improve print readability of nested Frozenset


The output of the code example below has terrible readability. The data I'd like to analyse is hidden within numerous frozenset({}) prints.

A = frozenset(["A1", "A2"])
B = frozenset(["B1", "B2"])
C = frozenset(["C1", "C2"])

foo = {A, B, C}

print(foo)
# {frozenset({'B1', 'B2'}), frozenset({'C1', 'C2'}), frozenset({'A1', 'A2'})}

print(*foo)
# frozenset({'B1', 'B2'}) frozenset({'C1', 'C2'}) frozenset({'A1', 'A2'})

Is there an easy way to neglect the printouts of the data collection type? I'm only interested in the grouping of the entries.

A more readable output would be:

({'B1', 'B2'}, {'C1', 'C2'}, {'A1', 'A2'})

TLD
I stumbled upon this issue when trying to find to optimal way to group a large number of items in pairs. The grouping had to comply to a lot of boundary conditions and I need a quick check if the solution found makes sense.

items = {'B1', 'B2', 'C1', 'C2', 'A1', 'A2'}
pairs = get_best_grouping(items) # Outputs a set containing frozensets
print(pairs)

Change the str function of frozenset (or any other native type) is a related question, but seems like a big intervention for this problem.


Solution

  • As you mentioned in the question, writing your own __str__ or __repr__ function would be the propper way to go, if that sounds too much hassle, how about you modify the string after the fact?

    print(str(frozenset({1,2,3})).replace('frozenset',''))

    ({1, 2, 3})

    Assuming of course your set does not contain the word "frozenset".

    But it is really not much more effort to make a new _frozenset class follwing the same logic as above (note the 1 in replace to make sure that we only replace the first occurence of the str '_frozenset'.):

    
    class _frozenset(frozenset):
        def __repr__(self):
            return (frozenset.__repr__(self)).replace('_frozenset','',1)
    
    print(_frozenset([1, 2, '_frozenset']))
    
    

    ({1, 2, '_frozenset'})

    Imho, key is here to simply reuse the definition of __repr__ of the builtin frozenset so we don't have to worry too much about the logic behind how to represent an iterable. Against first intuition, frozenset.__repr__() seems to (I have not looked into it) inspect the name of the class it is in and prepend that so it is not 'frozenset' but '_frozenset' one needs to replace.