Python中的类型安全

Python中的类型安全,python,type-safety,Python,Type Safety,我定义了一个Vector类,它有三个属性变量:x、y和z。坐标必须是实数,但没有什么可以阻止一个人执行以下操作: >>> v = Vector(8, 7.3, -1) >>> v.x = "foo" >>> v.x "foo" 我可以实现如下“类型安全”: import numbers class Vector: def __init__(self, x, y, z): self.setposition(x, y,

我定义了一个
Vector
类,它有三个属性变量:
x
y
z
。坐标必须是实数,但没有什么可以阻止一个人执行以下操作:

>>> v = Vector(8, 7.3, -1)
>>> v.x = "foo"
>>> v.x
"foo"
我可以实现如下“类型安全”:

import numbers

class Vector:
    def __init__(self, x, y, z):
        self.setposition(x, y, z)

    def setposition(self, x, y, z):
        for i in (x, y, z):
            if not isinstance(i, numbers.Real):
                raise TypeError("Real coordinates only")

        self.__x = x
        self.__y = y
        self.__z = z

    @property
    def x(self):
        return self.__x

    @property
    def y(self):
        return self.__y

    @property
    def z(self):
        return self.__z
…但这似乎不是蟒蛇式的

建议

但没有什么可以阻止一个人做以下事情:

>>> v = Vector(8, 7.3, -1)
>>> v.x = "foo"
>>> v.x
"foo"
我认为试图阻止某人做那样的事是不可取的。如果必须这样做,那么在我看来,您应该在可能使用
Vector
执行的任何操作期间检查类型安全性

引用G.V.R:

我们都是成年人

毕竟。有关更多信息,请参阅此项及其答案


我相信这里经验丰富的蟒蛇师会给你更好的答案。

你必须问问自己为什么要测试设置这些值的类型。在任何计算中,如果碰巧遇到错误的值类型,只需提出一个
TypeError
。奖励:标准操作已经做到了这一点

>>> 3.0 / 'abc'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: unsupported operand type(s) for /: 'float' and 'str'
>3.0/'abc'
回溯(最近一次呼叫最后一次):
文件“”,第1行,是否在中?
TypeError:/:“float”和“str”的操作数类型不受支持
是Python中常用的方法。它应该适用于任何行为类似于数字,但不一定是实数的


在大多数情况下,在Python中不应该显式地检查类型。您获得了灵活性,因为您的代码可以与自定义数据类型一起使用,只要它们的行为正确。

其他答案已经指出,在这里检查类型没有多大意义。此外,如果您的类是用纯Python编写的,那么它的速度不会很快

如果您想要一个更具Python风格的解决方案,您可以使用如下属性设置器:

@x.setter
def x(self, value):
    assert isinstance(value, numbers.Real)
    self.__x = value
禁用调试或启用优化模式时,assert语句将被删除

或者,您可以在setter中将
强制为浮点值。如果类型/值不可转换,则会引发异常:

@x.setter
def x(self, value):
    self.__x = float(value)

您不应该以这种方式提供类型安全性。是的,有人可以故意破坏您的代码,提供您的容器无法使用的值,但其他语言也是如此。即使有人在方法或成员函数中输入了正确的参数值,也不一定意味着它没有被破坏:如果程序需要一个IP地址,但您传递了一个主机名,那么它仍然无法工作,尽管两者都可能是字符串

我想说的是:Python的思维方式本质上是不同的。Duck类型基本上说:嘿,我不局限于某些类型,而是对象的接口或行为。如果一个物体的行为真的像我所期望的那样,我不在乎——去追求它吧

如果您试图引入类型检查,那么您基本上限制了该语言最有用的功能之一

也就是说,您确实需要进入测试驱动开发,或者至少是单元测试。没有理由不使用动态语言——这只是将检测(类型)错误的方式转移到构建过程中的另一个步骤,从编译时转移到每天多次运行测试套件。虽然这看起来像是额外的努力,但实际上它将减少调试和修复代码所花费的时间,因为它本质上是一种更强大的方法来检测代码中的错误


但够了,我已经在胡扯了。

但为什么?整数完全起作用。我知道这类问题通常出现在大型项目/团队中。如果你对类型安全有强烈的感觉,那么我建议你看看scala。虽然“接口”在这里有点用词不当,因为从来没有明确定义过接口(希望在注释中除外)。Duck类型意味着您必须实际查看或运行代码,以查看“接口”是什么。在我有限的Python经验中,这是duck类型的最大缺点。编写自己的代码速度更快,但使用其他人未记录的API(例如编写插件)要困难得多,因此注释变得更为重要。单元测试很好,但在调试器中单步测试并不是了解每个方法希望传递哪种类型(即如何正确使用API)的最佳方法。而且很难保证你的单元测试总是覆盖每一种边缘情况,然后编写一套全面的单元测试,测试你代码的所有分支,以便在用户之前捕获你的类型错误。对吗?无论如何,关于何时应该抛出错误:如果您知道某个组合是无效的,并且会导致问题,那么尽早失败不是更好/更清楚吗?早期失败不就更容易找到根本问题吗?(我想“为什么它无效”还不太清楚。)我认为问题是关于LBYL和EAFP的,所以从“pythonic”的角度来看,不仅“允许”您最初想要的类型是有意义的。对于一个“已知的坏”结果,切断其执行路径(raise)是非常有意义的。