¿Cómo emular un entero de 4 bits en Python 3?

Me gustaría emular el comportamiento de desbordamiento de enteros de 4 bits sin signo, como este:

>>> x, y = Int4(10), Int4(9) >>> x + y Int4(3) >>> x * y Int4(10) 

La herencia de builtin int parece funcionar. ¿Es posible implementar la clase Int4 sin reemplazar los métodos del operador como __add__ ?

No, la subclasificación int no reutilizará automáticamente el tipo cuando se le aplique aritmética:

 >>> class Int4(int): ... def __new__(cls, i): ... return super(Int4, cls).__new__(cls, i & 0xf) ... >>> x, y = Int4(10), Int4(9) >>> x + y 19 >>> type(x + y)  

__add__ anular los métodos __add__ , etc. para volver a Int4() cuando haga esto.

Si solo desea admitir el tipo en sí (por ejemplo, no es compatible con la conversión de otros tipos numéricos en el proceso), puede generar la mayoría de estos:

 from functools import wraps class Int4(int): def __new__(cls, i): return super(Int4, cls).__new__(cls, i & 0xf) def add_special_method(cls, name): mname = '__{}__'.format(name) @wraps(getattr(cls, mname)) def convert_to_cls(self, other): bound_original = getattr(super(cls, self), mname) return type(self)(bound_original(other)) setattr(cls, mname, convert_to_cls) for m in ('add', 'sub', 'mul', 'floordiv', 'mod', 'pow', 'lshift', 'rshift', 'and', 'xor', 'or'): add_special_method(Int4, m) add_special_method(Int4, 'r' + m) # reverse operation 

Esto produce métodos que siempre devuelven el tipo de self partir de métodos aritméticos especiales; esto permitirá una mayor subclasificación de Int4 también.

Manifestación:

 >>> from functools import wraps >>> class Int4(int): ... def __new__(cls, i): ... return super(Int4, cls).__new__(cls, i & 0xf) ... >>> def add_special_method(cls, name): ... mname = '__{}__'.format(name) ... @wraps(getattr(cls, mname)) ... def convert_to_cls(self, other): ... bound_original = getattr(super(cls, self), mname) ... return type(self)(bound_original(other)) ... setattr(cls, mname, convert_to_cls) ... >>> for m in ('add', 'sub', 'mul', 'floordiv', 'mod', 'pow', ... 'lshift', 'rshift', 'and', 'xor', 'or'): ... add_special_method(Int4, m) ... add_special_method(Int4, 'r' + m) # reverse operation ... >>> x, y = Int4(10), Int4(9) >>> x + y 3 >>> x * y 10 

__add__ método __add__ es una buena idea, ya que puede hacer que sus cálculos se vean claramente. Int4(4) + Int4(7) ve mejor que Int4(4).addTo(Int4(7)) (o algo así). Aquí está el código que podría ayudarte:

 class Int4: def __init__(self, num): # initialising self.num = self.cap(num) def __str__(self): return str(self.num) def __repr__(self): return "Int4(" + self.__str__() + ")" def __add__(self, other): # addition return Int4(self.cap(self.num + other.num)) def __sub__(self, other): # subtraction return Int4(self.cap(self.num - other.num)) @staticmethod def cap(num): # a method that handles an overflow while num < 0: num += 16 while num >= 16: num -= 16 return num 

Y probándolo:

 >>> x,y,z = Int4(5), Int4(8), Int4(12) >>> x Int4(5) >>> y Int4(8) >>> z Int4(12) >>> print x+y 13 >>> print z+y 4 >>> print xz 9 

Esto no es tan inteligente como la respuesta de @ martijn-pieters, pero parece funcionar en python 2.7 y 3. *, mientras que obtengo AttributeError: 'wrapper_descriptor' object has no attribute '__module__' en python 2.7 con esa respuesta.

 import sys lt_py3 = sys.version_info < (3,) lt_py33 = sys.version_info < (3, 3) class Int(int): ''' int types ''' def __new__(self, val=0): return int.__new__(self, val & (1 << self.bits - 1) - 1) def __max_type_bits(self, other): ''' determine the largest type and bits available from those in `self` and `other` ''' if hasattr(other, 'bits'): if self.bits < other.bits: return type(other), other.bits return type(self), self.bits def __unary_typed(oper): ''' return a function that redefines the operation `oper` such that the result conforms to the type of `self` ''' def operate(self): return type(self)(oper(self)) return operate def __typed(oper): ''' return a function that redefines the operation `oper` such that the result conforms to the type of `self` or `other`, whichever is larger if both are strongly typed (have a `bits` attribute); otherwise return the result conforming to the type of `self` ''' def operate(self, other): typ, bits = self.__max_type_bits(other) return typ(oper(self, other)) return operate def __unary_ranged(oper): ''' return a function that redefines the operator `oper` such that the result conforms to both the range and the type of `self` ''' def operate(self, other): ''' type and bitmask the result to `self` ''' return type(self)(oper(self) & (1 << self.bits - 1) - 1) return operate def __ranged(oper): ''' return a function that redefines the operator `oper` such that the result conforms to both the range and the type of `self` or `other`, whichever is larger if both are strongly typed (have a `bits` attribute); otherwise return the result conforming to the type of `self` ''' def operate(self, other): ''' type and bitmask the result to either `self` or `other` whichever is larger ''' typ, bits = self.__max_type_bits(other) return typ(oper(self, other) & (1 << bits - 1) - 1) return operate # bitwise operations __lshift__ = __ranged(int.__lshift__) __rlshift__ = __ranged(int.__rlshift__) __rshift__ = __ranged(int.__rshift__) __rrshift__ = __ranged(int.__rrshift__) __and__ = __typed(int.__and__) __rand__ = __typed(int.__rand__) __or__ = __typed(int.__or__) __ror__ = __typed(int.__ror__) __xor__ = __typed(int.__xor__) __rxor__ = __typed(int.__rxor__) __invert__ = __unary_typed(int.__invert__) # arithmetic operations if not lt_py3: __ceil__ = __unary_typed(int.__ceil__) __floor__ = __unary_typed(int.__floor__) __int__ = __unary_typed(int.__int__) __abs__ = __unary_typed(int.__abs__) __pos__ = __unary_typed(int.__pos__) __neg__ = __unary_ranged(int.__neg__) __add__ = __ranged(int.__add__) __radd__ = __ranged(int.__radd__) __sub__ = __ranged(int.__sub__) __rsub__ = __ranged(int.__rsub__) __mod__ = __ranged(int.__mod__) __rmod__ = __ranged(int.__rmod__) __mul__ = __ranged(int.__mul__) __rmul__ = __ranged(int.__rmul__) if lt_py3: __div__ = __ranged(int.__div__) __rdiv__ = __ranged(int.__rdiv__) __floordiv__ = __ranged(int.__floordiv__) __rfloordiv__ = __ranged(int.__rfloordiv__) __pow__ = __ranged(int.__pow__) __rpow__ = __ranged(int.__rpow__) class Int4(Int): bits = 4 x, y = Int4(10), Int4(9) print(x + y) print(x*y) 

Ejecutar este código en un archivo llamado answer.py produce

 $ python2.7 answer.py 3 2 $ python3.4 answer.py 3 2