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,就像一位严苛的家长一样,小心翼翼地对待每个开发者,生怕你闯祸。


目录
相关文章
|
2月前
|
Python
以下是一些常用的图表类型及其Python代码示例,使用Matplotlib和Seaborn库。
以下是一些常用的图表类型及其Python代码示例,使用Matplotlib和Seaborn库。
|
1月前
|
存储 索引 Python
Python散列类型(1)
【10月更文挑战第9天】
|
1月前
|
计算机视觉 Python
Python实用记录(一):如何将不同类型视频按关键帧提取并保存图片,实现图片裁剪功能
这篇文章介绍了如何使用Python和OpenCV库从不同格式的视频文件中按关键帧提取图片,并展示了图片裁剪的方法。
72 0
|
1月前
|
存储 数据安全/隐私保护 索引
Python 散列类型三以及函数基础
【10月更文挑战第11天】
Python 散列类型三以及函数基础
|
1月前
|
Python
【10月更文挑战第6天】「Mac上学Python 11」基础篇5 - 字符串类型详解
本篇将详细介绍Python中的字符串类型及其常见操作,包括字符串的定义、转义字符的使用、字符串的连接与格式化、字符串的重复和切片、不可变性、编码与解码以及常用内置方法等。通过本篇学习,用户将掌握字符串的操作技巧,并能灵活处理文本数据。
54 1
【10月更文挑战第6天】「Mac上学Python 11」基础篇5 - 字符串类型详解
|
1月前
|
Python
【10月更文挑战第6天】「Mac上学Python 10」基础篇4 - 布尔类型详解
本篇将详细介绍Python中的布尔类型及其应用,包括布尔值、逻辑运算、关系运算符以及零值的概念。布尔类型是Python中的一种基本数据类型,广泛应用于条件判断和逻辑运算中,通过本篇的学习,用户将掌握如何使用布尔类型进行逻辑操作和条件判断。
61 1
【10月更文挑战第6天】「Mac上学Python 10」基础篇4 - 布尔类型详解
WK
|
1月前
|
存储 Python
Python内置类型名
Python 内置类型包括数字类型(int, float, complex)、序列类型(str, list, tuple, range)、集合类型(set, frozenset)、映射类型(dict)、布尔类型(bool)、二进制类型(bytes, bytearray, memoryview)、其他类型(NoneType, type, 函数类型等),提供了丰富的数据结构和操作,支持高效编程。
WK
18 2
|
1月前
|
存储 编译器 索引
Python 序列类型(2)
【10月更文挑战第8天】
Python 序列类型(2)
|
1月前
|
存储 C++ 索引
Python 序列类型(1)
【10月更文挑战第8天】
|
1月前
|
存储 Java Apache
Python Number类型详解!
本文详细介绍了 Python 中的数字类型,包括整数(int)、浮点数(float)和复数(complex),并通过示例展示了各种算术操作及其类型转换方法。Python 的 `int` 类型支持任意大小的整数,`float` 类型用于表示实数,而 `complex` 类型用于表示复数。此外,文章还对比了 Python 和 Java 在数字类型处理上的区别,如整数类型、浮点数类型、复数类型及高精度类型,并介绍了各自类型转换的方法。尽管两种语言在语法上有所差异,但其底层逻辑是相通的。通过本文,读者可以更好地理解 Python 的数字类型及其应用场景。
49 2