在Python中子类化float类型,__init__()中的异常未被捕获

11 投票
2 回答
2929 浏览
提问于 2025-04-15 17:13

在Python 2.5中,我需要使用浮点数,并且想要修改__str__()这个方法。同时,我还想知道当构造函数出错时该怎么处理。

为什么我不能捕捉到从float.__init__()抛出的异常呢?

我该如何获取我自定义的浮点对象的数值?在我的代码中,我使用了float(self)

class My_Number(float):
    def __init__(self, float_string):
        try:
            super(My_Number, self).__init__(float_string)
        except (TypeError, ValueError):
            raise My_Error(float_string)

    def __str__(self):
        if int(float(self)) == float(self):
            return str(int(float(self)))
        else:
            return str(round(float(self), 2))


>>> n = My_Number('0.54353')
>>> print n
0.54

>>> n = My_Number('5.0')
>>> print n
5

>>> n = My_Number('foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for float(): foo

2 个回答

8

试试用 __new__ 这个方法:

class F(float):
    def __new__(cls, *arg, **kw):
        try:
            return float.__new__(cls, *arg, **kw)
        except ValueError:
            raise Exception("foo")

print F("3.5")            
print F("asdf")

另外,“self”已经是一个浮点数了,所以不需要再写 float(self),直接用“self”就可以了:

def __str__(self):
    return "%.2f" % self
24

float 是不可变的,这意味着它的 __init__ 方法,通常叫做初始化器,基本上是个无操作——在这里不会发生什么实质性的事情,因为 self 对象是不能被改变的(前提是它真的是 float 的实例,而不是子类的实例——当然,float 自己的 __init__ 方法必须基于这个假设来工作;-)。

因此,所有的实际操作都发生在 __new__ 方法中,这个方法才是真正的构造器,就像其他不可变类型,比如 intstrtuple 等等。很多人常常误以为 __init__ 是构造器:其实不是,它接收一个已经构造好的对象作为第一个参数 self,然后对它进行“初始化”(如果可能的话,也就是说,如果这个 self 是可变的!-)——真正的构造过程是在 __new__ 中完成的。

所以,你的 float 子类应该这样开始:

class My_Number(float):
  def __new__(cls, float_string):
    try: return float.__new__(cls, float_string)
    except (TypeError, ValueError): raise My_Error(float_string)

而且你可以去掉 __init__,因为它并不需要。现在:

>>> n = My_Number('foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __new__
NameError: global name 'My_Error' is not defined

(当然,如果你定义了一个 My_Error 异常类,那效果会更好;-)。

撰写回答