Python类型检查

时间:2020-02-23 14:43:38  来源:igfitidea点击:

1.什么是类型检查?

类型检查是一种编程语言功能,它指定如何创建变量以及由语言编译器或者解释器识别变量的类型。

2.类型检查有哪些不同类型?

可以根据类型检查将编程语言分为以下几种。

  • 静态类型语言-C,Java,C++等
  • 动态类型语言– JavaScript,Python,Ruby等

2.1)什么是静态类型检查?

变量的类型在编译时是已知的。
变量的类型是固定的,我们以后将无法更改。

让我们看一下Java中的变量声明。

String str = "Hello";

如果我们尝试更改变量的类型或者分配不兼容类型的值,则编译器将引发错误。

str = 10; //Type mismatch: cannot convert from int to String 

int str = 10; //Duplicate local variable str

2.2)什么是动态类型检查?

变量的类型在运行时确定。
我们没有在代码中指定变量的类型。
代码在运行时根据对象类型的不同而有所不同。

让我们看一下Python中的函数定义示例。

def add(x, y):
  return x + y

print(add(10, 5))
print(add('A', 'B'))

如果函数参数为整数,则返回总和。
如果它们是字符串,则将它们串联并返回。

如果我们传递任何其他自定义对象,则如果他们不支持'+'运算符,则可能会得到不同的响应或者引发错误。

2.3)静态类型语言与动态类型语言

  • 静态类型语言的好处是在编译时会捕获许多与不兼容类型有关的错误。
    对于动态类型的语言,情况并非如此。
    您可能需要很长时间才能收到与不兼容类型相关的错误。

  • 动态类型语言的好处是开发时间更短。
    但是,随着项目代码大小的增加,这种好处就会消失。
    由于类型错误,调试程序引发的错误确实非常困难,因为根据用户输入或者从其他来源收到的数据,该错误只会偶尔发生。

3.用Python进行鸭子输入

鸭子键入是动态键入的编程语言的概念。
对象的类型不如其定义的功能重要。

让我们以一个自定义对象和我们定义的add()函数为例进行说明。

def add(x, y):
  return x + y

class Data:

  def __init__(self, i):
      self.id = i

d1 = Data(10)
d2 = Data(5)

print(add(d1, d2))

此代码将产生以下运行时错误:

Traceback (most recent call last):
File "/Users/hyman/Documents/PycharmProjects/hello-world/theitroad/type_checking.py", line 12, in <module>
  print(add(d1, d2))
File "/Users/hyman/Documents/PycharmProjects/hello-world/theitroad/type_checking.py", line 2, in add
  return x + y
TypeError: unsupported operand type(s) for +: 'Data' and 'Data'

如果我们希望我们的对象支持加法运算符,我们要做的就是为其定义__add __()函数。

def __add__(self, other):
  return self.id + other.id

现在,print语句将打印15,并且代码不会产生任何错误。

因此,从本质上讲,对象的类型根本不重要。
只要定义了支持操作所需的功能,就不会因为对象类型而出现任何问题。

4.在Python中键入提示

Python 3.5添加了使用类型模块的类型提示支持。
顾名思义,这是开发人员提示功能参数预期类型和返回类型的一种方式。

假设我们有一个函数可以对两个数字执行一些运算。

def calculate(x, y, op='sum'):
  if op == 'divide':
      return x //y
  if op == 'difference':
      return x - y
  if op == 'multiply':
      return x * y
  # default is sum
  return x + y

即使仅用于数字,它也适用于字符串参数。

print(calculate(10, 3, 'divide'))  # 3
print(calculate(10, 5))  # 15
print(calculate('A', 'B'))  # AB

让我们看看如何为给定函数添加类型提示。

def calculate1(x: int, y: int, op: str = 'sum') -> int:
  # same code as above

函数参数类型提示带有冒号(:),返回类型使用->符号。

但是,这不会强制执行函数参数和返回类型。
该代码仍可用于其他类型的参数。

但是,第三方工具(例如类型检查器,IDE,lint等)可以对此进行解析,以警告我们参数类型错误的可能性。
例如,如果我们将字符串参数传递给此函数,则PyCharm IDE会生成警告消息,提示为"预期类型为'int',取而代之为'str'"。

4.1)类型提示的优点

  • 类型提示还记录了函数预期参数类型和返回类型的代码。

  • 它有助于API用户确保将正确类型的参数传递给函数。

  • 当将不兼容类型的参数传递给函数时,有助于改进IDE,类型检查器和Linter,以警告用户。

4.2)类型提示的缺点

  • 类型提示没有运行时优势。
    如果传递了其他类型的参数,它不会强制执行类型,也不会引发任何警告或者错误。

  • 类型提示更像文档。
    如果功能被更改,并且开发人员错过了相应地更改类型提示的操作,则会向使用该功能的开发人员提供错误消息。
    维护类型提示非常耗时,需要开发人员付出努力。

  • 它会稍微降低程序速度。

  • 类型提示是在Python 3.5中引入的,因此它是一个相当新的内容,不适用于旧版本的Python。

4.3)__annotations__属性

Python函数具有__annotations__属性,其中包含有关类型提示的信息。

def calculate(x, y, op='sum'):
  pass

def calculate1(x: int, y: int, op: str = 'sum') -> int:
  pass

print(calculate.__annotations__)  # {}

print(calculate1.__annotations__) 
# {'x': <class 'int'>, 'y': <class 'int'>, 'op': <class 'str'>, 'return': <class 'int'>}

5. Python运行时类型检查

我们可以使用type()函数在运行时获取变量的类型。

>>> x = 10
>>> type(x)
<class 'int'>
>>> 
>>> s1 = 'Hello'
>>> type(s1)
<class 'str'>
>>>

我们还可以使用isinstance()函数来检查变量是否属于某种类型。
该函数返回一个布尔值并接受两个参数。

>>> x = 10
>>> isinstance(x, int)
True
>>> isinstance(x, str)
False
>>>
>>> o = object()
>>> isinstance(o, (int, str, object))
True

6. Python静态类型检查

Python是一种动态类型化的语言。
但是,我们可以使用mypy模块进行静态类型检查。
请注意,这仅在我们向函数添加类型提示时才有效。

如果我们使用不兼容的数据类型参数调用函数,则mypy模块将检查代码并引发错误。
我们可以使用PIP命令安装mypy模块。

pip install mypy

假设我们有一个包含以下内容的Python脚本" type_checking.py"。

def calculate(x, y, op='sum'):
  pass

def calculate1(x: int, y: int, op: str = 'sum') -> int:
  pass

calculate('a', 'b')
calculate1('a', 'b')

现在,我们可以从命令行运行mypy来测试该文件的函数参数类型。

$mypy type_checking.py
type_checking.py:10: error: Argument 1 to "calculate1" has incompatible type "str"; expected "int"
type_checking.py:10: error: Argument 2 to "calculate1" has incompatible type "str"; expected "int"
Found 2 errors in 1 file (checked 1 source file)
$