1

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?

3
  • I'm also surprised += works. address += Hex(0xfe) doesn't get the specified results. Commented May 11, 2022 at 0:04
  • " I could not find such hence the above class." you can just define your own, class Hex(int): ... then simply implement __repr__ the way you did above Commented May 11, 2022 at 0:18
  • a = Hex(0xf); a += Hex(0x1); a prints 16. When I tried to overwrite __add__ I got an infinite loop until I figured that __add__ should return: Hex(int(self) + other) To @Bharel: There are two additions, 0xef00 and 0xfe. Commented May 11, 2022 at 1:10

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

2 Comments

I tried something like this and it did not work in some instance. Now, I can only find the following problem: a = Hex(0xf); a += Hex(0x1); a prints 16. I tried to fix that, but could not immediately do so.
I think then the best way to go would be to override all magic methods you'd use with a super call to the base implementation. Edited the answer.
0

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

1 Comment

a = Hex(0xf); a += Hex(0x1); a prints 16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.