subclassing int to attain a Hex representation
Asked Answered
K

5

2

Basically I want to have access to all standard python int operators, eg __and__ and __xor__ etc, specifically whenever the result is finally printed I want it represented in Hex format. (Kind of like putting my calculator into Hex mode)

class Hex(int):
  def __repr__(self):
    return "0x%x"%self
  __str__=__repr__ # this certainly helps with printing

if __name__=="__main__":
  print Hex(0x1abe11ed) ^ Hex(440720179)
  print Hex(Hex(0x1abe11ed) ^ Hex(440720179))

Ideally BOTH line of output should be hexadecimal: 0xfacade, however the first one yields decimal: 16435934

Any ideas?

Krystalkrystalle answered 7/8, 2009 at 2:24 Comment(2)
Why not just use the built-in function hex()? docs.python.org/library/functions.html#hexAssegai
Inserting hex() in the numerous the various appropriate places would be time consuming and imprecise. Switching the representation for anything of type Hex(int) would be a simple refactor. However: I'm thinking one need's to overload all the Hex/int dyadic operators to return a Hex result also (currently they still yield int). Maybe there a "Mixin" for overloading operators in bulk.Krystalkrystalle
P
1

In response to your comment:

You could write a Mixin by yourself:

class IntMathMixin:
    def __add__(self, other):
        return type(self)(int(self).__add__(int(other)))
    # ... analog for the others

Then use it like this:

class Hex(IntMathMixin, int):
    def __repr__(self):
         return "0x%x"%self
    __str__=__repr__ 
Portuna answered 7/8, 2009 at 4:5 Comment(1)
Why was the mixin solution preferred over a simple subclassing as per @Greg Hewgill's answer?Affer
S
7

You should define __repr__ and __str__ separately:

class Hex(int):
  def __repr__(self):
    return "Hex(0x%x)" % self
  def __str__(self):
    return "0x%x" % self

The __repr__ function should (if possible) provide Python text that can be eval()uated to reconstruct the original object. On the other hand, __str__ can just return a human readable representation of the object.

Shope answered 7/8, 2009 at 2:49 Comment(4)
Cheers for noting the distinction between str and repr.Krystalkrystalle
As repr(23) and str(23) give exactly the same result string, I wouldn't get particularly hot and bothered about a subclass of int also doing that -- after all if Hex is in a module you never know whether to return 'Hex(0x0)' or 'themodule.Hex(0x0)' (as you don't know how the import was expressed!) so the eval-ability of the string is pretty iffy anyway (__import__('themodule').Hex(0x0) is more widely eval'able, for modules not in subpackages, but if I saw THAT returned from __repr__ in a code review I'd veto submitting the changeset!-).Stribling
I consider __repr__ sort of a best-effort thing really. Obviously it can't do the right thing in all cases.Shope
The "best" effort (in order to be most likely eval'able) would be the above string with __import__, and I'd just hate to see that -- so "not quite best" is better. And since containers' __str__ uses __repr__ on the items, I'm leaning towards de-emphasizing the distinction.Stribling
S
6

A class decorator, especially in Python 2.6 and beyond, is the handiest way to wrap a lot of methods to "return an instance of this class's type rather than an instance of the superclass", which, as other have indicated, is your underlying issue (beyond quibbles with __str__ vs __repr__, worthwhile but not at all resolutory for your problem;-).

def returnthisclassfrom(specials):
  specialnames = ['__%s__' % s for s in specials.split()]
  def wrapit(cls, method):
    return lambda *a: cls(method(*a))
  def dowrap(cls):
    for n in specialnames:
      method = getattr(cls, n)
      setattr(cls, n, wrapit(cls, method))
    return cls
  return dowrap

@returnthisclassfrom('and or xor')
class Hex(int):
  def __repr__(self): return hex(self)
  __str__ = __repr__

a = Hex(2345)
b = Hex(5432)
print a, b, a^b

In Python 2.6, this emits

0x929 0x1538 0x1c11

as desired. Of course you can add more methodnames to the decorator, etc; if you're stuck with Python 2.5, remove the decorating line (the one starting with @) and use instead

class Hex(int):
  def __repr__(self): return hex(self)
  __str__ = __repr__
Hex = returnthisclassfrom('and or xor')(Hex)

a mite less elegant, but just as effective;-)

Edit: fixed an occurence of "the usual scoping issue" in the code.

Stribling answered 7/8, 2009 at 5:35 Comment(6)
+1. I knew there must be a simpler solution. But I wasn't aware one can use decorators on classes. Thx.Portuna
@Ralph, you're welcome! decorator syntax is only supported since python 2.6, but I also showed how a decorator (with less elegant syntax) can be used on a class in older python (e.g. 2.5, with which you're stuck in google app engine for example, or as the system-supplied Python with MacOSX and many Linux distros, etc;-).Stribling
There's something wrong with this (at least on Python 2.5) where the last method name in specials is used for all preceding methods too, e.g. print a, b, a&b, a|b, a^b >>> 0x929 0x1538 0x1c11 0x1c11 0x1c11, but it should be 0x929 0x1538 0x128 0x1d39 0x1c11.Gravimetric
Yeah, the problem is with the lambda using the last bound value of method when executed, so it will always use the last method in the specials list.Gravimetric
Why the __str__ = __repr__? I'll guess that int's __str__ does not implicitly fall through to __repr__ as per object. Is there any reason for this?Affer
This answers the question in a deeply reusable way--I would upvote 10 times if I could.Priming
G
1

You'll need to get the operators (+, -, ** etc) to return instances of Hex. As is, it will return ints, i.e.

class Hex(int):
    def __repr__(self):
        return "Hex(0x%x)" % self
    def __str__(self):
        return "0x%x" % self
>>> h1 = Hex(100)
>>> h2 = Hex(1000)
>>> h1
Hex(0x64)
>>> h2
Hex(0x3e8)
>>> h1+h2
1100
>>> type(h1+h2)
<type 'int'>

So, you can override the various operators:

class Hex(int):
    def __repr__(self):
        return "Hex(0x%x)" % self
    def __str__(self):
        return "0x%x" % self
    def __add__(self, other):
        return Hex(super(Hex, self).__add__(other))
    def __sub__(self, other):
        return self.__add__(-other)
    def __pow__(self, power):
        return Hex(super(Hex, self).__pow__(power))
    def __xor__(self, other):
        return Hex(super(Hex, self).__xor__(other))

>>> h1 = Hex(100)
>>> h2 = Hex(1000)
>>> h1+h2
Hex(0x44c)
>>> type(h1+h2)
<class '__main__.Hex'>
>>> h1 += h2
>>> h1
Hex(0x44c)
>>> h2 ** 2
Hex(0xf4240)
>>> Hex(0x1abe11ed) ^ Hex(440720179)
>>> Hex(0xfacade)

I don't know about this, I feel that there must be a better way without having to override every operator to return an instance of Hex???

Gravimetric answered 7/8, 2009 at 3:21 Comment(0)
P
1

In response to your comment:

You could write a Mixin by yourself:

class IntMathMixin:
    def __add__(self, other):
        return type(self)(int(self).__add__(int(other)))
    # ... analog for the others

Then use it like this:

class Hex(IntMathMixin, int):
    def __repr__(self):
         return "0x%x"%self
    __str__=__repr__ 
Portuna answered 7/8, 2009 at 4:5 Comment(1)
Why was the mixin solution preferred over a simple subclassing as per @Greg Hewgill's answer?Affer
N
0

Override __str__ as well.

__repr__ is used when repr(o) is called, and to display a value at the interactive prompt. __str__ is called for most instances of stringifying an object, including when it is printed.

The default __str__ behavior for an object is to fall back to the repr, but int provides its own __str__ method (which is identical to __repr__ (before Python 3), but does not fall back to __repr__).

Ned answered 7/8, 2009 at 2:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.