Python:float的子类可以在其构造函数中获取额外的参数吗?

时间:2016-03-11 15:18:17

标签: python python-3.x types subclass built-in

在Python 3.4中,我想创建一个float的子类 - 可以在数学和布尔运算中使用的东西,如float,但有其他自定义功能,可以接收初始化时控制该功能的参数。 (具体来说,我希望有一个自定义__str__和该方法中使用的参数。)

但是,我似乎无法使float的子类具有功能性的双参数构造函数。为什么?这仅仅是扩展内置类型的限制吗?

示例:

class Foo(float):
    def __init__(self, value, extra):
        super().__init__(value)
        self.extra = extra

现在,如果我尝试Foo(1,2),我会得到:

TypeError: float() takes at most 1 argument (2 given)

令人惊讶的是,我的新__init__的论点也被强制执行,所以如果我Foo(1)我得到了:

TypeError: __init__() missing 1 required positional argument: 'extra'

这是什么交易?我使用list的子类型做了类似的事情,并且对float无法正常工作感到惊讶。

5 个答案:

答案 0 :(得分:10)

由于float是不可变的,你也必须覆盖__new__。以下应该做你想要的:

class Foo(float):
    def __new__(self, value, extra):
        return float.__new__(self, value)
    def __init__(self, value, extra):
        float.__init__(value)
        self.extra = extra

foo = Foo(1,2)
print(str(foo))
1.0
print(str(foo.extra))
2

另见Sub-classing float type in Python, fails to catch exception in __init__()

答案 1 :(得分:4)

@cgogolin和@qvpham均提供有效的答案。但是,我认为float.__init__(value)方法中的__init__Foo的初始化无关。也就是说,它没有初始化Foo的属性。这样,它反而导致在操作上必须继承float类型的困惑。

实际上,解决方案可以进一步简化如下:

In [1]: class Foo(float):
   ...:     def __new__(cls, value, extra):
   ...:        return super().__new__(cls, value)
   ...:     def __init__(self, value, extra):
   ...:        self.extra = extra

In [2]: foo = Foo(1,2)
   ...: print(str(foo))
1.0

In [3]: print(foo.extra)
2

答案 2 :(得分:3)

cgogolin的解决方案是正确的。它与其他不可变的类如int,str,...是这样的但是我会写:

class Foo(float):
    def __new__(cls, value, extra):
       return super().__new__(cls, value)
    def __init__(self, value, extra):
       float.__init__(value)
       self.extra = extra

答案 3 :(得分:0)

您可以完全不执行__init__来做到这一点:

class Foo(float):
    def __new__(cls, value, extra):
        instance = super().__new__(cls, value)
        instance.extra = extra
        return instance

使用中:

>>> foo = Foo(1, 2)
>>> print(foo)
1.0
>>> print(foo.extra)
2

答案 4 :(得分:-1)

虽然您可以在__new__方法中处理初始化,因为它总是在__init__之前调用(或者如果__new__返回的对象不是类),这是在__init__中解耦对象初始化的最佳实践,而__new__仅用于对象创建。

例如,通过这种方式,您可以继承Foo的子类。 (此外,将*args, **kwargs传递给__new__将允许子类具有任意数量的位置或命名参数。)

class Foo(float):
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls)

    def __init__(self, value, extra):
        float.__init__(value)
        self.extra = extra

class SubFoo(Foo):
    def __init__(self, value, extra, more):
        super().__init__(value, extra)
        self.more = more

但是,如果您在__new__中进行初始化,则会继承object的{​​{1}},该实例的参数不多于实例本身。而且您将无法通过通用方式将其子类化。

__init__
class Bar(float):
    def __new__(cls, value, extra):
        self = super().__new__(cls, value)
        self.extra = extra
        return self

class SubBar(Bar):
    def __init__(self, value, extra):
        super().__init__(value, extra)