Python的静态类型之旅

简介: Python的静态类型之旅

本文系掘金Python月专题文章,转载请注明来源

江湖有句话:“动态类型一时爽,代码重构火葬场”被广为流传,这话一般出自静态语言拥护者口中,听起来有点耸人听闻,但也没有想象中的那么严重,Python在大型项目的应用太多了,Instagram就是最好的例子。

Python作为动态语言,在定义变量、函数返回值、方法参数都不需要指定数据类型,某种程度上让代码变得无比简洁、灵活,抛开程序运行效率,但动态语言也存在不足,例如:

1、IDE的智能提示比较鸡肋,因为无法判断变量类型,所以IDE就不知道变量有那些属性和方法,没有智能提示对老鸟来说是非常痛苦的,举个简单例子,印象中str有个startwith方法,但正确的写法是 startswith,有个s,我不得不去查个文档。(不过个人建议初学者还是老老实实用编辑器手敲代码,以此加深记忆)

2、编译过程中,只能发现语法错误,类型不匹配的问题只有等程序真正运行了才知道。虽然可以通过单元测试来规避,但是如果在代码编写的过程中有IDE给我们指出来不是更好吗。

3、接口调用全靠文档注释说明,调用某个方法或函数,返回值和参数类型说明只能根据文档来确定。虽然我们可以要求程序员使用docstring或者注释来说明函数的参数类型以及返回值类型,有个问题是即使一开始你规规矩矩的写了docstring,但是代码更新之后,你的docstring可能就没有同步更新。

这些问题在大型项目,特别是多人合作的项目上显得尤为突出。代码规范、Code Review 就变得更重要了。正因为这些问题,社区对静态类型特性的引进呼声越来越强烈,所以在 Python3.5,也就是 PEP484 中有了类型提示(Type Hints)。定义函数时,可以指定函数的返回值类型、参数的类型。

以前写一个函数是这样的:

def greeting(name):
    return "Hello" + name
>>> greeting("bob")
'Hellobob'
>>> greeting(1)
TypeError: must be str, not int

当你不去看文档或者源代码的时候,你根本不知道你可以传递什么类型的值进去,而当你传入整数1时,只有等到程序运行的时候才能发现错误,如果有一种数据类型检查工具在程序启动前事先查一遍就可以避免程序出错。

在Python3.5中,用 Type Hint 的写法是这样的:

def greeting(name: str) -> str:
    return 'Hello ' + name

上面就是静态类型的写法,多了 「: str」与 「-> str」,前者用来说明 name 的类型,后者指函数返回值的类型。这样一来,IDE像PyCharm这样的工具也能即时的发现代码的问题。

当然,除了IDE之外,我们还有更强大的静态类型检查工具,叫 mypy,这个工具也是由Python之父GUido亲自操刀实现的静态类型检查工具。

pip install mypy
$ mypy test.py
test.py:4: error: Argument 1 to "greeting" has incompatible type "int"; expected "str"

有了类型提示,Python在代码调用、重构、甚至是静态分析等方面有了更好的效果,不但减轻了开发时自行进行型态检查的负担,更重要的是,由于有了型态上的提示,让过去Python整合开发工具上做不好的各种智能提示、重构等功能有了统一的参考标准。

某种意义上类型提示只是一种辅助功能,虽然我们加了数据类型提示,但是对于Python解释器来说,它会直接忽略掉类型提示信息,即时类型有误也不会阻止程序的运行。

而对于变量的类型,在PEP484中可以通过类型注释来说明,就是以注释的方式来说明变量的类型,例如:

from typing import List
x = []                # type: List[Employee]
y = [1, 2]            # type: List[int]
y.append("a")

上面类型注释表示x必须是 Employee 对象组成的列表,y必须是 int 构成的列表,整数列表y追加一个字符串后,我们用 mypy 来检查代码有什么问题:

mypy test.py
xx.py:3: error: Name 'Employee' is not defined
xx.py:5: error: Argument 1 to "append" of "list" has incompatible type "str"; expected "int"

在 Python3.6,也就是 PEP526 的提案中,针对变量注解做了进一步优化,将类型的声明作为了语法的一部分,这样比起注释可读性更强,例如:

my_var: int  # 声明为整数类型的变量
my_var = 5  # 通过类型检查
other_var: int = 'a'  # 给整数类型变量赋值字符串,检查器会报错,但是解释器运行是不会有任何问题
print(other_var)
mypy xx.py  # 运行类型检查器
xx.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int")
python test.py # 运行解释器
a

类型提示虽然给了IDE智能提示、重构带来了很大的便利,而恰恰因为这些类型信息的声明,使得动态语言变得臃肿起来,例如:

T = TypeVar('T')
S = TypeVar('S')
class Foo(Generic[T]):
    def method(self, x: T, y: S) -> S:
    # Body

这是一段有泛型的注解,看起来跟Java代码没什么区别了。而讽刺的是,Java也开始加入了动态语言的特性,例如在Java10中,就有本地变量类型推断特性,可以使用关键字 var 来定义变量,而不需要指定数据类型,意味着,静态语言也开始往动态语言特性方面发展。

public class VarTest {
    public static void main(String[] args) {
        var name = "java";
        System.out.println(name);
    }
}

那么到底是静态语言好还是动态语言好,Java和Python各自作为静态语言和动态语言的代表,一个明显的特点就是都在互相借鉴彼此的优点,所谓天下大势,分久必合,合久必分。没有一种语言是完美的,Python灵活但可控性没那么强,更像是一位开放的家长,在语言的处理上给开发者最大的自由。毕竟 We are all consenting adults! 而反观Java,就像一位严苛的家长一样,小心翼翼地对待每个开发者,生怕你闯祸。


目录
相关文章
|
8天前
|
IDE 开发工具 开发者
Python类型注解:提升代码可读性与健壮性
Python类型注解:提升代码可读性与健壮性
169 102
|
5月前
|
索引 Python
Python的变量和简单类型
本文介绍了Python中变量命名规则、常用变量类型及字符串操作。变量命名需遵循字母、数字和下划线组合,不能以数字开头且不可与关键字冲突。字符串支持单引号、双引号或三引号定义,涵盖基本输出、转义字符、索引、拼接等操作。此外,还详细解析了字符串方法如`islower()`、`upper()`、`count()`等,帮助理解字符串处理技巧。
130 15
|
1月前
|
安全 JavaScript Java
Python中None与NoneType的真相:从单例对象到类型系统的深度解析
本文通过10个真实场景,深入解析Python中表示“空值”的None与NoneType。从单例模式、函数返回值,到类型注解、性能优化,全面揭示None在语言设计与实际编程中的核心作用,帮助开发者正确高效地处理“无值”状态,写出更健壮、清晰的Python代码。
166 3
|
1月前
|
缓存 数据可视化 Linux
Python文件/目录比较实战:排除特定类型的实用技巧
本文通过四个实战案例,详解如何使用Python比较目录差异并灵活排除特定文件,涵盖基础比较、大文件处理、跨平台适配与可视化报告生成,助力开发者高效完成目录同步与数据校验任务。
97 0
|
1月前
|
IDE API 开发工具
Python类型注解:让代码“开口说话”的隐形助手
Python类型注解为动态语言增添类型信息,提升代码可读性与健壮性。通过变量、函数参数及返回值的类型标注,配合工具如mypy、IDE智能提示,可提前发现类型错误,降低调试与协作成本。本文详解类型注解的实战技巧、生态支持及最佳实践,助你写出更高质量的Python代码。
118 0
|
4月前
|
Python
Python技术解析:了解数字类型及数据类型转换的方法。
在Python的世界里,数字并不只是简单的数学符号,他们更多的是一种生动有趣的语言,用来表达我们的思维和创意。希望你从这个小小的讲解中学到了有趣的内容,用Python的魔法揭示数字的奥秘。
122 26
|
4月前
|
人工智能 安全 IDE
Python 的类型安全是如何实现的?
本文探讨了 Python 的类型安全实现方式。从 3.5 版本起,Python 引入类型提示(Type Hints),结合静态检查工具(如 mypy)和运行时验证库(如 pydantic),增强类型安全性。类型提示仅用于开发阶段的静态分析,不影响运行时行为,支持渐进式类型化,保留动态语言灵活性。泛型机制进一步提升通用代码的类型安全性。总结而言,Python 的类型系统是动态且可选的,兼顾灵活性与安全性,符合“显式优于隐式”的设计哲学。
|
6月前
|
Rust JavaScript 前端开发
[oeasy]python075_什么是_动态类型_静态类型_强类型_弱类型_编译_运行
本文探讨了编程语言中的动态类型与静态类型、强类型与弱类型的概念。通过实例分析,如Python允许变量类型动态变化(如`age`从整型变为字符串),而C语言一旦声明变量类型则不可更改,体现了动态与静态类型的差异。此外,文章还对比了强类型(如Python,不允许隐式类型转换)和弱类型(如JavaScript,支持自动类型转换)的特点。最后总结指出,Python属于动态类型、强类型语言,对初学者友好但需注意类型混淆,并预告下期内容及提供学习资源链接。
171 21
|
6月前
|
Python Windows
[oeasy]python076_int这个词怎么来的_[词根溯源]整数类型_int_integer_touch
本文探讨了“int”一词的起源及其与整数类型的关联。通过词根溯源,揭示“int”来源于“integer”,意为“完整的数”,与零碎的分数相对。同时分析了相关词汇如“tact”(接触)、“touch”(触摸)及衍生词,如“tangential”(切线的)、“intagible”(无形的)和“integral”(完整的、不可或缺的)。文章还结合编程语言特性,解释了Python作为动态类型、强类型语言的特点,并总结了整型变量的概念与意义。最后预告了后续内容,提供了学习资源链接。
188 11
|
7月前
|
索引 Python
python字符串类型及操作
本文主要讲解字符串类型的表示、操作符、处理函数、处理方法及格式化。内容涵盖字符串的定义、表示方法(单双引号、三引号)、索引与切片、特殊字符转义、常见操作符(如+、*、in等)、处理函数(如len()、str()、chr()等)、处理方法(如.lower()、.split()等)以及格式化方式(如.format())。通过实例代码详细介绍了字符串的各种用法和技巧,帮助读者全面掌握字符串操作。
217 2
python字符串类型及操作

推荐镜像

更多