Python类型安全吗?

时间:2017-09-24 08:57:37

标签: python types type-safety dynamic-typing memory-safety

根据Wikipedia

  

如果计算机科学家不允许操作或转换违反类型系统的规则,则会将其视为“类型安全”。

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

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

  

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

静态类型检查和类型安全的单独概念。

这是对的吗?

8 个答案:

答案 0 :(得分:34)

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

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

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

在真实世界

例如,C和C ++不是类型安全的,因为您可以通过Type punning来破坏类型系统。 此外,C / C ++语言规范广泛允许undefined behaviour (UB)而不是显式处理错误,这已成为安全漏洞的来源,例如stack smashing漏洞利用和format string attack。在类型安全的语言中不应该有这样的漏洞利用。早期版本的Java有一个类型错误,其Generics证明它不是完全类型安全的。

直到今天,对于Python,Java,C ++等编程语言,很难证明这些语言完全类型安全,因为它需要数学证明。这些语言大量,编译器/解释器的错误一直在reported并得到修复。

  

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

在学术界

类型安全和类型系统,虽然适用于实际编程的根源和定义来自academia - 因此正式定义 “类型安全”的来源困难 - 尤其是在谈论现实世界中使用的真实编程语言时。学术界喜欢在数学上(正式)定义名为toy languages的微小编程语言。只有这些语言才有可能正式显示它们是类型安全的(并证明它们的操作是逻辑correct)。

  

[Wikipedia]类型安全通常是学术编程语言研究中提出的toy language的必要条件

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

实际上,假设python,java,c ++等正确的语言不是完全类型安全的,因为它们太大了。一个小虫子很容易穿过会破坏类型系统的裂缝。

摘要

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

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

答案 1 :(得分:9)

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

答案 2 :(得分:7)

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

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

答案 3 :(得分:3)

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

>>> 'a' + 1

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

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

答案 4 :(得分:1)

不在你最疯狂的梦里。

#!/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 ++进行强制转换,但必须刻意做到;不是偶然的。

真正会烧死你的地方是班级演员。当您声明带有基类参数的函数/方法,然后将指针传递给派生类时,由于方法/函数需要基类型,因此不一定总能得到所需的方法和变量。如果您覆盖派生类中的任何一个,则必须在方法/函数中加以考虑。

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

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

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

答案 5 :(得分:0)

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

可扩展性的三个轴是:

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

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

现在让我们说我们有一个简单的函数sum,返回两个数字的和。可以想象基于参数和返回类型都是已知的事实,对此功能进行单元测试。我们不是在谈论功能模板,它可以归结为简单的例子。请在称为sum的同一函数上编写一个简单的单元测试,其中参数和返回类型在字面上都可以是任何类型,它们可以是整数,浮点数,字符串和/或任何其他类型的用户定义类型,且加号运算符已重载/实现。您如何编写这样一个简单的测试用例?!?为了涵盖所有可能的情况,测试用例需要多么复杂?

复杂度意味着成本。没有适当的单元测试和测试覆盖范围,就没有安全的方法可以进行任何重构,因此该产品是维护垃圾,长期来看不会立即可见,因为在盲目环境中进行任何重构都就像在没有驾驶执照的情况下驾驶汽车一样。 ,就像like子一样醉,当然没有保险。

去图! :-)

答案 6 :(得分:0)

假设您有一个函数求和,带有两个参数 如果参数是无类型的(可以做任何事情),那么...好吧...对于在现实生活中的大型系统上工作的任何认真的软件工程师来说,这都是不可接受的 这是为什么:

  1. 天真的答案是“编译器是您的朋友”。尽管已经65岁了,但这是真的,嘿,这不仅与拥有静态类型有关! ide会使用编译器服务做很多事情,对于一般的joe程序员来说,这看起来就像魔术...(代码完成,设计时间(编辑)帮助等)
  2. 更为现实的原因在于,对于那些没有计算机科学及软件工程专业背景的开发人员来说,这完全是未知的。有3个可扩展性轴:设计/编写和部署; b。运行时&c。保持时间,基于重构。您认为谁是最昂贵的?清楚地出现在任何现实生活中的严肃系统上吗?第三个(c)。为了满足(c),您需要安全进行此操作。为了进行任何安全的重构,您需要进行单元测试和覆盖测试(以便您可以估算单元测试套件所覆盖的覆盖级别)-请记住,如果某些内容未经过自动测试,则会损坏(在运行时,在周期的后期,您可以在客户现场将其命名)-因此,要拥有一个不错的产品,您需要进行不错的单元测试和测试覆盖范围

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

这就是我要说的!感谢您的阅读,我发布此文章的唯一原因可能是,让您考虑到这一点,并且也许(MAYBE ..)有一天进行软件工程...

答案 7 :(得分:0)

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

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

而不是这个:

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

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