您的位置:首页 > 编程语言 > Python开发

关于python3.8的一些新特性的解析与代码演示

2019-08-04 23:12 330 查看
原文链接:http://www.cnblogs.com/traditional/p/11300448.html

python3.8测试版出来了,我们来介绍一些变动,代码演示一下,当然底层相关的细节变动就不介绍了

只允许传位置参数

还记得如果我们想让某些参数只能以关键字参数的方式传递该怎么做吗?

def foo1(x, y, z):
print(x, y, z)
"""
这是一个很简单的函数,但是现在我的需求是让参数z只能以关键字参数的方式传递,该怎么做呢?
现在的话,我调用foo(1, 2, 3)肯定是没问题的,但我想要求传参的时候这么传,foo(1, 2, z=3)
前两个参数无所谓,但是z必须通过关键字的方式传递
"""
# 很简单
def foo2(x, y, *args, z):
print(x, y, z)
"""
这样的话,如果只传递位置参数的话,无论我们传递多少个,最终都传不到z那里去。
因为*args最终都会接收掉
这样就满足了我们的需求
"""
# 但是还是不够优雅,因为我们定义了args却没有使用
# 但我们的目的并不是使用args,只是希望让*args后面参数能够以关键字的方式传递
# 于是可以这么定义
def foo3(x, y, *, z):
print(x, y, z)
"""
也就是说,我不定义*args,只是指定一个*即可
"""
# 这样是不是就更优雅了呢?
[/code]

但是python3.8中增加了新功能,除了指定某些参数只能通过关键字参数传递之外,可提供了新的模式,让我们只能传递位置参数。

def func1(a, b, c=1, /):
print(a, b, c)
"""
实现手段是/,我们可以使用*,保证*后面的参数只能通过关键字参数的方式传递
也可以使用/,保证/之前的参数只能通过位置参数的方式传递
"""
try:
func1(1, 2, c=2)
except Exception as e:
print(e)  # func1() got some positional-only arguments passed as keyword arguments: 'c'
# 提示我们向一个只能使用位置参数传递的参数c,通过关键字参数传递了
# 尽管我们定义的时候,是c=1,但是调用只能是位置参数,因为后面有个/

def func2(a, b, c, /, d, e, *, f):
"""
a,b,c只能位置参数传递,
d,e位置参数、关键字参数均可
f只能关键字参数传递
"""

"""
另外这种定义方式是不行的
def func3(a, b=1, /, c):
尽管/前面的参数在传递的方式只能以位置参数的方式传递,但是c既可以用位置参数也可以用关键字参数
如果我调用的时候,func(1, 2),那么这个2到底是传给b还是传给c呢?
"""
# 但是这样定义是可以的
def func4(a, b=1, /, *, c):
"""
因为c只能通过关键字参数传递
"""
[/code]

用官网来解释就是

def func1(name, **kwargs):
print(name)
print(kwargs)

try:
func1("satori", **{"name": "mashiro"})
except Exception as e:
print(e)  # func1() got multiple values for argument 'name'
# 告诉我们name传了两次,为什么呢
# 因为"satori"传递给了name,而**{"name": "mashiro"}相当于 name="mashiro"

def func2(name, /, **kwargs):
print(name)
print(kwargs)

# 这样传递是可以的
func2("satori", **{"name": "mashiro"})
"""
satori
{'name': 'mashiro'}
"""
[/code]

语法解析

在python3.8之前,这么传递参数也是可以的,尽管不会有人这么传递

def foo(a):
print(a)

foo(a=1)  # 1
foo((a)=1)  # 1
[/code]

但是在python3.8的时候,这么传递参数就不行了。会报出SyntaxError

continue的合法性

在3.8之前,finally语句允许break语句,但是不允许continue语句,下面这段代码如果在3.8之前运行,是会报错的。

for i in range(1, 10):
try:
assert i > 5
print(i)
except AssertionError:
pass
finally:
continue
[/code]

但如果使用python3.8来执行的话

for i in range(1, 10):
try:
assert i > 5
print(i)
except AssertionError:
pass
finally:
continue
[/code]

LOAD_GLOBAL

加载全局变量的速度,比之前快了百分之四十。

f-strings

我们平时使用的f-strings大概如下

s = "abc"
print(f"{s}")  # abc
print(f"{s!r}")  # 'abc'

v = 123
print(f"{v}")  # 123
print(f"{v + 3}")  # 126

name = "mashiro"
print(f"{name:>20}")  # 总共20个字符,右对齐
print(f"{name:<20}")  # 总共20个字符,左对齐
print(f"{name:^20}")  # 总共20个字符,中间对齐
"""
mashiro
mashiro
mashiro
"""

name = "mashiro"
print(f"{name:*>20}")  # 总共20个字符,右对齐,用*填充
print(f"{name:*<20}")  # 总共20个字符,左对齐,用*填充
print(f"{name:*^20}")  # 总共20个字符,中间对齐,用*填充
"""
*************mashiro
mashiro*************
******mashiro*******
"""
[/code]

但是在3.8中增加了一个=的语法

s = "abc"
print(f"{s}")
print(f"{s=}")
"""
abc
s='abc'
"""

v = 123
print(f"{v}")
print(f"{v=}")
print(f"{v+1=}")
"""
123
v=123
v+1=124
"""
# 可以看到,自动帮我们把=和=前面的内容组合在一起,然后拼接到值的前面了
# 另外{s=}或者{v=}这种用法是3.8中新增的,在3.8之前是报错的
[/code]

最终限定符

在python3.8的typing模块中,引入了最终限定符,叫做final,我们来看看这是干什么用的

另外值得一提的是,在python3.7中也是可以引入的,但是会报出一个ImportError,但是3.8中不会

那么这个final是干什么的呢?

我们给一个类加上这个final装饰器,那么这个类不可被继承。目前还是测试版,还没有报错,估计正式版出来应该会报错的

如果一个类没有被final装饰,说明这个类是可以被继承的,但如果父类的方法被加上了final装饰了,那么子类中是不可以重写的。当然在测试版中,这段也不会报错。

final是用于装饰函数或者类的,还有一个Final,可以作用于变量上面

我们知道python是动态语言,变量不需要显示声明,解释器会自动推断,但我们依旧可以指定。

这里我们只是pycharm提示的,但是对于python解释器执行的时候是不受影响的,比如函数foo,接收字符串返回整型,但我就反着来,传入整型,返回字符串,它能那我咋地?这在python中叫做注解,也就是说注解就类似于注释一样,只是方便代码编写人员阅读,当看到一个函数的时候,就知道这个函数接收什么样的参数,但是解释器在解释的时候是不会考虑注解的。只是说ide会在代码规范的层面上提示我们,比如我们明明定义函数的是要求返回int,结果却返回了str,所以智能的ide会提示我们,但这在解释器执行的层面是没有影响的。

那么Final到底是干啥的?

如果对一个变量使用Final进行注解,那么这个变量是不可被重新赋值的。现在是测试版,还是可以执行的,但是在正式版会报错。

翻转字典

在3.8之前,是不允许对字典使用reversed这个内置函数的

d = {'a': 1, 'b': 2, 'd': 4, 'c': 3}
try:
print(reversed(d))
except Exception as e:
print(e)  # 'dict' object is not reversible
[/code]

但在3.8中,可以使用了

d = {'a': 1, 'b': 2, 'd': 4, 'c': 3}
try:
print(reversed(d))  # <dict_reversekeyiterator object at 0x0000018482DDB4F0>
print(list(reversed(d)))  # ['c', 'd', 'b', 'a']
except Exception as e:
print(e)
[/code]

反斜杠

在Python中,如果\和后面字符无法组合的话,就会报错,在测试版中还只是一个警告

print("\k")
[/code]

赋值表达式

下面终于到了重头戏了,也不知道是谁的头这么重哈。这是一个笔者期待已久的功能,从python之父因为这件事和其他的核心开发者吵起来的时候,我就开始关注了。个人觉得这是python的一次大胆的尝试,至少我个人是非常喜欢这个语法的,让我找到了golang的感觉。

Time always save the best for last

时间总是会把好的东西留到最后,下面我们就开始吧

# 首先:=在golang中是可以直接用于变量的赋值的
# 也就是说在go中,直接可以写a:=1,但是python不行,因为python中创建变量还是使用=
# 如果非要使用的话,需要加上括号,(a:=1)这种方式是可行的,但是一般不这样创建变量
# python中的:=还是要放在相应的表达式中

a = 2
if a > 1:
print("a > 1")  # a > 1

# 使用赋值表达式的话
if (b := 2) > 1:
print("b > 1")  # b > 1
"""
if (b := 2) > 1:
相当于执行了两步,第一步:将2赋值给b,然后将b和1进行比较。
所以(b := 2)整体就相当于b,只不过之前是没有b的,现在有b了,而且b还等于2
如果在golang中的话,是这么写的,if b:=2;b>1
等于说golang直接把变量声明、赋值和条件判断合在一起了,使用;分割。
个人觉得python中的更简洁一些。
"""
[/code]

这里还有一个问题,那就是变量的作用域问题

if (a := 2) > 1:
...

# 那么此时我还能打印出来a吗?
try:
print(a)  # 2
except Exception as e:
print(e)

# 答案是可以打印出来的,个人觉得应该存在一定的作用域才对
# 比如这里的,a应该只作用于相应的if else语句块里面才对
# vars()是一个由全局变量组成的字典,或者使用globals()也行
print(vars().get("a"))  # 2
print(globals().get("a"))  # 2
# 我们依旧是可以获取到的,这说明a这个变量确实作为全局变量被创建了

# 之前说过在go中,可以直接使用x:="abc"这种方式声明变量,但这只是go的一个语法糖
# 或者说是简化声明变量的一种手段罢了,但对于python来说,已经可以直接使用=来声明变量了
# 不应该使用x:="abc"这种方式来声明,但如果我非要使用呢?可以加上括号
(x := 123)
print(x)  # 123
# 通过这里我们可以大胆的猜测,if (a:=2) > 1:就完全等价于
"""
a = 2
if a > 1:
...
这个(a:=2)就相当于将2赋值给全局变量a,然后(a:=2)就等价于a
"""

# 从测试的版本,我们可以得出这个结论,但是正式版如何还不得而知。
[/code]

下面讨论一下:=的优先级问题,不知道大家注意到了没有,我们在a := 2的两端加上了(),也就是说我们是这么写的if (a := 2) > 1,可如果我们换一种写法呢?

if (a := 2) > 1:
print(a)  # 2

# 换一种写法,会有什么输出呢?
if a := 2 > 1:
print(a)  # True
# 打印出来了True,说明:=的优先级相对于右边来说是较低的。
# 那这个if语句就等价于如下
"""
a = 2 > 1
if a:
print(a)

其实也很好理解,对于a = 2 > 1来说,也是先执行2 > 1,然后将结果赋值给a
那么对于:=也是一样的,从这里我们也能看出:=后面也是可以不加条件的
"""
if b := "haha":
print(b)  # haha
"""
等价于
b = "haha"
if b:
print(b)
"""
[/code]

一个表达式中不能出现多个:=

if a:=1 and b:=1:
print(a, b)
# 这种是不被允许的
[/code]

基础概念介绍于此,看看具体的例子

import re

s = "abc|def|ghi"
if match := re.match(r"abc\|", s):
print(match.group(0))  # abc|

with open("1.txt", "w", encoding="utf-8") as f:
data = "my\n name\n is\n satori"
f.write(data)

with open("1.txt") as f:
while line := f.readline():
print(line, end="")
"""
my
name
is
satori
"""
print()

import os

os.remove("1.txt")

l1 = [a := 1, b := 2, a + b]
print(l1)  # [1, 2, 3]
print(a, b)  # 1 2

l2 = [x + 3 for x in range(10) if (x + 3) % 2 == 0]
print(l2)  # [4, 6, 8, 10, 12]
l2 = [y for x in range(10) if (y := x + 3) % 2 == 0]
print(l2)  # [4, 6, 8, 10, 12]
[/code]
"""
y := 3 无效
(y := 3) 有效,但是不推荐
"""
(y := 3)
print(y)  # 3

"""
a = b := 3 无效
a = (b := 3) 有效,但是不推荐
"""
a = (b := 3)
print(a, b)  # 3 3

i = (j := 3) > 1  # i = 3 > 1 --> i = True
print(i, j)  # True 3
i = (j := (3 > 1))  # i = (j := True)
print(i, j)  # True True
[/code]
def foo(x=None, y=None):
print(f"x = {x}, y = {y}")

# 猜猜会打印什么
foo(y := 3)  # x = 3, y = None
"""
为什么会是这个结果,因为y := 3相当于将3赋值给y,然后foo(y),说白了就是foo(3),那么这个3就会按照顺序传递给x,而不是y
"""
print(y)  # 3

foo(y=3)  # x = None, y = 3
# 这个就是我们期待的结果了

# 或者
foo(x=(y:=33))  # x = 33, y = None
# 相当于将赋值给y,然后将y赋值给x,相当于foo(x=3)
print(y)  # 33
[/code]
"""
y = lambda: x := 1  无效
y = lambda: (x := 1)  有效,但貌似没啥乱用

x := lambda: 1  无效
(x := lambda: 1)  有效

关于括号的问题,诸如 xx:=yy,如果在xx:=yy的左边还有操作符的话
比如a = xx:=yy啊,a == xx:yy, a == xx:yy,a = lambda: xx:=yy之类的,这些都会抛出语法错误
只要左边还有操作符,那么必须加上小括号,也就是(xx:=yy)
至于右边就无所谓了,xx:=yy>1和(xx:=yy)>1都是可以的,只不过前者是将yy>1返回的布尔值赋值给xx,而后者是将yy赋值给xx,然后判断是否xx>1
yy = 3
print(xx:=yy>1)  # True
print(xx)  # True
print((xx:=yy)>1)  # True
print(xx)  # 3

但是注意的是,尽管xx:=yy>1和(xx:=yy)>1这种:=左边没有操作符,但是必须要放到放到语句里面,比如if,while,或者函数的参数、比如这里的print等等,如果直接写在最外层的话,还是会报错的。
比如:
xx:=yy>1  # 这种写法在最外层的话,还是不合法的,之前介绍过了
(xx:=yy>1)  # 在最外层直接写的话,还是要使用这种加括号的方式
"""
(x := lambda: 1)
print(x())  # 1
print(x() == 1)  # True

z = (x := (y := 3))
print(x, y, z)  # 3 3 3
"""
y = 3
z = (x := y)
z = x

x == y == z == 3
"""
flag = (z := (x := (y := 3))) == 3
"""
y = 3
flag = (z := (x := y)) == 3
x = y
flag = (z := x) == 3
z = x
flag = z == 3
flag = True
"""
print(flag)  # True
[/code]

感觉python要飘啊

print(f"{(s:=10)}")  # 10

a = 11
print(f"{a:=5}")
"""
输出结果:
11
表示输出结果为11,总共占五位,右对齐,只能应用于数字
"""

b = 2
print(f"{b:=20}")
"""
2
"""
[/code]
names = ["satori", "mashiro", "kurisu", "koishi"]
print([len(v := name) == 6 for name in names])  # [True, False, True, True]
print([v := name for name in names if len(name) == 6])  # ['satori', 'kurisu', 'koishi']
print(v)  # koishi
[/code]
def foo(x):
if x > 90:
return "excellent"
elif x > 75:
return "good"
elif x > 60:
return "ふつう"

else:
return "bad"

for score in [50, 65, 75, 95]:
if (level := foo(score)) == "excellent":
print(level)
else:
print("not excellent")
"""
not excellent
not excellent
not excellent
excellent
"""
[/code]
li = range(10)
print((arr := li[2: 5])[2])  # 4

value = 100
print(value == (value:= (value := 8) + 2))  # False
"""
value == (value:= (value := 8) + 2)
等价于
value = 8
value == (value:= 8 + 2)
等价于
value = 10
value == 10
但左边的value此时还是100
这就跟x, y = 1, 2
x, y = y, x
print(x, y)  # 2, 1
是一个道理,尽管x赋值给y的时候,y也和x一样,变成1了,但是y给x的还是原来的2,只有在x, y = y, x这个语句结束之后,才会看到y之后的值,也就是1
这里的value是一个道理

但是当我再次打印value的时候,这里的value就不再是外面的100了
"""
print(value)  # 10

num = 1
print(num == (num:=3))  # False
print(num)  # 3
[/code]

关于python3.8的新特性就介绍到这里,祝大家早日达到装逼如风,常伴吾身的境界

转载于:https://www.cnblogs.com/traditional/p/11300448.html

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: