'Print a Python variable in hexadecimal by default

I wish to display some variables, data members in hexadecimal format any time they are printed.

My current approach is to define a class Hex:

class Hex:
    """Facilitate printing an integer in hexadecimal format."""
    def __init__(self, data):
        if not isinstance(data, int):
            raise ValueError("Hex expects an integer.")
        self._data = data
    def __repr__(self):
        return hex(self._data)
    def __eq__(self, other):
        return self._data == other
    def __lt__(self, other):
        return self._data < other
    def __le__(self, other):
        return self._data <= other
    def __gt__(self, other):
        return self._data > other
    def __ge__(self, other):
        return self._data >= other
    def __add__(self, right):
        return self._data + right
    def __radd__(self, left):
        return self._data + left
    def __hash__(self):
        return hash(self._data)

This works all right as the following examples demonstrate it:

address = Hex(0xdeadbeef)
record1 = {'address': address, 'counter': 42}
print(record1)
{'address': 0xdeadbeef, 'counter': 42}

The __hash__ is defined so that the object is hashable and can be used as a key in a dict:

record2 = {address: 'address'}
print(record2)
{0xdeadbeef: 'address'}

The equality and comparisons are defined so that handling of duplicates and sorting works:

list1 = [Hex(0xff), 15, 23, Hex(0xf), 255]
print(list1)
[0xff, 15, 23, 0xf, 255]
print(set(list1))
{15, 23, 0xff}
list1.sort()
print(list1)
[15, 0xf, 23, 0xff, 255]

The __add__ and __radd__ are defined so that pointer arithmetic is supported:

new_address = Hex(record1['address'] + 0x20400000)
print(new_address)
0xfeedbeef

address += 0x3f00
address += Hex(0xfe)
print(address)
0xdeadfeed

And now to the questions.

Is there a built in or existing hex integer that somehow has its own __repr__ attached that prints it in hex, but otherwise it would work as an int. I could not find such hence the above class.

The above pointer arithmetic works (subtraction, negation can be added similarly) and I was surprised that += works as well. Should I still add __iadd__?

Should I add/override __new__? Something like the following would not create duplicate instances for the same value:

def __new__(cls, *args, **kwargs):
    if not hasattr(cls, 'instances'):
        cls.instances = {}
    data = args[1]
    if data in cls.instances:
        return cls.instances[data]
    # Create if not found:
    inst = super(Hex, cls).__new__(cls) #, *args, **kwargs)
    cls.instances[data] = inst
    return inst

Any other suggestion to fix the class Hex or make it better?



Solution 1:[1]

Instead of creating a new class from scratch that holds an int (_data), why don't you simply inherit from int and override the __repr__ method?

I wouldn't go as far as optimizing for duplicated values.

class Hex(int):
    def __repr__(self):
        return hex(self)

Edit -

Override the methods that return a new int with a call to super() and return as a Hex object. For example -

def __add__(self, val):
    return Hex(super().__add__(val))

Looks a little verbose but it works. Plus you can write a monkey patcher that takes a list of all the operations you want to override -

ops = ['__add__', '__sub__', '__mul__']

def monkey_patch(operation: str):
    """
    wraps Hex() around int's magic 
    method for provided operation.
    """
    old_op = getattr(int, operation)
    new_op = lambda self, val : Hex(old_op(self, val))
    setattr(Hex, operation, new_op)

for op in ops:
    monkey_patch(op)

full code

This works -

>>> a = Hex(0xf)
>>> a += 1
>>> a
0x10
>>> a -= 1
>>> a
0xf
>>> a * 2
0x1e

Solution 2:[2]

How about inheriting from int?

>>> class Hex(int):
...  def __repr__(self):
...    return hex(self)
...
>>> a = Hex(123)
>>> a
0x7b
>>> a = Hex(16)
>>> a
0x10
>>> Hex(a + 2)
0x12

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Bharel