Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 309
Shimon Rura Avatar asked Mar 11 '16 15:03

Shimon Rura


2 Answers

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
like image 39
kodemartin Avatar answered Sep 28 '22 11:09

kodemartin


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__()

like image 191
cgogolin Avatar answered Sep 28 '22 11:09

cgogolin