动态语言中静态类型的讽刺
当我们看到编程语言如何随着时间的推移而演变时,总是很有趣。
曾几何时,当我开始进入软件开发世界时,python、php 和 javascript 等动态语言因其灵活性和适合快速开发的简洁语法而受到赞赏。
然而,随着这些弱类型语言的发展,它们融合了强类型语言的特性,使它们与 c++ 和 java 非常相似:
- python:自 2015 年版本 3.5 开始引入类型提示功能,并在 2022 年版本 3.12 中得到增强。
- php:2015 年版本 7 中引入的声明类型。
- javascript:通过 2012 年 typescript 的发布进行了扩展,定义为“具有类型语法的 javascript”。
为什么会有这样的转变?
在严格类型语言中,我们在代码中显式定义变量的类型。目标是在执行程序之前捕获开发阶段的错误,并向编译器提供有关分配给这些变量的内存大小的提示。
// c++ example: 'y' will be an integer float x = 3.14; int y = x; // y = 3 (ignored the decimal part of the number)
另一方面,动态类型语言(例如 python、php 和 javascript)允许我们创建变量并让解释器在运行时暗示它们的类型:
# in python and php: 'y' will take the same type as 'x' x = 3.14 y = x // y = 3.14 (float)
动态语言中如何引入显式类型?
在下面的示例中,我们使用动态和静态类型声明相同的函数。
# using the classic syntax: def add(x, y): return x + y # using explicit typing: def add(x: int, y:int) -> int: return x + y
javascript / typescript:
// using the classic syntax function add(x, y) { return x + y; } // using explicit typing function add(x: number, y: number): number { return x + y; }
php:
// using the classic syntax: function add($x, $y) { return $x + $y; } // using explicit typing: function add(int $x, int $y): int { return $x + $y; }
php 8.2(于 2022 年 12 月发布)通过引入对 null、true 和 false 作为独立类型的支持来进一步推动它:
public null $nil = null; public false $false = false;`
讽刺在哪里?
不要将这篇文章视为对这些新功能的反对,我确实承认使用严格类型语言的优势。然而,例如,在 python 中使用类型注释并不能阻止您更改变量的类型:
x: int = 0 x = "john" print(type(x)) # <class></class>
php 也一样,它只会在控制台上打印 deprecated 警告。
有人可能会问为什么解释器允许我们执行这段代码?
这是因为这些语言是这样构建的:它们根据定义是动态类型的。如果我们删除这个特性,它们将不再是动态的;它们将成为像 c++ 一样的严格类型语言,但速度较慢。
希望您可以通过在 php 文件中将 strict_types 设置为 true 来要求解释器更加严格:
declare(strict_types=1);
在 python 中,您可以使用“mypy”包来分析代码并捕获错误:
$ mypy program.py error: incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
您可以看到“mypy”作为顾问,告诉您做错了什么,但这并不能阻止您执行代码,风险由您承担。
即使您不确定变量的类型,您仍然可以使用联合运算符来减少接受类型的列表:
以下来自 php 和 python 的示例展示了如何做到这一点:
y: int | float = f(x) # introduced in python 3.10 int | float $y = f($x) // introduced in php 8.0 let y: number | string // typescript
我们是否牺牲了代码的可读性?
十年前,我决定使用 python 攻读博士学位,因为它简单且能够快速构建新想法原型。然后我也开始将它用于我的其他项目。
现在,我发现自己阅读了一些奇怪的 pep,并质疑自己是否真的值得通过包含这些新功能来使我的代码库复杂化。
让我们看一个打印字典项目的示例函数。这是初始版本:
def print_attributes(**kwargs): for key, value in kwargs.items(): print(key, value) person = {"name": "john", "height": 1.84} print_attributes(**person)
通过使用 python 3.12 中引入的 pep 692 的建议,代码变为:
from typing import TypedDict, Unpack class Person(TypedDict): # create a class inheriting from TypedDict name: str height: float def print_attributes(**kwargs: Unpack[Person]) -> None: # use the Unpack operator for key, value in kwargs.items(): print(key, value) person: Person = {"name": "John", "height": 1.84} # create an instance of the class print_attributes(**person)
总结:我们创建了一个继承自 typeddict 的类,指定了每个项目的名称和类型,并使用 unpack 运算符告诉“mypy”接收到的对象是一个 typeddict。
结果,我们的代码大小增加了一倍。如果我们的对象有更多的项目,它会变得更长。
幸运的是,我们可以对代码的某些部分使用静态类型,而将其余部分保留为动态类型。或者,如果我们愿意,我们可以选择根本不使用它。
我们什么时候应该使用它?
不要因为学到了一个新的、闪亮的功能而感到有重写整个代码库的压力。
这些新功能就像工具。我的建议是明智地使用它们:
在以下场景中使用静态类型:
- 从外部源(例如数据库、库和 api)检索数据时。
- 代码中不允许失败的关键部分。
- 当您的代码库容易出现频繁错误时。
在以下情况下避免使用静态类型:
- 设计原型以快速测试您的想法。
- 实现内部逻辑,其中类型检查只会导致冗长的代码,没有任何好处。
- 仅在屏幕上显示数据(例如绘制图表、图像、数字......)。
- 编写无需用户输入的命令行脚本。
请记住,在编码方面,黄金法则始终是力求简单,除非您有充分的理由使事情复杂化。
以上就是动态语言中静态类型的讽刺的详细内容,更多请关注其它相关文章!