我来自Java的世界,想知道除了编译代码时错过错误之外,Python中的动态类型有什么优点?
你喜欢Python的类型定义吗?你有大型项目中类型定义带来帮助的例子吗?它不会有一些容易出错的问题吗?
我来自Java的世界,想知道除了编译代码时错过错误之外,Python中的动态类型有什么优点?
你喜欢Python的类型定义吗?你有大型项目中类型定义带来帮助的例子吗?它不会有一些容易出错的问题吗?
通常情况下,静态类型检查是不可判定的。这意味着有些程序是静态类型安全的,但类型检查器无法“证明”它们是静态类型安全的,因此类型检查器必须拒绝这些程序。
换句话说:存在类型安全的程序,但类型检查器不允许你编写它们。更简洁地说:静态类型检查防止你编写某些程序。
这适用于所有静态类型,而不仅仅是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版本不同,这个版本是完全静态类型安全的,但没有任何与类型相关的杂乱代码。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 vs BASIC或Python vs Haskell。
var
和 auto
关键字来显式告诉它们使用隐式类型。 - Jörg W Mittag我怀疑绝大多数非平凡的Java程序都包含动态类型。每次在Java中将Object类型转换为显式类型时,都会进行动态类型检查,这包括在1.5之前使用集合类的每个用法。实际上,即使是Java泛型仍然可以推迟某些类型检查直到运行时。
每次使用Java反射时,都会进行动态类型检查。这包括从文本文件中的类或方法名称映射到实际类或方法,例如每次使用Spring XML配置文件。
这是否使Java程序变得脆弱且容易出错?Java程序员是否花费大量时间跟踪和修复不正确的动态类型?可能不会,Python程序员也是如此。
动态类型的一些优点:
你喜欢用Python吗?
Python的一部分。在Python中喜欢它是愚蠢的。
你有一个大型项目中它帮助的例子吗?
有。每一天我都为此感到高兴,因为由于鸭子类型,我可以进行更改,并且这些更改相对局部化,通过所有单元测试、集成测试,并且没有影响其他地方。
如果使用Java,这些更改将需要无休止地重构以从类中提取接口,以便我可以引入仍然符合Java静态类型检查下允许的变化。
这不会有点容易出错吗?
不比静态类型检查更容易出错。简单的单元测试可以确认对象符合预期的特征。
在Java中很容易编写一个(a)通过编译时检查并(b)在运行时崩溃的类。强制转换是实现此目的的好方法。未满足类的意图是常见的事情,即类可能编译但仍然不能正常工作。
__new__
。 - Score_Under这是一种减轻负担的方式。你可以将颜色红色视为“Red”(一个常量),或者是“255, 0, 0”(一个元组),或者是“#FF0000”(一个字符串):三种不同的格式,需要三种不同的类型,或者在Java中进行复杂的查找和转换方法。
这使得代码更简单。