11

我来自 Java 世界,我想知道除了在编译代码时丢失错误之外,Python 中的动态类型还有什么好处?

你喜欢 Python 的打字吗?你有一个例子,它在一个大项目中有所帮助吗?是不是有点容易出错?

4

7 回答 7

17

在一般情况下,静态类型检查是不可判定的。这意味着有些程序是静态类型安全的,但类型检查器无法证明它们是静态类型安全的,因此类型检查器必须拒绝这些程序。

换句话说:有类型检查器不允许您编写的类型安全程序。或者,更简洁地说:静态类型会阻止您编写某些程序。

这通常适用于所有静态类型,而不仅仅是 Java。

具体到Java:它有一个相当糟糕的类型系统。它的类型系统的表达力不足以表达甚至非常简单的属性。例如:static void java.util.Arrays.sort(Object[] a)它实际上在哪里说结果必须是,你知道的,排序的?或者数组元素必须部分排序?

Java 的另一个问题是它的类型系统有这么大的洞,以至于你可以开一辆卡车:

String[] a = new String[1];
Object[] b = a;
b[0] = 1; // ArrayStoreException

这种特殊情况下的问题是协变数组。数组不可能既是协变的又是类型安全的。

Java 结合了静态类型的所有麻烦而没有任何优点。所以,你也可以摆脱麻烦。

但是,请注意,这不是通用的。还有其他语言具有更好的类型系统,但其权衡却不太清楚。

例如,这是 Python 中有史以来最愚蠢的语言基准(斐波那契):

def fib(n):
    if n < 2: return n
    return fib(n-2) + fib(n-1)

和Java:

int fib(int n) {
    if (n < 2) return n;
    return fib(n-2) + fib(n-1);
}

请注意,那里有相当多的混乱,这仅与静态类型有关。为了使比较更公平,让我们想象一种具有 Python 语法和 Java 语义的语言:

def fib(n: int) -> int:
    if n < 2: return n
    return fib(n-2) + fib(n-1)

[有趣的旁注:在 Python 3.x 中添加了可选的静态类型注释,这实际上也是有效的 Python 代码,尽管它显然仍然不是静态类型安全的,因为注释就是:注释。它们从未在任何地方实际检查过。]

那里有一些明确的混乱。但是,在 Haskell 中,它看起来像这样:

fib n
  |     n < 2 = n
  | otherwise = fib (n-2) + fib (n-1)

与 Python 版本不同,这完全静态类型安全的,但与类型相关的混乱为零。

在这种特殊情况下,静态类型和动态类型的好处之间的问题就不太清楚了。

顺便说一句,更惯用的 Haskell 版本可能如下所示:

fib 0 = 0
fib 1 = 1
fib n = fib (n-2) + fib (n-1)

或这个:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

实际上,Java 和 Python 之间更重要的区别并不在于 Java 是静态类型的,而 Python 是动态类型的,而是 Java 不是一种好的编程语言,而 Python. 所以,Java 总是会失败,不是因为它是静态类型的,而是因为它是垃圾。将 BASIC 与 Haskell 进行比较,Haskell 显然胜出,但同样,不是因为它是静态类型的,而是因为 BASIC 是垃圾。

更有趣的比较是 Java 与 BASIC 或 Python 与 Haskell。

于 2010-09-01T20:16:15.333 回答
13

我怀疑绝大多数重要的 Java 程序都有动态类型。

每次在 Java 中从 Object 转换为显式类型时,您都在进行动态类型检查 - 这包括在 1.5 中引入泛型之前对集合类的每次使用。实际上,Java 泛型仍然可以将一些类型检查推迟到运行时。

每次使用 Java 反射时,您都在进行动态类型检查。这包括从文本文件中的类或方法名到真实类或方法的映射——例如每次使用 Spring XML 配置文件时。

这是否会使 Java 程序变得脆弱且容易出错?Java 程序员是否会花费大量时间来跟踪和修复错误动态类型的问题?可能不会——Python 程序员也不会。

动态类型的一些优点:

  • 大大减少了对继承的依赖。我见过具有大量继承树的 Java 程序。Python 程序经常使用很少或不使用继承,更喜欢使用鸭子类型。
  • 编写真正通用的代码很容易。例如,min() 和 max() 函数可以采用任何可比较类型的序列 - 整数、字符串、浮点数、具有适当比较方法的类、列表、元组等。
  • 更少的代码。很大一部分 Java 代码对解决手头的问题没有任何帮助——它纯粹是为了让类型系统满意。如果 Python 程序的大小是等效 Java 程序的五分之一,那么编写、维护、阅读和理解的代码就只有五分之一。换句话说,Python 的信噪比要高得多。
  • 更快的开发周期。这与更少的代码齐头并进——你花更少的时间思考类型和类,而更多的时间思考解决你正在处理的问题。
  • 几乎不需要 AOP。我认为 Python 有面向方面的库,但我不知道有谁使用它们,因为 99% 的你需要 AOP 的东西都可以用装饰器和动态对象修改来完成。AspectJ 在 Java 世界中的广泛使用向我表明,核心 Java 语言中存在必须使用外部工具来弥补的缺陷。
于 2010-09-01T20:26:07.717 回答
5

你喜欢 Python 吗?

它是 Python 的一部分Python 中 喜欢它是愚蠢的。

你有一个例子,它在一个大项目中有所帮助吗?

是的。每一天我都为我可以做出改变而高兴——因为 Duck 类型——它们被合理地本地化,通过了所有的单元测试,通过了所有的集成测试,并且在其他地方没有任何中断。

如果这是 Java,那么这些更改将需要无休止的重构以将接口从类中提取出来,以便我可以引入在 Java 的静态类型检查下仍然允许的变体。

是不是有点容易出错?

只不过是静态类型。一个简单的单元测试确认对象符合预期的特征。

It's easy to write a class in Java that (a) passes compile-time checks and (b) crashes horribly at run time. Casts are a good way to do this. Failing to meet the classes intent is a common thing -- a class may compile but still not work.

于 2010-09-01T20:03:10.297 回答
2

许多模式(例如来自 GoF)是不必要的,或者可以在具有函数风格的动态类型语言中以较少的努力实现。事实上,很多模式都是“内置”到 python 中的,所以如果你编写简短的“pythonic”代码,你将免费获得所有好处。您不需要 Iterator、Observer、Strategy、Factory Method、Abstract Factory 和一堆 Java 或 C++ 中常见的其他模式。

这意味着要编写的代码更少,而且(更重要的是)需要阅读、理解和支持的代码更少。我认为这是像 python 这样的语言的主要好处。在我看来,这大大超过了静态类型的缺失。与类型相关的错误在 python 代码中并不常见,它们很容易通过简单的功能测试来捕获(而且这样的测试在 python 中肯定比在 java 中更容易编写)。

于 2010-09-01T20:33:17.767 回答
0

这是你头脑中的一个负担。您可以将红色视为“Red”(常量)或“255, 0, 0”(元组)或“#FF0000”(字符串):三种不同的格式,需要三种不同的类型,或复杂的查找和Java中的转换方法。

它使代码更简单。

于 2010-09-01T19:16:20.267 回答
0

例如,您可以编写可以向其传递整数以及字符串、列表或字典或其他任何内容的函数,并且它将能够以适当的方式透明地处理所有这些(或者如果它抛出异常无法处理类型)。你也可以用其他语言来做类似的事情,但通常你不得不求助于(ab)使用指针、引用或类型转换之类的东西,这会为编程错误打开漏洞,而且很丑陋。

于 2010-09-01T19:19:29.810 回答
0

由于您来自 Java 世界,显而易见的答案是,最好不要被迫编写所有您被迫编写的东西,只是为了让 Java 的类型系统满意。

当然,还有其他静态类型检查语言不会强迫您编写 Java 强迫您编写的所有内容。

甚至 C# 也会对局部方法变量进行类型推断!

还有其他静态类型检查语言提供的编译时错误检查比 Java 提供的更多。

(不太明显的答案 - Python 中的动态类型有什么好处? - 可能需要更多地了解 Python 才能理解。)

于 2010-09-01T19:24:57.160 回答