44

根据维基百科

如果计算机科学家不允许违反类型系统规则的操作或转换,则认为该语言是“类型安全的”。

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

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

请注意,类型安全与语言是在编译时检查类型还是在运行时检查类型无关:C 在编译时检查,并且不是类型安全的;Python 在运行时检查,并且是类型安全的。

静态类型检查和类型安全的概念都是分开的。

那是对的吗?

4

8 回答 8

62

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

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

可悲的是,事情并没有那么简单。

在现实世界

例如,C 和 C++ 不是类型安全的,因为您可以通过Type punning破坏类型系统。此外,C/C++ 语言规范广泛允许未定义行为 (UB),而不是显式处理错误,这已成为安全漏洞的来源,例如堆栈粉碎漏洞和格式字符串攻击。在类型安全的语言中,这样的利用不应该是可能的。Java 的早期版本在其泛型中存在类型错误,这证明它不是完全类型安全的。

直到今天,对于 Python、Java、C++ 等编程语言……很难证明这些语言是完全类型安全的,因为它需要数学证明。这些语言非常庞大,编译器/解释器的错误不断被报告和修复。

[维基百科]另一方面,许多语言对于人工生成的类型安全证明来说太大了,因为它们通常需要检查数千个案例。.... 由于实现中的错误或以其他语言编写的链接库中的错误,可能会在运行时发生某些错误;在某些情况下,此类错误可能会使给定的实现类型不安全。

在学术界

类型安全和类型系统虽然适用于现实世界的编程,但它们的根源和定义来自学术界——因此很难对究竟什么“类型安全”进行正式定义——尤其是在谈论现实中使用的真实编程语言时世界。学术界喜欢在数学上(正式)定义称为玩具语言的微型编程语言。只有对于这些语言,才有可能正式证明它们是类型安全的(并证明它们的操作在逻辑上是正确的)。

[维基百科]类型安全通常是学术编程语言研究中提出的任何玩具语言的要求

例如,学者们努力证明 Java 是类型安全的,因此他们创建了一个名为 Featherweight Java 的较小版本,并在一篇论文中证明了它类型安全的。同样,这个博士。Christopher Lyon Anderson 的论文采用了 Javascript 的一个子集,将其称为 JS0,并证明它是类型安全的。

实际上假设像 python、java、c++ 这样的正确语言不是完全类型安全的,因为它们太大了。一个小虫子很容易从破坏类型系统的裂缝中溜走。

概括

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

参考资料:http ://www.pl-therapy.net/2014/08/05/type-safety/ 和https://en.wikipedia.org/wiki/Type_system

于 2017-09-24T10:19:23.773 回答
17

不在你最疯狂的梦想中。

#!/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++ 中使用强制转换做一些讨厌的事情,但他们必须故意这样做;不是偶然的。

一个真正让你着迷的地方是班级选角。当您使用基类参数声明函数/方法然后将指针传递给派生类时,您并不总是获得所需的方法和变量,因为方法/函数需要基类型。如果您在派生类中覆盖任何内容,则必须在方法/函数中考虑它。

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

考虑使用胰岛素或输液泵。以所需的速率/间隔将有限量的挽救生命/延长生命的化学物质泵入人体的东西。

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

于 2020-11-03T12:07:54.553 回答
9

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

于 2017-09-24T09:06:06.157 回答
9

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

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

于 2017-10-03T16:16:56.193 回答
6

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

>>> 'a' + 1

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

由于此检查仅在运行时发生,而不是在运行程序之前发生,因此 Python 不是类型安全语言(尽管有PEP-484 )。

于 2017-09-24T09:01:27.333 回答
0

假设你有一个函数 sum,如果参数是非类型化的(可以是任何东西),则取两个参数然后......嗯......这对于任何在现实生活中的大型系统上工作的严肃软件工程师来说都是不可接受的,原因如下:

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

现在,让我们来看看我们的智力挑战函数(sum)。是 sum(a,b) 没有指定 a 和 b 的类型,没有办法进行像样的单元测试。像 assent sum(1,1) 之类的测试是 2 IS A LIE,因为它除了假设的整数参数之外不涵盖任何内容。在现实生活中,当 a 和 b 是雌雄同体类型时,就没有办法针对函数 sum 编写真正的单元测试!各种框架甚至假装从上面描述的残缺的测试用例中得出测试覆盖率结果。那是(显然)另一个谎言。

这就是我要说的!感谢您的阅读,我发布此内容的唯一原因也许是为了让您想到这一点,也许(也许......)有一天做软件工程......

于 2020-11-27T03:13:08.740 回答
0

我们只是在一段代码中有一个大错误。错误是因为我们有这个:

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

而不是这个:

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

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

于 2020-12-30T20:08:12.903 回答
0

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

可扩展性的 3 个轴是:

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

进行安全重构的唯一方法是对所有内容进行全面测试(使用测试驱动开发或至少使用单元测试以及至少体面的覆盖测试,这不是质量保证,这是开发/研发)。未涵盖的内容将会损坏,这样的系统更像是垃圾而不是工程工件。

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

复杂性意味着成本。没有适当的单元测试和测试覆盖,就没有安全的方法来进行任何重构,所以产品是维护垃圾,不是立即可见的,从长远来看是清楚的,因为盲目执行任何重构就像驾驶没有驾驶执照的汽车,像臭鼬一样喝醉了,当然,没有保险。

去搞清楚!:-)

于 2020-07-30T23:50:48.597 回答