在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
无法正常工作感到惊讶。
答案 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)
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)