关于动态类型:在python中强制/转换为正确类型的最佳位置

Best place to coerce/convert to the right type in Python

我对python还比较陌生,我正在尝试适应它的动态类型。有时我有一个函数或类,它需要某个特定类型的参数,但可以得到另一个可强制使用的类型的值。例如,它可能期望得到一个float,但是收到一个int或一个十进制。或者它可能需要一个字符串,但接收一个定义__str__特殊方法的对象。

将参数强制为正确类型(及其原因)的最佳实践是什么?我是在函数/类中还是在调用方中执行此操作?如果在调用者中,是否还要在函数中检查它?如。

替代方案1:

1
2
3
4
def myfunc(takes_float):
    myval = float(takes_float)

myfunc(5)

替代方案2:

1
2
3
4
def myfunc(takes_float):
    myval = takes_float

myfunc(float(5))

替代方案3:

1
2
3
4
5
def myfunc(takes_float):
    assert isinstance(takes_float, float)
    myval = takes_float

myfunc(float(5))

我已经读过这个答案和这个答案,他们说在python中检查类型是"不好的",但是我不想浪费时间跟踪非常简单的错误,这些错误会立即被编译器以静态类型语言发现。


你"强迫"(也许--这可能是一个中午)当它是你必须这样做,而不是更早。例如,假设有一个函数接受一个浮点值并返回其正弦和余弦的和:

1
2
3
import math
def spc(x):
  math.sin(x) + math.cos(x)

你应该"强迫"X浮在哪里?答:根本就没有地方——罪和罪是为你做的,例如:

1
2
>>> spc(decimal.Decimal('1.9'))
0.62301052082391117

那么,何时必须强制(尽可能晚)呢?例如,如果要对参数调用String方法,则必须确保它是一个字符串--尝试对非字符串调用例如.lower将不起作用,len可能起作用,但如果参数是一个列表(给出列表中的项目数,而不是它所代表的字符数),则可以执行与预期不同的操作离子会像一根绳子一样被占据),等等。

至于捕获错误——想想单元测试——半正派的单元测试将捕获静态类型会捕获的所有错误,然后是一些错误。但是,这是另一个主题。


这要看情况而定。为什么你需要一个float?一个int会破坏函数吗?如果是,为什么?

如果需要参数来支持float具有但int没有的函数/属性,则应检查该函数/属性,而不是参数恰好是float。检查对象是否可以做您需要它做的事情,而不是它恰好是您熟悉的特定类型。

谁知道呢,也许有人会发现Python实现float的一些主要问题,并创建一个notbrokenfloat库。它可能支持float在修复某些异常bug时所做的一切,但它的对象不属于float类型。手动将其转换为float可能会删除这个漂亮的新类的所有好处(或者可能会彻底中断)。

是的,这是一个不太可能的例子,但我认为这是在使用动态类型语言时要进入的正确心态。


只有一次整型和浮点型会出现问题。这是唯一一次你会发现一个"简单"的错误,它很奇怪,并且是调试的挑战。

除法。

当你需要的时候,其他的一切都会完成你需要的转换。

如果您使用的是python 2.x,并且在不经意间随意地使用/操作符,那么在某些常见情况下,您可能会出错。

你有几个选择。

  • from __future__ import division将为您提供用于划分的python 3语义。

  • 始终使用-Qnew选项运行,以获得新的除法语义。

  • /操作附近使用float

  • 划分是唯一能影响类型的地方。这是唯一一次整数的行为与浮点数的行为不同,而浮点数的方式会无声地影响结果。

    所有其他类型的不匹配问题都将以TypeError异常的形式失败。所有其他。您不会浪费时间调试。你马上就会知道怎么了。

    更具体一点。

    没有调试"Expect a string but no t get a string"。这将立即崩溃,并具有追溯功能。没有混乱。没有时间浪费思考。如果函数需要一个字符串,那么调用方必须提供该字符串——这就是规则。

    上面的备选方案2很少用于解决这样一个问题:您有一个函数需要一个字符串,而您感到困惑,忘记提供一个字符串。这种错误很少发生,并且会导致立即类型异常。