我是否可以创建一个类来扩展在Python中具有输入验证的`int`基类?

问题描述 投票:1回答:2

我想创建一个扩展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类中的新值?

python class validation inheritance base-class
2个回答
2
投票

在这种情况下,您实际上不必覆盖__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,但严格来说,这与其他类的配合不好。


0
投票

在研究了__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))
© www.soinside.com 2019 - 2024. All rights reserved.