还望各位大佬们多多指导、补充。


有什么python相关报错解答自己不会的、或者源码资料/模块安装/女装大佬精通技巧 都可以来这里:(https://jq.qq.com/?_wv=1027&k=dwzSWSBK)或者+V:python10010问我

python符号冲突 python^符号_开发语言

日常的自用Python脚本没有太大的工程压力,

能紧跟更新步伐、尝试新的特性。

但是语法糖用的好就是效率提升,

用的不好就是可读性灾难,

有些语法的出现也伴随着种种的争议,

用更新的语法不代表能写出更好的代码。

通过语法的更新变化还有变化带来的争议,也能窥透语言的设计哲学、汇聚浓缩在一个特定点上的社区开发经验。选择合适自己的、保持对代码精简可读的追求才是最重要。

那么就从老到新,理一理那些有意思的小feature吧。可能有漏掉有趣的点、也可能有解释不到位的地方,欢迎各位大佬更正补充。

Python 3.0-3.6

PEP 3132 可迭代对象解包拓展

Python3.0引入,加强了原本的星号运算符(*),让星号运算符能够智能地展开可迭代对象。

python学习交流裙:660193417 ##3
>>> a, \*b, c = range(5)
>>> a
0
>>> c
4
>>> b
[1, 2, 3]

隐式赋值也同样适用

>>> for a, \*b in [(1, 2, 3), (4, 5, 6, 7)]:
>>>     print(b)
[2, 3]
[5, 6, 7]

注意双星号(**)不能用相同语法展开字典

人畜无害,用处也不大的一个feature

PEP 465 矩阵乘法运算符

Python3.5引入,顾名思义,使用@符号。直接支持numpy、pandas等使用。

>>> a = numpy.array([1, 2, 3])
>>> b = numpy.array([10, 20, 30])
>>> a @ b
140

>>> c = numpy.array([[10, 15], [20, 25], [30, 35]])
>>> d = numpy.array([[4, 5, 6], [7, 8, 9]])
>>> c @ d
array([[145, 170, 195],
       [255, 300, 345],
       [365, 430, 495]])

矩阵乘法运算符的魔术方法为__matmul__()、rmatmul()、imatmul()三个;

本身用处不大,但是提供了一个额外的操作符使用空间,可以用来重载来进行类似距离计算之类的用途。

python学习交流裙:906715085##3
>>> from math import sqrt

>>> class Point:
>>>     def \_\_init\_\_(self, x, y):
>>>         self.x = x
>>>         self.y = y
>>> 
>>>     def \_\_matmul\_\_(self, value):
>>>         x_sub = self.x - value.x
>>>         y_sub = self.y - value.y
>>>         return sqrt(x_sub\*\*2 + y_sub\*\*2)
>>> 
>>> a = Point(1, 3)
>>> b = Point(4, 7)
>>> print(a @ b)
5

争议主要存在于:作为矩阵乘法来说@操作符没有直观联系、影响可读性,不如直接使用matmul

PEP 3107/484/526 函数注解/类型提示/变量注解

Python3.0引入函数注解、3.5引入typing,让python也能享受静态类型的福利。可以说是py3中个人最喜欢的feature,使用简单、效果强大,直接让开发效率以及代码可维护性直线增长。

# 参数后加:即可标注类型,函数结构定义后接->即可标注返回类型
def get\_hello(name: str) -> str:
    return f"Hello, {name}!"

如上进行标记之后IDE便能自动读取参数、返回类型,直接出联想爽快如java。

而PEP 484 Typing则是极大的扩充了类型定义语法,支持别名、泛型、Callable、Union等等。非常推荐直接阅读PEP。

下面就是一个泛型的例子

from typing import TypeVar, Iterable, Tuple

T = TypeVar('T', int, float, complex)
Vector = Iterable[Tuple[T, T]]

def inproduct(v: Vector[T]) -> T:
    return sum(x\*y for x, y in v)

def dilate(v: Vector[T], scale: T) -> Vector[T]:
    return ((x \* scale, y \* scale) for x, y in v)

vec = []  # type: Vector[float]

随后在3.6引入了众望所归的变量注解(PEP 526),

使用也很简单,直接在变量后添加冒号和类型即可,

搭配函数注解一起食用体验极佳

pi: float = 3.142

# 也同样支持Union等
from typing import Union

a: Union[float,None] =1.0

3.7中又引入了延迟标记求值(PEP 563),

让typing支持了前向引用、并减轻了标注对程序启动时间的影响,如虎添翼。

# 3.7前合法
class Tree:
    def \_\_init\_\_(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right

# 3.7前不合法、3.7后合法
class Tree:
    def \_\_init\_\_(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

静态类型检查对Python所带来的副作用主要还是启动时间上的影响,当然大部分场景所带来的便利是远大于这一副作用的。

PEP 498 f-string

Python3.6引入,应该是用的最多的feature之一了,但是看到很多代码里面还是str.format,就不得不再提一下。

>>> a = 10
>>> #只需要简单的在任意字符串字面量前加个f,就可以用花括号直接引用变量
>>> print(f"a = {a}")
a = 10

>>> # 格式化也很方便,使用:即可
>>> pi = 3.14159
>>> print(f"pi = {pi: .2f}")
pi = 3.14

也可以在表达式后接!s或者!r来选择用str()还是repr()方法转换为字符串。

基本就是str.format的语法糖。

在3.8版本以后,又增加了直接套表达式的功能,输出信息非常方便。

>>> theta = 30
>>> print(f'{theta=}  {cos(radians(theta))=:.3f}')
theta=30  cos(radians(theta))=0.866

PEP 515 数值字面值下划线

Python3.6引入。输入太长的数字字面值怎么办?

>>> a = 123\_456\_789
>>> b = 123456789
>>> a == b
True

比较鸡肋…

Python 3.7

PEP 557 数据类Data Classes

提供了一个方便的dataclass类装饰器,直接上代码举例:

from dataclasses import dataclass

@dataclass
class InventoryItem:
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total\_cost(self) -> float:
        return self.unit_price \* self.quantity_on_hand

对这个例子,这个类会自动生成以下魔术方法

def \_\_init\_\_(self, name: str, unit_price: float, quantity_on_hand: int = 0) -> None:
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand
def \_\_repr\_\_(self):
    return f'InventoryItem(name={self.name!r}, unit\_price={self.unit\_price!r}, quantity\_on\_hand={self.quantity\_on\_hand!r})'
def \_\_eq\_\_(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) == (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def \_\_ne\_\_(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) != (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def \_\_lt\_\_(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) < (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def \_\_le\_\_(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) <= (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def \_\_gt\_\_(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) > (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented
def \_\_ge\_\_(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.unit_price, self.quantity_on_hand) >= (other.name, other.unit_price, other.quantity_on_hand)
    return NotImplemented

这一条PEP也是比较有争议的,主要原因是Python其实已经内置了不少的类似模型:collection.namedtuple、typing.NamedTuple、attrs等 ;

但是这条PEP的提出还是为了保证方便地创建资料类的同时,保证静态类型检查,而已有的方案都不方便直接使用检查器。

Python 3.8

PEP 572 海象牙运算符

python符号冲突 python^符号_运算符_02

"逼走"了Guido van Rossum,最有争议的PEP之一。

首先引入了海象牙运算符:=,代表行内赋值。

# Before
while True:
    command = input("> ");
    if command == "quit":
        break
    print("You entered:", command)
    
# After
while (command := input("> ")) != "quit":
    print("You entered:", command)

assignment expressions在进行分支判断时非常好用,

写的时候能够舒服很多。

本身使用也集中在if/while这种场景,虽然让语法变复杂了,

但是总体还是可控的,舒适程度大于风险。

海象运算符本身问题不大,但是争议主要存在于PEP 572的第二点,对于生成器语义的变化。

在PEP 572后,生成器的in后的运算顺序产生了变化,原本是作为生成器输入,结果现在变成了生成器闭包的一部分。

temp_list = ["abc","bcd"]
result_list = (x for x in range(len(temp_list)))
print(list(result_list))

# 等价于
# Before
temp_list = ["abc", "bcd"]


def func\_data(data: int):
    for x in range(data):
        yield x


result_list = func_data(len(temp_list))
print(list(result_list))

# After
temp_list = ["abc", "bcd"]