如何在Python 3中模拟4位整数?
我想模拟无符号4位整数的溢出行为,像这样:
>>> x, y = Int4(10), Int4(9)
>>> x + y
Int4(3)
>>> x * y
Int4(10)
看起来内置的 int
继承是可行的。有没有办法实现 Int4
类,而不需要重写像 __add__
这样的操作符方法呢?
3 个回答
0
这段内容没有@martijn-pieters的回答那么聪明,但在python 2.7和3.*上似乎都能正常工作。而我在python 2.7上用那个回答时,会遇到AttributeError: 'wrapper_descriptor' object has no attribute '__module__'
的错误。
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)
把这段代码放在一个叫answer.py
的文件里运行,会得到
$ python2.7 answer.py
3
2
$ python3.4 answer.py
3
2
0
重写 __add__
方法是个不错的主意,因为这样可以让你的计算看起来更清晰。比如说 Int4(4) + Int4(7)
这种写法比 Int4(4).addTo(Int4(7))
(或者类似的写法)要好看得多。
这里有一些代码可以帮助你:
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
接下来是测试代码:
>>> 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 x-z
9
6
不,直接继承 int
并不会在进行数学运算时自动使用这个类型:
>>> 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)
<type 'int'>
你需要重写 __add__
等方法,以便在进行运算时能够转换回 Int4()
类型。
如果你只想支持这个类型本身(比如不想在过程中支持其他数字类型),那么你可以生成大部分这些方法:
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
这样做会产生一些方法,这些方法在进行数学运算时总是返回 self
的类型;这也允许你进一步继承 Int4
。
示例:
>>> 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