Python的静态类型之旅

栏目: Python · 发布时间: 6年前

内容简介:本文系掘金Python月专题文章,转载请注明来源江湖有句话:“动态类型一时爽,代码重构火葬场”被广为流传,这话一般出自静态语言拥护者口中,听起来有点耸人听闻,但也没有想象中的那么严重,Python在大型项目的应用太多了,Instagram就是最好的例子。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这样的工具也能即时的发现代码的问题。

Python的静态类型之旅

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


以上所述就是小编给大家介绍的《Python的静态类型之旅》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Effective Engineer

The Effective Engineer

Edmond Lau / The Effective Bookshelf, Palo Alto, CA. / 2015-3-19 / USD 39.00

Introducing The Effective Engineer — the only book designed specifically for today's software engineers, based on extensive interviews with engineering leaders at top tech companies, and packed with h......一起来看看 《The Effective Engineer》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具