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.
精彩评论