Python: Can a subclass of float take extra arguments in its constructor?

扶醉桌前 提交于 2019-11-30 16:50:22

问题


In Python 3.4, I'd like to create a subclass of float -- something that can be used in math and boolean operations like a float, but has other custom functionality and can receive an argument at initialization that controls that functionality. (Specifically, I wanted to have a custom __str__ and a parameter that is used in that method.)

However, I can't seem to get a subclass of float to have a functional two-argument constructor. Why? Is this simply a limitation on extending built-in types?

Example:

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

Now if I try Foo(1,2) I get:

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

Surprisingly, my new __init__'s arguments are enforced too, so if I do Foo(1) I get:

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

What's the deal here? I've done similar things with subtypes of list and was surprised it didn't work on float.


回答1:


As float is immutable you have to overwrite __new__ as well. The following should do what you want:

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

See also Sub-classing float type in Python, fails to catch exception in __init__()




回答2:


Both @cgogolin and @qvpham provide working answers. However, I reckon that float.__init__(value) within the __init__ method is irrelevant to the initialization of Foo. That is, it does nothing to initialize attributes of Foo. As such, it rather causes confusion on the necessity of the operation toward subclassing the float type.

Indeed, the solution can be further simplified as follows:

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



回答3:


the solution of cgogolin is right. it's like so with another immutable classes like int, str, ... But i will write:

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



回答4:


You can do this without implementing __init__ at all:

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

In use:

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



回答5:


While you can handle initialization in the __new__ method, because it's always called before __init__ (or even instead of, if __new__'s returned object is other than an instance of the class), it's best practice decoupling object initizalization in __init__ and leaving __new__ only for object creation.

For instance, in that way you would be able to subclass Foo. (Furthermore, passing *args, **kwargs to __new__ will allow the subclass have any number of positional or named arguments.)

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

However, if you handle initialization in __new__ you will inherit object's __init__ which hasn't more arguments than the instance itself. And you won't be able to subclass it by the common way.

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)
>>> sub_bar = SubBar(1, 2)
TypeError: object.__init__() takes no parameters


来源:https://stackoverflow.com/questions/35943789/python-can-a-subclass-of-float-take-extra-arguments-in-its-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!