0 引言:定义一个线段
假设我们使用 Python 实现数学中的整点线段的概念,即 $L = [a,b]$, 且 $a,b \in Z$。使用 Python 可以这样写:
类 LineSegment 模拟了线段的形态与长度。但是,这样的写法是不是有点繁琐?为了更加人性化,Python 提供了 typing 模块,让我们可以对代码进行注解。
1 typing 注解 Python
如果想要声明函数参数和返回值的类型,您可以这样做:
这就是被称为 function annotation 的写法。使用冒号 : 加类型名来代表参数的类型,使用箭头 -> 加类型表示返回值的类型。注解部分不会被 Python 解析器所解析。只是一种注解的方式,类似于:
注意:由于 Python 是动态语言,所以注解是对函数参数和返回值的“注释”,没有强制定义的作用。
比如,您像这样 add(1.2, 3.0) 传入参数,Python 解释器并不会报错。
输出结果并没有报错:
1.1 使用 inspect 检查 python 对象的类型
如果您想要让 Python 对类型进行检查,可以借助模块 inspect。比如:
如果对函数的参数进行检查呢?这个需要借助 sig=inspect.signature:
输出结果:
接着,可以直接获取函数的信息:
输出结果:
Parameter 是 inspect 下的一个类,可以把它看做是一个有序字典,里面存放了函数的参数和参数类型,遍历 params.values() 的 annotation 属性会得到参数的注解类型:
输出结果是:
您可以这样定义类型检查函数:
1.2 利用 python 对象的 __annotations__ 属性检查类型
下面以定义一个商品对象:
输出结果是(Any 代表 Python 很难表达的形式或者类型):
可以看出,__annotations__ 属性与 1.1 中的 params.values()[k].annotations 很相似。
这样,可以改写其类型检查函数为:
2 dataclass 提供强大的类型注解机制
dataclass 的定义位于 PEP-557,一个 dataclass 是指“一个带有默认值的可变的 namedtuple”,广义的定义就是有一个类,它的属性均可公开访问,可以带有默认值并能被修改,而且类中含有与这些属性相关的类方法,那么这个类就可以称为 dataclass,再通俗点讲,dataclass 就是一个含有数据及操作数据方法的容器。
我们先看看 dataclass 的参数:
key
含义
init
指定是否自动生成__init__,如果已经有定义同名方法则忽略这个值,也就是指定为True也不会自动生成
repr
同init,指定是否自动生成__repr__;自动生成的打印格式为class_name(arrt1:value1, attr2:value2, ...)
eq
同init,指定是否生成__eq__;自动生成的方法将按属性在类内定义时的顺序逐个比较,全部的值相同才会返回True
order
自动生成__lt__,__le__,__gt__,__ge__,比较方式与eq相同;如果order指定为True而eq指定为False,将引发ValueError;如果已经定义同名函数,将引发TypeError
frozen
设为True时对field赋值将会引发错误,对象将是不可变的,如果已经定义了__setattr__和__delattr__将会引发TypeError
参数 unsafehash 的使用比较复杂,当设置为unsafehash=True时将会根据类属性自动生成__hash__,然而这是不安全的,因为这些属性是默认可变的,这会导致hash的不一致,所以除非能保证对象属性不可随意改变,否则应该谨慎地设置该参数为True。对于 unsafehash=False 的情况将根据eq和frozen参数来生成__hash__,下面单独列出:
参数设置
含义
eq=True,frozen=True
__hash__将会生成
eq=True,frozen=False
__hash__将被设为None
eq=False,frozen=True
__hash__将使用超类(object)的同名属性(通常就是基于对象id的hash)
注意:最好去掉了__init__方法,以确保 dataclass 装饰器可以添加它生成的对应方法。
如果要覆盖 __init__,我们将失去 dataclass 的优势,因此,如果要处理任何附加功能可以使用新的 dunder 方法:__post_init__,让我们看看__post_init__方法对于我们的包装类来说是什么样子的。
由于 Python 支持中文作为变量名、类名、函数名等,所以,我可以这样定义一个 Python 类:
输出结果:
上面的代码模拟了出售商品的清单,整个代码看起来都很清爽。