如何根据所需的精度将数字解析为int或float?

时间:2016-03-19 23:38:24

标签: python python-3.x casting

要求:

  1. 输入可以是字符串或数字
  2. 如果输入可以被视为int而不会损失精度,则转换为int
  3. 如果输入可以被视为浮动,则转换为浮动
  4. 以下是我使用此代码的部分代码。

    def make_operand(symbol, left=None, right=None):
        valid_symbols = ['*', '/', '+', '-']
        if symbol in valid_symbols:
            return Operand(symbol, left, right)
    
        as_int = re.compile("^-?[0-9]+$").match(str(symbol))
        as_float = re.compile("^[-+]?[0-9]*\.?[0-9]+$").match(str(symbol))
    
        as_number = int(symbol) if as_int else float(symbol) if as_float else None
    
        if as_number:
            return NumericOperand(as_number)
    
        raise ValueError("Invalid symbol or number")
    

    这样可行,但看起来很乱,闻起来有点不对劲。

    使用try块的实现也有效,但似乎不那么简单:

        as_number = None
        try:
            as_float = float(symbol)
        except ValueError:
            as_float = None
    
        if as_float:
            as_int = int(as_float)
            as_number = as_int if as_int == as_float else as_float
    
        if as_number:
            return NumericOperand(as_number)
    
        raise ValueError("Invalid symbol or number")
    

    是否有更好的方法,或者其中一种接近Pythonic的做法?

2 个答案:

答案 0 :(得分:2)

如果您不介意第三方模块,那么有一个名为fastnumbers的C-exension模块就是为此目的而设计的。 fast_real函数完全符合您的要求(假设您使用coerce=True上提供的fastnumbers>=0.7.4)。

完全披露,我是作者。

>>> from fastnumbers import fast_real
>>> fast_real('56')
56
>>> fast_real('56.0')
56
>>> fast_real('56.07')
56.07
>>> fast_real('56.07 lb')
'56.07 lb'
>>> fast_real(56.07)
56.07
>>> fast_real(56.0)
56.0
>>> fast_real(56.0, coerce=True)
56
>>> fast_real(56)
56
>>> fast_real('56.07 lb', raise_on_invalid=True) 
Traceback (most recent call last):
  ...
ValueError: could not convert string to float: '56.07 lb'

答案 1 :(得分:0)

因为PEP 367任何可以转换为整数而没有精度损失的python对象将定义__index__方法,以便可以在索引中使用它。唯一的特例是使用str,您可以这样做:

def as_num(value):
    if isinstance(value,str):
        try:
            return int(value)
        except ValueError:
            pass #don't put return float(value) here or you will get chained exceptions
        return float(value) #this might fail and raise the error
    elif hasattr(value,"__index__"):
        return value.__index__() #possibly just int(value) if the method exists?
    else:
        return float(value) #this would fail if the object cannot be cast to a float

虽然有可能在未定义__index__intfloat返回等效值的特殊情况下仍然无法接受,在这种情况下,您已拥有的代码是几乎没事。只需将if as_float更改为if as_float is not None,这样symbol=0就不会失败而且你很高兴,虽然结构可以简化一点:

try:
    as_float = float(symbol)
except ValueError:
    raise ValueError("Invalid symbol or number") #maybe just let the other error go instead?
else:
    if as_float.is_integer():
        return NumericOperand(int(as_float))
    return NumericOperand(as_float)