Python3的一些新特性

下面的内容在py3.8测试ok,有些是3.5或者3.7之后才开放的特性

语句变成函数

print语句在python3里变成了print()函数。

1
2
3
4
5
6
7
# python2
print 'hello pig!'

# python3
print('hello pig')
print('1', '2', '3', sep='+')
print('1', '2', '3', sep='\n')

raise语句。

1
2
3
4
5
# python2
raise Exception, 'I made a mistake.'

# python3
raise Exception('I made a mistake.')

当然还有exec()

1
2
3
4
5
# python2
exec 'print("Hello World")'

# python3
exec('print("Hello World")')

除法

python2中/的结果是整型,python3中是浮点类型。

1
2
3
4
5
6
7
# python2
print 10/2
>>> 5

# python3
print(10/2)
>>> 5.0

允许数值下划线

为了提高多位数字的可读性,可以这样写:

1
2
a = 1_000_000_000_000 # 等同于 1000000000000
b = 0x_FF_FF_FF_FF # 等同于 4294967295

字典保持顺序

现在的字典是有序的了,不需要再调用collections.OrderedDict来生成有序字典了。

类似迭代器的返回值

在python3中,之前返回列表的内置函数,比如map,range,zip,filter等等。
它们的返回值都变成了类似迭代器的对象。(这种懒加载方式为了性能考虑)

这里以map来举例:

1
2
3
4
5
6
7
# python2
map(sum, ((1, 2), (3, 4), (5, 6)))
>>> [3, 7, 11]

# python3
map(sum, ((1, 2), (3, 4), (5, 6)))
>>> <map at 0x7ff2989a0910>

注意:字典里的dict.keys(),dict.values(),dict.items()在python3里也不会返回列表了,
同样返回的是一个类似迭代器的对象。(字典的dict.iteritems()方法没有了)

except语句

except语句的轻微改变,
捕获异常必须使用except...as...的格式。

1
2
3
4
5
6
7
8
9
10
11
# python2
try:
1/0
except ZeroDivisionError, e:
print e

# python3
try:
1/0
except ZeroDivisionError as e:
print(e)

赋值表达式

优点是可以防止多次计算重复值和可以写赋值表达式。
(其实就是为了少写一行,有点鸡肋的特性)

比如这样一段代码:

1
2
3
4
5
6
7
8
9
import re

data = "check123out"
match = re.search("(\d+)", data)
if match:
num = match.group(1)
else:
num = None
print(num)

使用赋值表达式:

1
2
3
4
5
6
7
8
9
import re

data = "check123out"

if match := re.search("(\d+)", data): # 这里进行了简化
num = match.group(1)
else:
num = None
print(num)

类型标注

比如声明一个函数时,标注其参数和返回值的类型。
注意:它只起到标注作用(增加代码可读性),python并不会真的限制其类型。

一个简单的例子:

1
2
3
4
5
def greeting(name: str) -> str:
return 'Hello ' + name

def plus(a: int, b: int = 2) -> int:
return a + b

类型标准支持别名,可以用它来简化一些复杂的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from typing import Dict, Tuple, Sequence

def broadcast_message(
message: str,
servers: Sequence[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:
pass

# 可以使用别名简化为

ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]

def broadcast_message(message: str, servers: Sequence[Server]) -> None:
pass

当然万物皆是对象的python,函数也可以成为其标注的对象。

1
2
3
4
5
6
from typing import Callable

def get_regex() -> Callable[[str, str], bool]:
def regex(pattern: str, string: str) -> bool:
return re
return regex()

变量也可以标注类型,这样就可以先声明类型,再进行赋值。

1
2
name: str
id: int = 123

在python3.7后,我们可以使用dataclass来简化声明类的时,标记类型的写法。
(这是真好用!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 之前的写法
from typing import Tuple

class Bar:
def __init__(
self, name: str, phone: int, location: Tuple[float, float]
):
self.name = name
self.phone = phone
self.location = location

# 使用dataclass写法
from dataclasses import dataclass
from typing import Tuple

class Bar(dataclass):
name: str
phone: int
location: Tuple[float, float]

参考:https://www.python.org/dev/peps/pep-0484/

格式化字符串

可以不用format了!

一个简单的用法:

1
2
3
4
name = "Han Meimei"
print(f"My name is {name}.")

>>> My name is Han Meimei.

支持函数。

1
2
3
4
5
def foo():
return 20

print(f'result={foo()}')
>>> result=20

可以用做十进制转换。

1
2
3
value = 1234
f'input={value:#06x}'
>>> 'input=0x04d2'

用来格式化日期

1
2
3
date = datetime.date(1991, 10, 12)
print(f'{date} was on a {date:%A}')
>>> '1991-10-12 was on a Saturday'

还可以这样嵌套来限制字符宽度和数字的长度。

1
2
3
4
5
6
7
import decimal

width = 10 # value部分的总长度,左边空格填充
precision = 4 # 数字的总长度,这里即保留小数点后两位
value = decimal.Decimal('12.34567')
print(f'result: {value:{width}.{precision}}')
>>> result: 12.35

支持=符号,来打印可读性的输出:

1
2
3
4
5
6
pos_x = 1.0
pos_y = 2.0
pos_z = 3.0

print(f'The xyz value: {pos_x=} {pos_y=} {pos_z=}')
>>> The xyz value: pos_x=1.0 pos_y=2.0 pos_z=3.0

参考:https://www.python.org/dev/peps/pep-0498/

虚拟变量

在python2中,运行下面的代码,变量i是会被打印出来的。

1
2
3
a = [i for i in range(10)]
print(i)
>>> 9

而在python3中就会触发错误:NameError: name 'i' is not defined

断点调试

之前设置断点都是通过诸如pycharm之类的IDE来设置。
在python3.6之前,也许会使用import pdb; pdb.set_trace()

现在可以在代码里通过函数breakpoint()来设置断点。
比如下面这段会导致ZeroDivisionError代码:

1
2
3
4
5
def divide(e, f):
return f / e

a, b = 2, 1
print(divide(a, b))

添加breakpoint()函数。

1
2
3
4
5
6
def divide(e, f):
breakpoint()
return f / e

a, b = 2, 1
print(divide(a, b))

当调试完成后,可以注释掉所有的breakpoint()代码,也可以设置环境变量PYTHONBREAKPOINT为0,python将会忽略所有breakpoint()的调用。

模块重载

python3中的reload被替换成了:

1
2
3
import spam
import imp
imp.reload(spam)