views:

139

answers:

5

Hi all, I need to make a variable with similar behaviour like in C lanquage. I need byte or unsigned char with range 0-255. This variable should overflow, that means...

myVar = 255
myVar += 1

print myVar #!!myVar = 0!!
+5  A: 

You'll have to do myVar &= 0xFF to ensure it stays in the range 0-255.

You can generally do as many manipulations as you want to the number, so long as you mask it before you print, send to method written in C, or whatever requires it to be in the 8-bit range.

Mark Rushakoff
Longpoke
+5  A: 

The ctypes module contains the functionality you need, albeit in a difficult to use form. For example:

>>> import ctypes
>>> ctypes.c_ubyte(255)
c_ubyte(255)
>>> ctypes.c_ubyte(255 + 1)
c_ubyte(0)

This also works for signed types:

>>> ctypes.c_byte(127 + 1)
c_byte(-128)

You can unbox the object to get the primitive int like so:

>>> ctypes.c_byte(127 + 1).value
-128
fmark
I like this approach except that Python doesn't let you do `c_ubyte(250) + 6`. :/
Mark Rushakoff
Yep, its ugly isn't it? You can't even do `c_ubyte(250) + 6c_ubyte(6)`
fmark
This is probably implementation dependent... OP never said he's accessing some ABI, he just wants modular arithmetic.
Longpoke
What if he wants to emulate signed types? Modular arithmetic won't help here.
fmark
+8  A: 

I see lots of good answers here. However, if you want to create your own type as you mentioned, you could look at the Python Data model documentation. It explains how to make classes that have customized behaviours, for example emulating numeric types.

With this info, you could make a class like so:

class Num:
    def __init__(self, n):
        self.n = (n % 256)

    def __repr__(self):
        return repr(self.n)

     def __add__(self, other):
        return Num(self.n+int(other))

    # transform ourselves into an int, so
    # int-expecting methods can use us
    def __int__(self):
        return self.n

Then you can do things like this:

>>> a = Num(100)
>>> print a
100
>>> b = a + 50
>>> print b
150
>>> c = Num(200)
>>> d = a + c
>>> print d
44

I realize that you may want to support more operations than I've shown in Num, but from this example and the documentation, it should be fairly clear how to add them.

Blair Conrad
Perhaps a ctypes wrapper that implemented the interfaces you described here would be perfect.
fmark
+1 this is how to do it.
Longpoke
+1  A: 

Combining Blair's excellent answer, and my previous one (because they are all distinct solutions and you might like one more than the other:

import ctypes

class CInt:
    def __init__(self, ctype, n):
        self.ctype = ctype
        self.n = ctype(n)

    def __repr__(self):
        return repr(self.n.value)

    def __add__(self, other):
        return CInt(self.ctype, self.n.value + int(other))

    # transform ourselves into an int, so
    # int-expecting methods can use us
    def __int__(self):
        return self.n.value

It is similar to Blair's, except that you can pass it the ctypes type constructor you want to use in the constructor:

>>> n = CInt(ctypes.c_byte, 127)
>>> n + 1
-128
fmark
This will lead to undefined behaviour. OP doesn't want undefined behaviour, he wants modular arithmetic.
Longpoke
+1  A: 

To extend on @Blair Conrad's answer: an alternative implementation could subclass int and override desired methods:

class Byte(int):
    _all = None # cache
    __slots__ = ()
    def __new__(cls, value):
        if Byte._all is None:
           Byte._all = [int.__new__(cls, i) for i in xrange(256)]
        return Byte._all[value % 256]
    def __iadd__(self, other):
        return self + Byte(other)
    def __isub__(self, other):
        return self - Byte(other)
    def __add__(self, other):
        if isinstance(other, Byte):            
            return Byte(int(self) + other)
        return int(self) + other
    def __sub__(self, other):
        if isinstance(other, Byte):            
            return Byte(int(self) - other)
        return int(self) - other
    def __neg__(self):
        return Byte(-int(self))
    def __repr__(self):
        return "Byte(%d)" % self

Example:

>>> myvar = Byte(255)
>>> myvar
Byte(255)
>>> myvar += 1
>>> myvar
Byte(0)
>>> myvar -= 1
>>> myvar
Byte(255)
>>> -myvar
Byte(1)
>>> myvar.i = 1
Traceback (most recent call last):
...
AttributeError: 'Byte' object has no attribute 'i'
>>> from itertools import permutations
>>> for a,b in permutations((Byte(1), Byte(-1), 1), 2):
...     print "%r + %r = %r" % (a,b, a+b)
...     print "%r - %r = %r" % (a,b, a-b)
Byte(1) + Byte(255) = Byte(0)
Byte(1) - Byte(255) = Byte(2)
Byte(1) + 1 = 2
Byte(1) - 1 = 0
Byte(255) + Byte(1) = Byte(0)
Byte(255) - Byte(1) = Byte(254)
Byte(255) + 1 = 256
Byte(255) - 1 = 254
1 + Byte(1) = 2
1 - Byte(1) = 0
1 + Byte(255) = 256
1 - Byte(255) = -254
>>> id(Byte(255)) == id(Byte(1)+Byte(254))
True
J.F. Sebastian