3、接口存根文件这个选项允许你如下图一般保存你的代码:
并在原文件的旁边添加一个扩展名为pyi的文件:
接口文件并不是一个新事物,C/C++已经用了十几年了。因为Python是一种解释语言,它通常不需要接口文件。但是由于计算机科学中的每一个问题都可以通过增加一个新的中间层来解决,那么我们就可以添加一层来存储类型信息。这样做的优点是:√你不需要修改源代码,且在任何python版本下都可以工作,因为解释器不会处理这些文件。√在存根文件中,你可以使用最新的语法(比如,类型标注)。
对于复合类型,一遍一遍的重复定义显得很笨重。所以系统允许你通过下面的方式对复合类型进行命名:
你还可以指定类型为多种指定类型中的一个:
我们还可以用TypeVar函数来定义自己的一般变量:
最后,在不需要检查的地方可以使用Any这种类型提示来禁止类型检查:2、鸭子类型–协议在这种情况下,与其说是使用类型去定义更像是用python语言来定义,并遵守这样的定律:如果一个生物像鸭子一样嘎嘎叫又表现的很像鸭子,那么无论是出于什么样的目的都可以认为这个生物就是鸭子。在本例中,你需要在对象上定义想要的操作和属性,而不是直接声明它们的类型。这里的依据请查看PEP-544~Protocols。
哈,抓到你了一旦你开始在代码库中添加类型提示,有时可能会遇到一些奇怪的情况。那时你可能像下面这只海豹一样露出“这是什么鬼”的表情。
1、Python2/3间的差异这里来快速地在一个类上实现__repr__方法:
现在,要让IDE接收这种形式,你需要添加一些linter注释,这使得代码读起来很复杂。更重要的是,类型检查器的存在迫使你必须在运行中额外进行一次检查。2、多个返回类型假设你想编写一个函数,作用是将一个字符串或者整数乘以2。对此的一种尝试可能是:
你输入的类型是str或者int,你的返回值相应的也是str或int。然而,如果如上图中那样做,你实际上是在告诉类型提示输入的类型可以是这两种类型中的任意一种。因此,作为调用方,你需要声明调用的类型:
这种不便可能会让一些人通过定义返回值为any类型来避免麻烦。但是,这儿有一种更好的解决方案。类型提示系统允许你定义重载。重载的意思是对于一个给定类型的输入只会返回一个指定类型的输出。对于本例而言:
一旦运行你就会发现:
有人可能会问这到底是怎么回事。我给返回值的定义是float类型而不是test.A.float类型。出现这种混淆错误的原因时类型提示通过定义的位置出发评估每一个遇到的范围来解析类型。一旦找到匹配的名称,它就停止了。本例中类型提示所查找的第一层次是在类A中,在这里它找到了一个float(float函数)并进行了替换。
值得注意的是,要做到这一点你还需要引入builtins同时为了避免在运行时出现这种问题,你可以使用typing.TYPE_CHECKING标志来指导类型提示,这个标志只有在linter工具执行期间为真,其余时刻都为假。4、逆变参数检查下面的例子。你定义了一个抽象的基本类,其包含了一些常规操作。然后你有一些具体的类,它们都只处理一种类型。
然而当你运行类型linter工具进行检查时会发现:
这里的问题在于类中的参数是逆变的。这意味着在你的派生类中,必须处理父类中所有的类型。然而,你还可以在派生类中添加额外的类型。也就是说你只能扩展派生类中的函数参数,但不能以任何形式加以限制:
5、兼容性看看你能否从下面的代码片段中发现错误:
这是因为B是A的子类。进而B可以被装入一个A类的容器中(又因为B扩展了A,所以B能做的比A更多)。然而B的类方法不能被装入A类的容器,因为它不能近用一个参数来调用magic方法。此外,类型linter工具也不能指出这一点:
一个快速而简单的解决方法是通过一些手段确保B.magic方法可以在只有一个参数的情况下工作,比如将第二个参数设置为可选项。现在用我们所学到的来看下面的代码:
你觉得会发生什么?注意,我们将类方法移动到构造函数中,并没有做其他的修改。所以我们的脚本也需要一点小小的修改:
领取专属 10元无门槛券
私享最新 技术干货