Python: float 的子类可以在其构造函数中使用额外的参数吗?
Python: Can a subclass of float take extra arguments in its constructor?
在 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
.
上不起作用
由于 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__()
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
@cgogolin 和@qvpham 都提供了有效的答案。但是,我认为__init__
方法中的float.__init__(value)
与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
您可以在根本不实施 __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
虽然您可以在 __new__
方法中处理初始化,因为它总是在 __init__
之前调用(或者甚至代替,如果 __new__
返回的对象不是实例在 class 中),最好的做法是在 __init__
中解耦对象初始化,并让 __new__
仅用于对象创建。
例如,这样你就可以class Foo
。 (此外,将 *args, **kwargs
传递给 __new__
将允许子 class 具有任意数量的位置或命名参数。)
class Foo(float):
def __new__(cls, value, *args, **kwargs):
return super().__new__(cls, value)
def __init__(self, value, extra):
self.extra = extra
class SubFoo(Foo):
def __init__(self, value, extra, more):
super().__init__(value, extra)
self.more = more
但是,如果您在 __new__
中处理初始化,您将继承 object
的 __init__
,它没有比实例本身更多的参数。而且您将无法通过常规方式对其进行子class。
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
在 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
.
由于 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__()
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
@cgogolin 和@qvpham 都提供了有效的答案。但是,我认为__init__
方法中的float.__init__(value)
与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
您可以在根本不实施 __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
虽然您可以在 __new__
方法中处理初始化,因为它总是在 __init__
之前调用(或者甚至代替,如果 __new__
返回的对象不是实例在 class 中),最好的做法是在 __init__
中解耦对象初始化,并让 __new__
仅用于对象创建。
例如,这样你就可以class Foo
。 (此外,将 *args, **kwargs
传递给 __new__
将允许子 class 具有任意数量的位置或命名参数。)
class Foo(float):
def __new__(cls, value, *args, **kwargs):
return super().__new__(cls, value)
def __init__(self, value, extra):
self.extra = extra
class SubFoo(Foo):
def __init__(self, value, extra, more):
super().__init__(value, extra)
self.more = more
但是,如果您在 __new__
中处理初始化,您将继承 object
的 __init__
,它没有比实例本身更多的参数。而且您将无法通过常规方式对其进行子class。
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