我需要编写一个实现32位无符号整数的类,就像它们在C编程语言中一样。我最关心的是二元转换,但我通常希望我的班级:
int
并且正确使用int
U32
类(int + U32
,U32
+ int等)的任何操作也会返回U32 可以在this answer中找到,我得到了一个在Python 2下运行的解决方案。最近我尝试在Python 3下运行它,并注意到以下测试代码在旧版本的Python下工作正常,Python 3引发错误:
class U32:
"""Emulates 32-bit unsigned int known from C programming language."""
def __init__(self, num=0, base=None):
"""Creates the U32 object.
Args:
num: the integer/string to use as the initial state
base: the base of the integer use if the num given was a string
"""
if base is None:
self.int_ = int(num) % 2**32
else:
self.int_ = int(num, base) % 2**32
def __coerce__(self, ignored):
return None
def __str__(self):
return "<U32 instance at 0x%x, int=%d>" % (id(self), self.int_)
def __getattr__(self, attribute_name):
print("getattr called, attribute_name=%s" % attribute_name)
# you might want to take a look here:
# https://stackoverflow.com/q/19611001/1091116
r = getattr(self.int_, attribute_name)
if callable(r): # return a wrapper if integer's function was requested
def f(*args, **kwargs):
if args and isinstance(args[0], U32):
args = (args[0].int_, ) + args[1:]
ret = r(*args, **kwargs)
if ret is NotImplemented:
return ret
if attribute_name in ['__str__', '__repr__', '__index__']:
return ret
ret %= 2**32
return U32(ret)
return f
return r
print(U32(4) / 2)
print(4 / U32(2))
print(U32(4) / U32(2))
这是错误:
Traceback (most recent call last):
File "u32.py", line 41, in <module>
print(U32(4) / 2)
TypeError: unsupported operand type(s) for /: 'U32' and 'int'
看起来在Python 3中根本没有调用getattr
技巧。为什么会这样?如何在Python 2和3下使用此代码?
答案 0 :(得分:6)
您的Python 2解决方案依赖于旧样式类行为。您的Python 2代码将以与Python 3相同的方式失败,以使您的类继承自object
:
class U32(object):
这是因为对于新式类,在类型上查找特殊方法,而不是对象本身。此行为更改修复了旧模型的几个极端情况。
在实践中,这意味着直接在__div__
本身上查找U32
等方法,而不是U32
的实例上的属性,以及{{ 1}}不咨询钩子。
不幸的是,特殊方法查找还绕过任何__getattr__
或__getattr__
个钩子。请参阅documentation on Special Method lookups:
除了为了正确性而绕过任何实例属性之外,隐式特殊方法查找通常也会绕过
__getattribute__
方法,即使对象的元类也是如此:[...]
以这种方式绕过
__getattribute__()
机制为解释器内的速度优化提供了很大的空间,代价是处理特殊方法的一些灵活性(必须在类对象本身上按顺序设置特殊方法由翻译一致地调用。)
您唯一的选择是在您的班级上动态设置所有特殊方法。类装饰器在这里可以做得很好:
__getattribute__()
我更新了代理函数以正确处理多个参数,并在返回def _build_delegate(name, attr, cls, type_):
def f(*args, **kwargs):
args = tuple(a if not isinstance(a, cls) else a.int_ for a in args)
ret = attr(*args, **kwargs)
if not isinstance(ret, type_) or name == '__hash__':
return ret
return cls(ret)
return f
def delegated_special_methods(type_):
def decorator(cls):
for name, value in vars(type_).items():
if (name[:2], name[-2:]) != ('__', '__') or not callable(value):
continue
if hasattr(cls, name) and not name in ('__repr__', '__hash__'):
continue
setattr(cls, name, _build_delegate(name, value, cls, type_))
return cls
return decorator
@delegated_special_methods(int)
class U32(object):
def __init__(self, num=0, base=None):
"""Creates the U32 object.
Args:
num: the integer/string to use as the initial state
base: the base of the integer use if the num given was a string
"""
if base is None:
self.int_ = int(num) % 2**32
else:
self.int_ = int(num, base) % 2**32
def __coerce__(self, ignored):
return None
def __str__(self):
return "<U32 instance at 0x%x, int=%d>" % (id(self), self.int_)
时自动强制返回自定义类。
答案 1 :(得分:0)
(已通过Python 3.7测试)
indicator="ind_c"
现在可以轻松继承其他uint类型:
class U32(int):
MAXVALUE = 0xffffffff
def __new__(cls, value):
return int.__new__(cls, value & cls.MAXVALUE)
def __add__(self, *args, **kwargs):
return self.__new__(type(self),int.__add__(self, *args, **kwargs))
def __radd__(self, *args, **kwargs):
return self.__new__(type(self),int.__radd__(self, *args, **kwargs))
def __sub__(self, *args, **kwargs):
return self.__new__(type(self), int.__sub__(self, *args, **kwargs))
def __rsub__(self,*args, **kwargs):
return self.__new__(type(self),int.__rsub__(self, *args, **kwargs))
def __mul__(self, *args, **kwargs):
return self.__new__(type(self),int.__mul__(self, *args, **kwargs))
def __rmul__(self, *args, **kwargs):
return self.__new__(type(self),int.__rmul__(self, *args, **kwargs))
def __div__(self, *args, **kwargs):
return self.__new__(type(self),int.__floordiv__(self, *args, **kwargs))
def __rdiv__(self, *args, **kwargs):
return self.__new__(type(self),int.__rfloordiv__(self, *args, **kwargs))
def __truediv__(self, *args, **kwargs):
return self.__new__(type(self),int.__floordiv__(self, *args, **kwargs))
def __rtruediv__(self, *args, **kwargs):
return self.__new__(type(self),int.__rfloordiv__(self, *args, **kwargs))
def __pow__(self, *args, **kwargs):
return self.__new__(type(self),int.__pow__(self, *args, **kwargs))
def __rpow__(self, *args, **kwargs):
return self.__new__(type(self),int.__rpow__(self, *args, **kwargs))
def __lshift__(self, *args, **kwargs):
return self.__new__(type(self),int.__lshift__(self, *args, **kwargs))
def __rlshift__(self, *args, **kwargs):
return self.__new__(type(self),int.__rlshift__(self, *args, **kwargs))
def __rshift__(self, *args, **kwargs):
return self.__new__(type(self),int.__rshift__(self, *args, **kwargs))
def __rrshift__(self, *args, **kwargs):
return self.__new__(type(self),int.__rrshift__(self, *args, **kwargs))
def __and__(self, *args, **kwargs):
return self.__new__(type(self),int.__and__(self, *args, **kwargs))
def __rand__(self, *args, **kwargs):
return self.__new__(type(self),int.__rand__(self, *args, **kwargs))
def __or__(self, *args, **kwargs):
return self.__new__(type(self),int.__ror__(self, *args, **kwargs))
def __ror__(self, *args, **kwargs):
return self.__new__(type(self),int.__ror__(self, *args, **kwargs))
def __xor__(self, *args, **kwargs):
return self.__new__(type(self),int.__rxor__(self, *args, **kwargs))
def __rxor__(self, *args, **kwargs):
return self.__new__(type(self),int.__rxor__(self, *args, **kwargs))
class U16(U32):
def __new__(cls, value):
cls.MAXVALUE = 0xFFFF
return int.__new__(cls, value&cls.MAXVALUE)
class U8(U32):
def __new__(cls, value):
cls.MAXVALUE = 0xFF
return int.__new__(cls, value&cls.MAXVALUE)
type( U8(0) + 1 ) = <class '__main__.U8'>
U8(0xcde) = 0xde
U8(2) + 0xff = 0x1
U8(0) + 0xfff = 0xff
U8(0) - 2 = 0xfe
U8(0xf) * 32 = 0xe0
U8(0x7)**3 = 0x57
U8(8) / 3 = 0x2
U8(0xff)>>4 = 0xf
U8(0xff)<<4 = 0xf0
type( 1 + U8(0) ) = <class '__main__.U8'>
我创建了这些算法,以接管现有加密算法的一些计算,这些算法很大程度上依赖于uint32上的移位。
一个值得注意的选择是反射(或交换)运算符功能。这些是带有r前缀的字符,例如__radd __,__ rsub__等。它们在我们的uint类型是第二个运算符时才起作用,例如1 + U8(0xff)。如果实现了__radd__,则结果将为0x0且类型为U8;如果未实现__radd__,则结果将为0x100且类型为int。虽然U8(0xff)+1 = 0保持不变(因为已定义__add__)。 对于我的用例,最好同时定义反射函数,以确保所有结果均为U32类型。