我需要编写一个实现32位无符号整数的类,就像它们在C编程语言中一样.我最关心的是二元转换,但我通常希望我的班级:
int具有与工作int正常U32类(int + U32,U32+ int等)的任何操作也返回U32正如在这个答案中可以找到的,我得到了一个在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))
Run Code Online (Sandbox Code Playgroud)
这是错误:
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'
Run Code Online (Sandbox Code Playgroud)
getattr在Python 3中看起来根本没有调用这个技巧.为什么会这样?如何在Python 2和3下使用此代码?
您的Python 2解决方案依赖于旧样式类行为.您的Python 2代码将以与Python 3相同的方式失败,以使您的类继承自object:
class U32(object):
Run Code Online (Sandbox Code Playgroud)
这是因为对于新式类,在类型而不是对象本身上查找特殊方法.此行为更改修复了旧模型的几个极端情况.
在实践中,这意味着类似__div__的方法直接在U32其自身上查找,而不是作为实例的属性U32,并且不查询__getattr__钩子.
不幸的是,特殊方法查找也绕过任何__getattr__或__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_)
Run Code Online (Sandbox Code Playgroud)
我更新了代理函数以正确处理多个参数,并且如果int返回则自动强制返回到自定义类.