我想创建一个扩展int
基类的类,以便该对象本身是一个整数(即,您直接对其进行设置并从中读取),但也具有输入验证功能-例如,仅允许给定范围。
根据我的研究,扩展常规基类时不会调用__init__
方法,因此我不确定如何执行此操作。我也不清楚如何访问对象的值(即分配给它的实际整数)或如何在类中修改该值。我看到了扩展任何基类(字符串,浮点型,元组,列表等)的相同问题。
如果我可以使用__init__
,它将类似于:
class MyInt(int):
def __init__(self, value):
if value < 0 or value > 100:
raise ValueError('MyInt objects must be in range 0-100, value was {}'.format(value))
self = value
如何验证扩展的int类中的新值?
在这种情况下,您实际上不必覆盖__new__
。创建对象后,将调用__init__
,您可以检查其是否在范围内。
class MyInt(int):
def __init__(self, x, **kwargs):
if self < 0 or self > 100:
raise ValueError('MyInt objects must be in range 0-100, value was {}'.format(value))
您可以覆盖__new__
以在MyInt(...)
返回新对象之前引发异常。
class MyInt(int):
def __new__(cls, *args, **kwargs):
x = super().__new__(cls, *args, **kwargs) # Let any value error propagate
if x < 0 or x > 100:
raise ValueError('MyInt objects must be in range 0-100, value was {}'.format(value))
return x
[您可能想尝试验证调用super().__new__
的参数before,但严格来说,这与其他类的配合不好。
在研究了__new__
中的MarkM's suggested link函数之后,该方法将完成我所寻找的,但是我也质疑这是否是正确的方法。请参阅下面的讨论和解决方案部分。
但是,我也质疑这样做的必要性,至少对于简单的输入验证检查而言。子类化不可变基类的好处是,您可以获得不可变性的所有好处,例如内置哈希,字符串表示等,它们直接从基类继承。但是,您只需将属性添加到另一个类,添加一个setter函数并使它不可变,您将获得相同的好处。
子类的优点是,如果您希望所有不可变的对象都具有相同的输入验证和/或可以应用于许多不同的类/模块的其他方法,而无需创建将它们转换为可变的单独类的额外复杂性对象,然后需要附加级别的属性或方法访问(即,您通过“ MyInt”类创建“ myint”对象,但需要“ value”属性或类似的东西来访问基础int,例如myint.value
而不只是myint
)。
因此,也许有更好的方法来执行此操作,但是要回答我自己的问题,这是我编写的测试脚本,至少可以使此工作正常进行。
[请注意int can have multiple arguments,当将字符串解释为特定的基数时,例如('1111',2),它将二进制字符串'1111'转换为十进制15。基数也可以输入为kwarg,因此需要将* args和** kwargs完全传递给int __new__
函数。
而且,在进行验证之前,我最终先对int进行了调用,以便在尝试进行验证之前将浮点数和字符串首先转换为int。
请注意,由于MyInt继承了int的子类,因此您必须返回一个int值-失败时不能返回“ None”(尽管您可以返回0或-1)。这导致我引发ValueError并在主脚本中处理错误。
class MyInt(int):
def __new__(cls, *args, **kwargs):
value = super().__new__(cls, *args, **kwargs)
argstr = ', '.join([str(arg) for arg in args]) # Only for prototype test
print('MyInt({}); Returned {}'.format(argstr, value)) # Only for prototype test
if value < 0 or value > 100:
raise ValueError(' ERROR!! Out of range! Must be 0-100, was {}'.format(value))
return value
if __name__ == '__main__':
myint = MyInt('1111', 2) # Test conversion from binary string, base 2
print('myint = {} (type: {})\n'.format(myint, type(myint)))
for index, arg in enumerate([-99, -0.01, 0, 0.01, 0.5, 0.99, 1.5, 100, 100.1, '101']):
try:
a = MyInt(arg)
except ValueError as ex:
print(ex)
a = None
finally:
print(' a : {} = {}'.format(type(a), a))