python的type hints

在PEP 483中提出了使用Type Hints来表明数据类型。

子类型

子类型的定义:
如果满足下列2个条件,则second_type是first_type的子类型

  1. second_type的任意值属于first_type的值的几何
  2. first_type的任意函数属于second_type的函数集合

可以看出,一个类型包含的值越多,函数越少,则在类型树上越接近根部。

一个例子:int类型是float类型的子类型,int类型支持位运算,float不支持。而float支持的函数,int类型都支持。
另外一个例子,List[int]则不是 List[float]的子类型. 第一个条件满足,但是第二个条件,append函数只能向List[float] 增加实数,不能向List[int] 增加实数。

有2种声明subtype的方式:

  1. nominal subtyping, 类型树是基于class 树的,例如
class UserId(int):
     pass

UserId则是int类型的子类型

  1. structural subtyping, 子类型关系是通过声明来推断的

gradual typing

gradual type允许用户指定部分变量,在动态类型和静态类型中进行了平衡。

在此定义了一种新的关系,与...一致(is-consistent-with),这种关系类似于"是...子类型",除了当涉及到Any类型时,不具有传递性。
当a的类型与b的类型一致,那么可以将a的值赋给b。这种一致性关系可以用以下三个规则定义:

  1. 如果类型t1是类型t2的子类型,那么称t1t2一致。(反过来不行,不能说t2t1一致)
  2. 类型Any与所有类型一致。(但是Any不是任何类型的子类型)
  3. 任何类型与Any一致。(但是任何类型不是Any的子类型)

Any 可以看作是一个有所有值和所有函数的类型。结合子类型的定义,Any即作为类型树的顶端(有所有类型)也是类型树的底端(有所有函数)。当使用Any类型时,一般表示任何类型都可以接受,这时候就蜕化到了动态类型,使得静态类型检查器不再报错。

Types和Classes

类是动态,运行时概念,
类型由building blocks构建,被静态类型检查器使用。
每个类都是一个类型。
类和类型的区别可以总结为以下一般准则:

  1. 类型不能实例化,否则会产生TypeError错误。(Generic的非抽象子类可以)
  2. 类型不能被继承,除非是Generic和其子类
    3.类型如果出现在isinstance issubclass中,会产生TypeError错误。(除非是非参数的generics)

building blocks

  • Any
  • Union[t1,t2,...] 指定的类型是t1,t2... 的子类型
  • Optional[t1]Union[t1,None] 的简写
  • Tuple[t1,t2,...tn] 元组,其中的变量类型依次为t1,t2,...,tn
    • 空元组使用Tuple[()]
    • 可变的同类型元组可以使用Tuple[t1,...] 使用三个点表示
  • Callable[[t1,t2,...tn],tr] 函数,参数类型分别为t1,t2,...,tn,返回值为tr。无法为可选参数或关键字参数声明类型,但是可以声明不检查参数列表:Callable[...,tr],是的,用三个点表示。

变量注释语法

PEP 526 python3.6

primes: List[int] = []

captain: str  # Note: no initial value!

class Starship:
    stats: ClassVar[Dict[str, int]] = {}

这样写不是强制的,python仍然是动态语言。这样只是为了便于读程序和使用第三方库。

ClassVartyping模块的一个特殊的类,为了向类型检查器表示该变量是class 变量,不需要在实例中赋值。需要注意,在ClassVar中,不能嵌套任何类型变量。

一般约定,对于实例变量,应该在__init__或其他函数中进行注解。

from typing import Generic, TypeVar
T = TypeVar('T')

class Box(Generic[T]):
    def __init__(self, content):
        self.content: T = content

函数注释语法

def greeting(name: str) -> str:
    return 'Hello ' + name

参数name的类型是str,返回值类型也是str。

可以接受的注释(Annotations)

可以是内建类,抽象积累,types模块中的类型,用户自定义类。
还可以是None,Any,Union,Tuple,Callable,和从typing中导出的类(例如Sequence和Dict),类型变量(type variables)和类型别名(type aliases)

类型别名aliases

类型别名可以通过简单的变量赋值的形式实现

Url = str

def retry(url: Url, retry_count: int) -> None: ...

类型别名最好首字母大写。
任何在注释中接受的类型都可以在别名中使用。

from typing import TypeVar, Iterable, Tuple

T = TypeVar('T', int, float, complex)
Vector = Iterable[Tuple[T, T]]

def inproduct(v: Vector[T]) -> T:
    return sum(x*y for x, y in v)
def dilate(v: Vector[T], scale: T) -> Vector[T]:
    return ((x * scale, y * scale) for x, y in v)
vec = []  # type: Vector[float]

Generic types

类似于函数的语法,函数接受value返回value, generic type 构造器接受type,构造type。
generic function, 接受type variables

def take_first(seq: Sequence[T]) -> T: # a generic function
    return seq[0]

accumulator = 0 # type: int

accumulator += take_first([1, 2, 3])   # Safe, T deduced to be int
accumulator += take_first((2.7, 3.5))  # Unsafe

类型变量,type variables

X=TypeVar('X')声明了类型变量X,名字必须和变量名一样,默认情况下,类型变量可以是任何类型。
Y=TypeVar('Y',t1,t2,...)则指定了类型,类型Union[t1,t2]

S = TypeVar('S', str, bytes)

def longest(first: S, second: S) -> S:
    return first if len(first) >= len(second) else second

result = longest('a', 'abc')  # The inferred type for result is str

result = longest('a', b'abc')  # Fails static type check

在这个例子中,两个参数的类型必须是一样的。
但是如果没有指定,则可以是任意类型。

你可能感兴趣的:(python的type hints)