开发者

Python Custom Numeric Types: Wrap around number

Quick one hopefully; brain has gone blank.

Is it possible / practical to have a class that is just a generic numeric value (eg float, int, etc) that when a value becomes <=0 it loops around to os.maxint-value, similar to a unsigned value in C?

'This is completely insane' is also an acceptable answer!

'Reasonable' use cause; a particular 'effort' cost evaluation where a negative 'cost' is, while theoretically fine, practically inva开发者_C百科lid and is usually checked for each value and flipped to maxint if <=0.

Instead of all those lovely additional comparisons, just give the cost-value this inate behaviour.


Found the answer

Using Numpy's uint types works for this.

import numpy.uint32
valueA=uint32(5)
valueB=uint32(-5)
assert valueA < valueB


Firstly, Python generally does not have maxint value since it's integer type is unbounded. When it goes over sys.maxint it turns into a long type. Still you can easily implement what you want by writing descriptor if you want this to be class attribute and property if you want this to be instance attribute.


I recently wrote something like this and blogged about it. The TL/DR of why one would want this is essentially already stated by the OP: You might want to use Python to simulate the behavior of C or x86 assembly code, where operations are often performed modulo 2³², or other powers of two. But also, you might want a type that implements elements of a finite field. The following code does both.

I understand that the fancy metaclassing is a bit of an overkill, but I wanted it really compact and the metaclass is an easy way to keep it fairly short.

class wrapped(type):
    def __new__(cls, name, bases, nmspc, mod=None, bits=None, hexrep=False, signed=False):
        assert int in bases[0].__mro__
        if None not in (mod, bits) and 1 << bits != mod:
            raise ValueError('incompatible mod and bits argument.')
        mod = mod or bits and 1 << bits
        if mod:
            for op in 'add', 'and', 'floordiv', 'lshift', 'mod', 'mul', 'or', 'rshift', 'sub', 'xor':
                opname = F'__{op}__'
                nmspc[F'__r{op}__'] = nmspc[opname] = lambda self, them, op=getattr(int, opname): (
                    self.__class__(op(self, them)))
            nmspc['__rtruediv__'] = nmspc['__truediv__'] = lambda self, them, p=int.__pow__: (
                self.__class__(self * p(them, -1, mod)))
            nmspc['__pow__'] = lambda self, them, op=int.__pow__: self.__class__(op(self, them, mod))
            nmspc['__inv__'] = lambda self, op=int.__invert__: self.__class__(op(self))
            nmspc['__neg__'] = lambda self, op=int.__neg__: self.__class__(op(self))
            nmspc.update(mod=mod, signed=signed)
            if hexrep is True:
                nib, up = divmod((mod.bit_length() - 1) - 1, 4)
                nib += bool(up)
                nmspc['__repr__'] = lambda self: F'{self:#0{nib}x}'
        return type.__new__(cls, name, bases, nmspc)

    def __call__(cls, value=0, *args, **kwargs):
        if isinstance(value, int):
            value = value % cls.mod
            if cls.signed and (value & (cls.mod >> 1)):
                value -= cls.mod
            return type.__call__(cls, value)
        return cls(int(value, *args, **kwargs))


class wrap(int, metaclass=wrapped):
    pass

you would use it as follows:

In [2]: class uint32(wrap, mod=(1 << 32), hexrep=True): pass

In [3]: a = uint32(0xC0C4C01A)

In [4]: a
Out[4]: 0x0C0C4C01A

In [5]: a ** 0x00F
Out[5]: 0x02A028000

Or, if you don't like the hexadecimal representation, just don't set hexrep to True for your new integer type.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜