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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容