python编程基本语法(值得收藏的python语法总结)python教程 / python语法详解...

wufei123 发布于 2024-06-16 阅读(6)

作者:袁昊 腾讯专项技术测试工程师2020年python2停止维护,而随着Python版本的不断更新,许多旧的语法在可读性与效率上都已经有更好的替代了当然,大部分的重要特性,例如装饰器、生成器、async等,相信大家都已经了然于心,这里就对一些用的稍微少一些、日常看到的代码中不太常见的能用得上的语法做一个简单的笔记,供大家参考。

经验有限,见解甚浅,还望各位大佬们多多指导、补充日常的自用Python脚本没有太大的工程压力,能紧跟更新步伐、尝试新的特性但是语法糖用的好就是效率提升,用的不好就是可读性灾难,有些语法的出现也伴随着种种的争议,用更新的语法不代表能写出更好的代码。

翻看语言的更新日志确实蛮有意思通过语法的更新变化还有变化带来的争议,也能窥透语言的设计哲学、汇聚浓缩在一个特定点上的社区开发经验选择合适自己的、保持对代码精简可读的追求才是最重要那么就从老到新,理一理那些有意思的小feature吧。

可能有漏掉有趣的点、也可能有解释不到位的地方,欢迎各位大佬更正补充Python 3.0-3.6PEP 3132 可迭代对象解包拓展Python3.0引入,加强了原本的星号运算符(*),让星号运算符能够智能地展开可迭代对象。

>>> a, *b, c = range(5) >>> a 0>>> c 4>>> b [1, 2, 3] 隐式赋值也同样适用>>>fora,*bin[(1,2,3),(4,5,6,7)]:>>>print(b)

[2,3][5,6,7]注意双星号(**)不能用相同语法展开字典人畜无害,用处也不大的一个featurePEP 465 矩阵乘法运算符Python3.5引入,顾名思义,使用@符号直接支持numpy、pandas等使用。

>>>a=numpy.array([1,2,3])>>>b=numpy.array([10,20,30])>>>a@b140>>>c=numpy.array([[10,15],[20,25],[30,35

]])>>>d=numpy.array([[4,5,6],[7,8,9]])>>>c@darray([[145,170,195],[255,300,345],[365,430,495]])矩阵乘法运算符的魔术方法为

__matmul__()、__rmatmul__()、__imatmul__()三个本身用处不大,但是提供了一个额外的操作符使用空间,可以用来重载来进行类似距离计算之类的用途>>> from math import sqrt 。

>>> classPoint:>>>     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争议主要存在于:作为矩阵乘法来说@操作符没有直观联系、影响可读性,不如直接使用matmulPEP 3107/484/526 函数注解/类型提示/变量注解Python3.0引入函数注解、3.5引入typing,让python也能享受静态类型的福利。

可以说是py3中个人最喜欢的feature,使用简单、效果强大,直接让开发效率以及代码可维护性直线增长# 参数后加:即可标注类型,函数结构定义后接->即可标注返回类型defget_hello(name: str)

 -> str:returnf"Hello, {name}!"如上进行标记之后IDE便能自动读取参数、返回类型,直接出联想爽快如java而PEP 484 Typing则是极大的扩充了类型定义语法,支持别名、泛型、Callable、Union等等。

非常推荐直接阅读PEPhttps://www.python.org/dev/peps/pep-0484/下面就是一个泛型的例子from typing import TypeVar, Iterable, Tuple T = TypeVar(

T, int, float, complex) Vector = Iterable[Tuple[T, T]] definproduct(v: Vector[T]) -> T:return sum(x*y 

for x, y in v) defdilate(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.03.7中又引入了延迟标记求值(PEP 563),让typing支持了前向引用、并减轻了标注对程序启动时间的影响,如虎添翼。

# 3.7前合法classTree:def__init__(self, left:Tree, right:Tree):         self.left = left         self.right = right

# 3.7前不合法、3.7后合法classTree:def__init__(self, left: Tree, right: Tree):         self.left = left         

self.right = right 更多的python类型检查示例代码:https://github.com/realpython/materials/tree/master/python-type-checking

静态类型检查对Python所带来的副作用主要还是启动时间上的影响,当然大部分场景所带来的便利是远大于这一副作用的PEP 498 f-stringPython3.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.866PEP 515 数值字面值下划线Python3.6引入输入太长的数字字面值怎么办?>>> a = 123_456_789>>> b = 123456789。

>>> a == b True 比较鸡肋...Python 3.7PEP 557 数据类Data Classes提供了一个方便的dataclass类装饰器,直接上代码举例:from dataclasses 

import dataclass @dataclassclassInventoryItem:     name: str     unit_price: float     quantity_on_hand: int = 

0deftotal_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 fInventoryItem(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.8PEP 572 海象牙运算符

"逼走"了Guido van Rossum,最有争议的PEP之一首先引入了海象牙运算符:=,代表行内赋值# Beforewhile True:     command = input("> ");     。

ifcommand == "quit":         breakprint("You entered:", command)      # Afterwhile (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"] deffunc_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"] deffunc_data():for x in range(len(temp_list)):         yield x result_list = func_data() print(list(result_list))

这样的修改目的是配合海象牙运算符增加代码可读性,但无疑是带破坏性的修改,且让运行顺序变得迷惑,让一些老代码出现难以发现的bugpython社区在激烈辩论后,这一部分的修改被成功撤销,只保留了海象牙运算符。

关于这个PEP,知乎上有难得一见的有价值讨论,这部分范例代码也引用自此:https://www.zhihu.com/question/274823057/answer/376917512PEP 570 仅限位置形参

在函数形参处新增一个/语法,划分非关键字与关键字形参例如deff(a,b,/,c,d,*,e,f):print(a,b,c,d,e,f)# 以下调用均合法f(10,20,30,d=40,e=50,f=60)。

# 以下调用均不合法f(10,b=20,c=30,d=40,e=50,f=60)# b cannot be a keyword argumentf(10,20,30,40,50,f=60)# e must be a keyword argument

/语法的添加让调用函数时可以在可读性与简洁之间自由选择,可以选择强制不接受关键字参数、不需要形参名称时也可以省略同时也让接受任意参数函数的实现变得方便了许多,例如:classCounter(dict):

def__init__(self, iterable=None, /, **kwds):# Note "iterable" is a possible keyword argument这条本来也有其他方案,例如装饰器实现、

def fn(.arg1, .arg2, arg3):、def fn(a, (b, c), d):等,这里就不一一展开了,推荐阅读PEP原文Python 3.9PEP 584 字典合并运算符在此之前,要想合并两个字典的画风是这样的。

a={a:1,b:2} b={c:3} a.update(b) # 或者是 c = {**a, **b} 但自从有了|之后,可以变成这样a |= b c = a | b 当然这个操作符也伴随着一些争议,大概是这样:

反方:合并不符合交换律 正方:python字典合并本身就不符合交换律,特别是python3.6之后统一到有序字典后,相比合并应该更类似于拼接反方:类似管道写法进行多次合并效率低,反复创建和销毁临时映射 正方:这种问题在序列级联时同样会出现。

如果真出现了合并大量字典的使用场景,应当直接显式循环合并反方:|操作符容易和位运算混淆运算符行为强依赖于变量种类,这在python是非常不利于可读性的 正方:确实有这个问题,但是|已经很混乱了(位运算、集合操作、。

__or__()魔术方法重载),所以还是先规范变量命名吧即将到来的Python 3.10PEP 617 / bpo-12782 括号内的上下文管理这一条是针对with语法(PEP 343)的小变动,让一个

with可以管理多个上下文使用也很简单with (CtxManager() as example):     ... with (     CtxManager1(),     CtxManager2() ):     ... 。

with (CtxManager1() as example,       CtxManager2()):     ... with (CtxManager1(),       CtxManager2() 

as example):     ... with (     CtxManager1() as example1,     CtxManager2() as example2 ):     ...

比较实用,避免了with下面接with产生不必要缩进的尴尬值得注意的是,这一条语法变动是新的非LL(1)文法CPython PEG解析器所带来的副产物所以PEP 617的标题是New PEG parser for CPython。

PEP 634 结构化模式匹配match-case直接上结构:match subject:     case :              case 

>:              case :              case _:         是不是感觉熟悉又臭名昭著的switch-case终于来了?当然还是有区别的:

这个写法基本还是if-elif-else的语法糖,运行完case就自动break出来再加上一些看着不错的模式匹配特性defhttp_error(status):     match status:case

400:             return"Bad request"case401| 403 |404:             return"Not allowed"case404:             

return"Not found"case418:             return"Im a teapot"case_:return"Somethings wrong with the Internet"

这样的写法看着就比if-elif-else看着清爽了许多针对元组、类、列表也有不错的支持:# point is an (x, y) tuple match point:     case (0, 0):         。

print("Origin")     case (0, y):         print(f"Y={y}")     case (x, 0):         print(f"X={x}")     

case (x, y):         print(f"X={x}, Y={y}")     case _:         raise ValueError("Not a point") 结语语言的发展是由技术的进步、工程的需求凝结出的结晶,从中透露出的是满满的代码设计哲学。

充分了解语法,可以让开发变得顺畅舒适;理解了语法背后的原因与争议,则可以开拓计算机科学领域的视野与时俱进,深入了解各种新兴技术,才是真正的极客~推荐阅读Whats new in Python https://docs.python.org/zh-cn/3.10/whatsnew/index.html

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

河南中青旅行社综合资讯 奇遇综合资讯 盛世蓟州综合资讯 综合资讯 游戏百科综合资讯 新闻3001