Python 类型安全吗?

Is Python type safe?

根据Wikipedia

Computer scientists consider a language "type-safe" if it does not allow operations or conversions that violate the rules of the type system.

由于 Python 运行时检查确保类型系统规则得到满足,我们应该考虑 Python 一种类型安全的语言。

Jason Orendorff 和 Jim Blandy 在 Programming Rust 中提出了同样的观点:

Note that being type safe is independent of whether a language checks types at compile time or at run time: C checks at compile time, and is not type safe; Python checks at runtime, and is type safe.

静态类型检查和类型安全都是独立的概念。

对吗?

在 Python 中,如果您在错误的上下文中使用错误类型的变量,将会出现 运行 时间错误。例如:

>>> 'a' + 1

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects

因为这个检查只发生在 运行time,而不是在你 运行 程序之前,Python 不是类型安全的语言(PEP-484 尽管如此)。

维基百科文章将类型安全与内存安全相关联,这意味着无法访问相同的内存区域,例如整数和字符串。这样 Python 是类型安全的。您不能隐式更改对象的类型。

许多程序员会将静态类型检查等同于类型安全:

  • “语言 A 具有 static 类型检查,因此它 类型安全的”
  • “语言 B 具有 动态 类型检查,因此它 不是 类型安全的”

遗憾的是,事情并没有那么简单。

在现实世界中

例如,C 和 C++ 不是类型安全的,因为您可以通过 Type punning 破坏类型系统。 此外,C/C++ 语言规范广泛允许 undefined behaviour (UB) rather than explicitly handling errors and this has become the source of security exploits such as the stack smashing exploit and the format string attack. Such exploits shouldn't be possible in type-safe languages. Early versions of Java had a type bug with its Generics 证明它不是 完全 类型安全。

时至今日,对于 Python、Java、C++ 等编程语言,很难证明这些语言 完全 类型-安全,因为它需要数学证明。这些语言 数量庞大 并且 compilers/interpreters 存在不断被 reported 修复的错误。

[ Wikipedia ] Many languages, on the other hand, are too big for human-generated type safety proofs, as they often require checking thousands of cases. .... certain errors may occur at run-time due to bugs in the implementation, or in linked libraries written in other languages; such errors could render a given implementation type unsafe in certain circumstances.

在学术界

类型安全和类型系统虽然适用于现实世界的编程,但其根源和定义来自 academia – and so a formal definition of what exactly is "type safety" comes with difficulty – especially when talking about real programming languages used in the real world. Academics like to mathematically (formally) define tiny programming languages called toy languages. Only for these languages is it possible to show formally that they are type-safe (and prove they the operations are logically correct)。

[ Wikipedia ] Type safety is usually a requirement for any toy language proposed in academic programming language research

例如,学术界努力证明 Java 是类型安全的,因此他们创建了一个名为 Featherweight Java 的较小版本,并在 Christopher Lyon Anderson 的 paper that it is type-safe. Similarly, this Ph.D. paper 中证明了Java脚本的子集,称为 JS0 并证明它是类型安全的。

实际上假设 python、java、c++ 等适当的语言不是完全类型安全的,因为它们太大了。一个小错误很容易从裂缝中溜走,从而破坏类型系统。

总结

  • python 可能不是 完全类型安全的——没有人证明过,证明太难了。您更有可能在语言中发现一个小错误,证明它不是类型安全的。
  • 事实上,大多数编程语言可能不是完全类型安全的 - 都是出于同样的原因(只有玩具学术语言有被证明是)
  • 您真的不应该相信 静态类型语言 类型 是安全的。它们通常比动态类型语言更安全,但是说它们完全类型安全是错误的 因为没有证据证明这一点。

参考文献:http://www.pl-enthusiast.net/2014/08/05/type-safety/https://en.wikipedia.org/wiki/Type_system

因为还没有人说过,还值得指出 Python 是一种 强类型 语言,不应将其与动态类型混淆。 Python 将类型检查推迟到最后一刻,通常会导致抛出异常。这解释了 Mureinik 提到的行为。话虽如此,Python 也经常进行自动转换。这意味着它将尝试将 int 转换为 float 以进行算术运算,例如。

您可以通过检查输入类型手动在程序中实施类型安全。因为一切都是对象,所以您始终可以创建从基础 类 派生的 类,并使用 isinstance 函数来验证类型(当然是在运行时)。 Python 3 添加了类型提示,但这不是强制执行的。并且 mypy 已经为该语言添加了静态类型检查(如果您愿意使用的话),但这并不能保证类型安全。

通常,大型/复杂系统需要类型检查,首先是编译类型(静态)和 运行 时间(动态)。这不是学术界,而是一个简单的、常识性的经验法则,比如“编译器是你的朋友”。除了 运行 时间性能影响外,还有其他主要影响,如下所示:

可扩展性的 3 个轴是:

  1. 构建时间(在时间和预算内设计和制造安全系统的能力)
  2. 运行时间(显而易见)
  3. 维护时间(以安全的方式维护(修复错误)和扩展现有系统的能力,通常通过重构)

进行安全重构的唯一方法是对所有内容进行全面测试(使用测试驱动开发或至少进行单元测试以及至少体面的覆盖测试,这不是质量检查,这是 development/r&d ).未涵盖的内容会损坏,并且这样的系统比工程工件更垃圾。

现在假设我们有一个简单的函数 sum,return计算两个数字的总和。基于参数和 returned 类型都是已知的这一事实,可以想象对该函数进行单元测试。我们不是在谈论函数模板,它归结为简单的例子。请在名为 sum 的同一函数上编写一个简单的单元测试,其中参数和 return 类型可以是任何类型,它们可以是整数、浮点数、字符串 and/or 任何其他类型的用户定义类型加号运算符 overloaded/implemented。你怎么写这么简单的测试用例?!?测试用例需要多复杂才能覆盖所有可能的场景?

复杂性意味着成本。没有适当的单元测试和测试覆盖率,就没有安全的方法来进行任何重构,所以产品是维护垃圾,不是立即可见的,长期来看是清晰的,因为盲目地进行任何重构就像在没有驾驶执照的情况下驾驶汽车一样, d运行k 就像一只臭鼬,当然,没有保险。

去看看吧! :-)

做梦都想不到。

#!/usr/bin/python

counter = 100          # An integer assignment
miles   = 1000.0       # A floating point
name    = "John"       # A string

print counter
print miles
print name

counter = "Mary had a little lamb"

print counter

当你运行你看到:

python p1.py
100
1000.0
John
Mary had a little lamb

当任何一种语言允许您毫不费力地将变量的内容从整数转换为字符串时,您就不能认为它是“类型安全”的。

在专业软件开发的现实世界中,我们所说的“类型安全”是指编译器会捕获愚蠢的东西。是的,在 C/C++ 中你可以采取特殊措施来规避类型安全。你可以这样声明

union BAD_UNION
{
   long number;
   char str[4];
} data;

但是程序员必须付出额外的努力才能做到这一点。我们不必花费额外的时间来破坏 python.

中的计数器变量

程序员可以通过在 C/C++ 中进行强制转换来做一些令人讨厌的事情,但他们必须故意这样做;不是不小心。

真正让您着迷的地方是 class 铸造。当你声明一个带有基本 class 参数的 function/method 然后传入指向派生 class 的指针时,你并不总能得到你想要的方法和变量,因为 method/function 需要基本类型。如果您在派生的 class 中覆盖了其中的任何一个,则必须在 method/function.

中考虑到它

在现实世界中,“类型安全”语言有助于防止程序员意外地做出愚蠢的事情。它还可以保护人类免于死亡。

考虑使用胰岛素或输液泵。以所需 rate/interval.

将有限数量的生命 saving/prolonging 化学物质泵入人体的东西

现在考虑当存在泵步进器控制逻辑试图将字符串“insulin”解释为要管理的整数量的逻辑路径时会发生什么。结果不会好。这很可能是致命的。

假设你有一个函数 sum,有两个参数 如果参数是未类型化的(可以是任何类型)那么......好吧......这对于在现实生活中的大型系统上工作的任何认真的软件工程师来说都是不可接受的 原因如下:

  1. 天真的回答是“编译器是你的朋友”。尽管已经 65 岁了,但这是真的,嘿,这不仅仅是关于拥有静态类型! ide(s) 将编译器服务用于很多事情,对于普通的 joe 程序员来说,这看起来就像魔术...(代码完成、设计时(编辑)协助等
  2. o 更现实的原因在于一些开发人员完全不知道的东西,这些开发人员没有很强的计算机科学和软件工程背景。有 3 个可扩展性轴:a。 design/write 并部署,b。 运行时间 & c。维护时间,基于重构。你觉得谁最贵?在任何现实生活中的严肃系统上明显重复出现?第三个(c)。为了满足 (c),您需要安全地进行操作。为了进行任何安全的重构,你需要进行单元测试和覆盖测试(这样你就可以估计你的单元测试套件覆盖的覆盖水平)——记住,当某些东西没有被自动测试时,它就会崩溃(在 运行 时间,在周期的后期,在客户现场,随你便) - 所以,为了有一个像样的产品,你需要有像样的单元测试和测试覆盖率

现在,让我们来看看我们的智力挑战函数(求和)。 is sum(a,b) 没有指定a和b的类型,没有办法做像样的单元测试。像 assent sum(1,1) is 2 IS A LIE 这样的测试,因为它除了假定的整数参数之外什么都不包含。在现实生活中,当 a 和 b 是雌雄同体类型时,就没有办法针对函数 sum 编写真正的单元测试!各种框架甚至假装从上述的残废测试用例中得出测试覆盖率结果。那是(显而易见的)另一个谎言。

这就是我要说的!感谢阅读,我发布这篇文章的唯一原因也许是让你想到这一点,也许(也许......)有一天可以做软件工程......

我们刚刚在一段代码中出现了一个大错误。错误是因为我们有这个:

   if sys.errno:
        my_favorite_files.append(sys.errno)

而不是这个:

    if args.errno:
        my_favorite_files.append(sys.errno)

积极地将任何东西转换为布尔值,因为它使 if 语句更容易,这是我不希望在类型安全的语言中找到的东西。