当前位置: 首页 > 科技观察

如何更好地使用Python的类型提示?

时间:2023-03-14 18:43:29 科技观察

一会用动态语言,代码重构火葬场。相信你一定听过这句话。就像单元测试一样,虽然它会花费你少量的时间来编写代码,但从长远来看是非常值得的。本文分享如何更好地理解和使用Python的类型提示。1.类型提示仅在句法层面有效类型提示(自PEP3107起引入)用于为变量、参数、函数参数及其返回值、类属性和方法添加类型。Python的变量类型是动态的,可以在运行时修改。只在语法层面支持在代码中添加类型提示,对代码的运行没有影响。Python解释器在运行代码时会忽略类型提示。因此,类型提示的一个直观作用就是提高代码的可读性,方便调用者传入/传出合适类型的参数,便于代码重构。Python内置的基本类型可以直接用于类型提示:变量的类型提示示例:a:int=3b:float=2.4c:bool=True:list=["A","B","C"]e:dict={"x":"y"}f:set={"a","b","c"}g:tuple=("name","age","job")类型提示对于函数:defadd_numbers(x:type_x,y:type_y,z:type_z=100)->type_return:returnx+y+z其中type_x、type_y、type_z、type_return可以是内置基本类型或自定义类型。类的类型提示:classPerson:first_name:str="John"last_name:str="Does"age:int=312.用mypy检查类型提示如果有这样一段代码:x:int=2x=3.5使用Python解释器执行不会出错:借助mypy,先用pipinstallmypy安装,然后安装mypyscript.py:关于mypy的更多信息,请参考上面的工具mypy,它使Python类型提示很实用。3.类型提示的好处如果解释器不强制执行类型提示,为什么要编写类型提示?类型提示确实不会改变代码的运行方式:Python本质上是动态类型的,而且不太可能改变。但是,从开发人员体验的角度来看,类型提示有很多好处。(1)使用类型提示,尤其是在函数中,通过类型提示明确参数的类型和产生的结果类型,非常容易阅读和理解。(2)类型提示消除了认知开销,使代码更易于阅读和调试。给定输入和输出的类型,您可以轻松推断出对象及其调用方式。(3).类型提示可以改善代码编辑体验。IDE可以依靠类型检查来静态分析您的代码并帮助检测潜在的错误(例如,传递错误类型的参数、调用错误的方法等)。此外,可以根据类型提示为每个变量提供自动完成功能。IDE'stypecheckingIDE'stypecheckingIDE'sauto-completionafterIDEtypecheckingsum(l)标准库类型考虑了这个问题,你可以这样做:fromtypingimportListdefmy_dummy_function(vector:List[float]):returnsum(vector)5.Dictusage如果你想提示这样的类型:my_dict={"name":"Somenzz","job":"engineer"}使用Dict,你可以这样定义类型:fromtypingimportDictmy_dict_type=Dict[str,str]my_dict:my_dict_type={"name":"Somenzz","job":"engineer"}6.TypedDict用法如果需要提示这样的类型,应该怎么办?d={"name":"Somenzz","interests":["chess","tennis"]}使用TypedDict,你可以这样做:TypedDict7,UnionUsage自Python3.10起,Union被|取代这意味着Union[X,Y]现在等同于X|Y。Union[X,Y](或X|Y)表示X或Y。假设您的函数需要从缓存目录读取文件并加载Torch模型。此缓存目录位置可以是字符串值(例如/home/cache)或Pathlib库中的Path对象,在这种情况下,代码如下所示:defload_model(filename:str,cache_folder:Union[str,Path]):ifisinstance(cache_folder,Path):cache_folder=str(cache_folder)model_path=os.join(filename,cache_folder)model=torch.load(model_path)returnmodel8,CallableUsage当你需要传入一个函数作为参数时,此参数的类型提示可以是Callable。从输入importCallabledefsum_numbers(x:int,y:int)->int:returnx+ydeffoo(x:int,y:int,func:Callable)->int:output=func(x,y)returnoutputfoo(1,2,sum_numbers)还可以像这样为函数参数指定参数列表,真的很强大:语法:Callable[[input_type_1,...],return_type]示例:deffoo(x:int,y:int,func:Callable[[int,int],int])->int:output=func(x,y)returnoutput9,Anyusage当你传入的参数可以是任何类型时,你可以使用Anydefbar(input:Any):...10.Optionalusage如果你的函数使用带有默认值的可选参数,那么你可以在类型模块中使用Optional类型。从输入importOptionaldeffoo(format_layout:Optional[bool]=True):...11。序列使用序列类型对象是任何可以被索引的对象:列表、元组、字符串、对象列表、元组列表元组等来自输入importSequencedefprint_sequence_elements(sequence:Sequence[str]):fori,sinenumerate(s):print(f"item{i}:{s}"12.Tuple的使用Tuple类型的工作方式与List类型略有区别,Tuple需要指定每个位置的类型:fromtypingimportTuplet:tuple[int,int,int]=(1,2,3)如果不关心元组中每个元素的类型,可以继续使用元组的内置类型。t:tuple=(1,2,3,["cat","dog"],{"name":"John"})最后,类型提示在代码之上带来了一个额外的抽象层:它们帮助记录代码,澄清关于输入/输出,并防止在顶部执行静态代码分析(mypy)时出现神秘和错误行为。