Python随笔-5
本文最后更新于:2022年9月30日 晚上
文章未完成
__future__
模块
说白了就是把新版本的特性导入到当前Python版本。
然后程序里是这个东西:from __future__ import annotations
额,这条语句其实搞不是很懂,参考了下别人的博文,大概有这么两种解释得好的:
加入以下语句,解释器将不再构造这些复合类型。
from __future__ import annotations
一旦解释器解析脚本语法树后,它会识别类型提示并绕过评估它,将其保留为原始字符串。这种机制使得类型提示发生在需要它们的地方:由linter来进行类型检查。 在Python 4中,这种机制将成为默认行为。
原文链接:全面理解Python中的类型提示(Type Hints)
还有一个是代码例子:(原文链接:Python Class 定义时声明当前 Class 类型)
1 |
|
但是上面的代码是会报错的,原因就在于self: Foo
。因为在类Foo
定义前就已经被自己的方法__init__
使用了,解释器无法理解,报出NameError: name 'Foo' is not defined
错误。
这个时候这条语句就有用了:
1 |
|
typing
模块——类型注解支持
这个模块提供对类型提示的运行时支持。下边详细介绍一下程序里边的从typing导入的各种东西:
TypeVar
类型变量的缩写。类型变量主要是为静态类型检查器提供支持,用于泛型类型与泛型函数定义的参数。有关泛型类型,详见Generic
。
其实就是定义一个泛型变量,然后你就能把这个泛型变量用在泛型类型或者泛型函数定义的参数中了。直接上例子:
1 |
|
你也可以对类型变量进行绑定(bound),或者约束类型变量,比如:
1 |
|
但是要注意的是,类型变量不能既是被绑定的又是被约束的。
Generic
用于泛型类型的抽象基类。
泛型类型一般通过继承含一个或多个类型变量的类实例进行声明。官方文档给出的例子是这样的:
1 |
|
这里还给出了另一篇文章中的例子:
1 |
|
或许可以理解为,如果你定义的一个类中使用了某种类型变量,那么你可以通过Generic
来对你的类声明。(如果你创建的类中想要使用某种类型变量,如T,那么你就通过继承Generic[T]
类来使用。)
Protocol
官方文档是这么描述的:协议类的基类。呃,那么问题来了,我也不知道什么是协议类啊。。。所以我直接使用搜索引擎,找到了点零零散散的东西。
在Python中,协议就是一个或一组方法(接口),那协议类很明显就是包含协议的类,就是在这个类里边放一些不实现的方法,是不是像那啥,抽象基类啊?
比如下面的代码,__eq__
和__lt__
不就pass了吗,这俩就能看成协议。
1 |
|
然后官方文档接着说了:这些类主要与静态类型检查器搭配使用,用来识别结构子类型(静态鸭子类型)。啊,好的,我也不知道啥是鸭子类型,所以我又去找搜索引擎帮忙了,然后我找到这么一篇文章:你知道什么是Python里的鸭子类型和猴子补丁吗?
文章里是这么说的:
鸭子类型是对Python中数据类型本质上是由属性和行为来定义的一种解读。
Python是一种动态语言,不像Java和C++这种强类型语言,Python里实际上没有严格的类型检查。
只要某个对象具有鸭子的方法,可以像鸭子那样走路和嘎嘎叫,那么它就可以被其它函数当做鸭子一样调用。
啥意思呢,举个栗子:
假设我们现在有两个类,一个Duck
类,一个Goose
类,两个类都实现了同样的方法bark()
然后我们自定义了一个函数test
,这个函数接受一个Duck
对象,并调用该对象的bark()
方法。在其它语言中,我们需要指定传入形参duck
的类型,得这么写test(Duck duck)
,但是在Python中不需要,直接这么写:test(duck)
但由于Python是动态语言,没有严格类型检查,这就导致你把一个Goose
类的对象塞进去也不会报错,因为Goose
类也实现了bark()
方法。
卸载再来看下官方文档给出的例子:
1 |
|
上面的Proto
类即是协议类,里面只有一个协议(方法)meth()
,而另一个则是C
,它实现了协议meth()
,那么它就可以看作是鸭子类型。
这里的func()
函数用了类型检查,虽然指定了参数x
的类型是Proto
,但是由于Proto
继承了Protocol
,所以传入鸭子类型C
的时候,跳过了静态类型检查。
Protocol 类可以是泛型,例如:
1 |
|
Callable
希望本文章能够帮到您~