(全文字数: 2300,阅读时间: 5分钟)
Python3和Python2之间的差别,是Python编程领域一个无法回避的话题。对于大部分Python爱好者来说,Python2是一个老朋友。相对来说,Python3则是一个新鲜一些的事物。
要问Python3到底新在哪里?网络上相关的博文汗牛充栋。但是随机阅读一些文章之后你会发现,它们几乎千篇一律地强调这些差异:print函数,除法运算符,unicode字符串等。
这些差异固然十分显著,但是它们并不是全部内容。对于Python3来说,仅仅强调这些差异是不公平的,并不能充分体现Python3的真实特点。最重要的是,了解这些差异,不足以让大家产生从Python2往Python3迁移的源动力。
事实上,Python3作为一个新事物,必然伴随着一批新特性。写这篇文章的目的,就是简要介绍若干Python3中重要新特性,引导大家客观认识Python3的强大功能。在此基础上,希望大家在开启新项目时,考虑真正代表Python的Python3,而不是无限期固守Python2。
字符串格式化新方式:f-string。字符串格式化是最基本的编程场景之一。传统的Python字符串格式化方式有两种:使用%操作符和使用str.format方法。
name, age = "Eric", 24# 方式1:使用%操作符print("Hello, %s. You are %s." % (name, age)) # 方式2: 使用str.format方法print("Hello, {}. You are {}.".format % (name, age))
这两种格式化方法共同的特点是可读性较差,并且易于出错,尤其在被格式化变量较多的情况下。Python 3.6提供了另外一种更简洁,更健壮,并且执行速度更快的字符串格式化方法:f-string:
name, age = "Eric", 24# 方式3(使用f-string方法)print(f"Hello, {name}. You are {age}.")
类型注解。大家知道,Python是一种动态类型编程语言。在定义变量时,不需要指定类型;只有运行时,变量才有类型,并且此时变量的类型由它此时的值的类型所决定。动态类型的好处是编程灵活,但其弊端是与静态类型相比,代码可读性较差。
为了提高Python程序可读性,从Python3.5开始,在定义变量时,可以指定变量所应该拥有的类型。这一功能被称为类型提示或者类型注解,示例如下:
# 无类型注解:定义变量name,初始值Ericname = "Eric"# 有类型注解:定义变量name,类型str, 初始值Ericname : str = "Eric"
需要强调的是,类型注解只是提示变量应该具有某一类型,而不强制变量必须具备这种类型。也就是说,Python作为动态类型编程语言的本质并没有发生改变,而只是增加了与静态语言类似的,描述变量类型的方式。
类型注解除了包括变量类型注解,还包括函数注解。后者可以用来提示函数返回值应该具有的类型。例如下面程序中的"-> str"语法,说明函数naivesum返回值应该是str类型:
def naivesum(a: str, b: str) -> str: return a + b
除了基本类型,结合标准库中的typing模块,还可以为变量或函数返回值指定任意复杂类型。例如,下面的代码定义了一个类型是字符串数组的变量names:
from typing import Listnames: List[str] = ["alice", "bob"]
枚举。Python3.4中提供了对枚举类型的支持,并通过Enum类来实现。例如,定义Month类型的枚举类:
from enum import EnumMonth = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员:
for name, member in Month.__members__.items(): print(name, '=>', member, ',', member.value)
这里value属性是自动赋给成员的int常量,默认从1开始计数。如果需要更精确地控制枚举类型,可以从Enum派生出自定义的类。还是比较灵活的。
数据类。Python是一种多范式的编程语言。面向对象编程是Python的重要范式之一。相比Python2,Python3对面向对象编程的支持更加全面。例如,从Python 3.7起,提供了全新的数据类(data class)。
什么是数据类呢?顾名思义,数据类就是类的属性以数据为主的类。基于全新的@dataclass装饰器创建数据类的示例如下:
from dataclasses import dataclass@dataclassclass DataClassCard: rank: str suit: str
而传统定义数据类的方式为:
class RegularCard def __init__(self, rank, suit): self.rank = rank self.suit = suit
可以看到,仅仅在初始化函数中,每个数据属性,例如rank,就重复出现了三次,而基于数据类的实现,rank只需要出现一次即可。当然,数据类的优势绝不只在于语法更加简洁。除此之外,数据类的可读性和描述性更强,基于数据类的各种数据比较运算也更加直接。
异步IO。在Python性能或并发编程中,多线程,多进程,异步IO是常用的技术手段。对于文件读写,网络访问等IO密集型应用来说,异步IO带来的性能提升通常是最明显的。
Python2不支持异步IO,一般使用第三方库gevent,以打补丁(patch)的形式将同步库变换为异步库,从而实现异步IO。然而gevent的方式存在一系列弊端:并不是所有Python库都被能patch,问题调试困难,Windows支持力度有限等。
Python3.4则提供了原生的异步IO支持。具体来说,它提供了async/await两个新的关键字来定义协程。这里协程是Python异步IO中的基本调度单元。并且在标准库asyncio模块中,提供了执行和管理协程的一系列API。
异步IO编程是Python3的重要特性,并且这个特性在不断演进之中。从Python 3.4到Python 3.7,异步IO的功能越来越完善,围绕Python 异步IO的生态系统也日益丰富。例如,第三方库aiohttp提供了异步的HTTP客户端服务器框架,第三方库aioredis提供了异步访问Redis数据库的功能。
至此,我简要列举了一些Python3的新特性。大家可以看到,这些新特性出现在不同的Python3版本中。从2008年到现在,基本上每年Python3都会发布一个较大的版本(从Python3.1到Python3.7),伴随而来的是层出不穷的新特性。本文列举的新特性,可以说只占很小一部分。
Python2最后一个大版本是2010年的Python2.7。根据Python社区的计划,从明年,即2020年开始Python2就停止维护了。在我看来,有没有官方维护,不一定是大家在舍弃Python2时的优先考虑因素。从Python2升级到Python3的主要动机,应当是拥抱Python3所能提供的新特性。
事实上,Python3不仅有许多新特性,而且官方支持力度大。更重要的,2008年至今,"十年磨一剑",可以说Python3已经相当成熟了。对于历史遗留项目,继续使用Python2是没有问题的。但是,如果要开启一个全新的项目,为什么还不选择Python3呢?
推荐阅读:
Python lambda: 从入门到放弃
Python: 告别Print?