什么是Ellipsis在Python中,你有时可能会看到一个奇怪的用法,像这样:>>>...Ellipsis当你输入三个点后,Python解释而不是报错,设备会返回像“省略号”这样的消息给你。那么这个有趣的东西是什么呢?查阅Python官方文档后,可以看到它是一个**“内置常量”**(Built-inConstant)。通常用于切片用户定义的容器数据类型的使用的扩展。这意味着它可能被用作“小众和替代”的语法糖,但是如果你在Python中将它用于容器数据类型(例如列表)的切片索引,它可能会导致错误:>>>nums=list(range(10))>>>nums[0,1,2,3,4,5,6,7,8,9]>>>nums[...]Traceback(mostrecentcallast):文件“”,line1,inTypeError:listindicesmustbeintegersorslices,notellipsis另外,如果你使用的是Python2解释器,完全不支持Ellipsis的使用,从输入开始就会报错:$python2WARNING:Python2.7isnotrecommended.ThisversionisincludedinmacOSforcompatibilitywithlegacysoftware.FutureversionsofmacOSwillnotincludePython2.7.Instead,itisrecommendedthatyoutransitiontousing'python3'fromwithinTerminal.Python2.7.16(默认,2019年11月9日,05:55:08)[GCC4.2.1CompatibleAppleLLVM11.13.0.1.0(clang)--objc-sondarwin键入“help”、“copyright”、“credits”或“license”获取更多信息。>>>...File"",line1...^SyntaxError:invalidsyntaxAlthough在列表中说WhenusingEllipsisin,会报错,但是当你遇到这种情况时,你会发现解释器返回给你的是这样的:>>>nums=[1,2,3]>>>nums[1,2,3]>>>nums[1]=nums>>>nums[1,[...],3]可以看到这里我们把nums中的第二个元素替换成了自己,会形成一个连续的递归嵌套赋值,解释器最终直接给出第一个和最后两个元素,其他的元素都会被包含进去……根据另一个Python官方文档,Ellipsis本身不支持任何操作,它只是一个单例对象(Singleton)。三个省略号的文风很可爱。(原文为:“有些人认为能写出这样不完整的代码会很可爱”)说这个看似“鸡”的Ellipsis类型对象没有用,应用似乎并不正确。因为它在某些地方也被用作一种奇怪的语法糖。Numpy中的切片虽然官方说Ellipsis主要用于用户自定义容器类型的切片操作,但是找了半天发现只有Numpy是用Ellipsis实现所谓切片操作的。使用Python进行数据分析、挖掘或机器学习的朋友一定对Numpy的高性能科学计算库不陌生。在Numpy中我们实际上使用Ellipsis进行切片索引:>>>importnumpyasnp>>>arr=np.arange(9).reshape((3,3))>>>arrarray([[0,1,2],[3,4,5],[6,7,8]])需要注意的是,Ellipsis主要作用于二维以上的数组:>>>arr[...,1:2]array([[1],[4],[7]])>>>arr[2,...]array([6,7,8])从结果可以看出Ellipsis的三个省略号其实是等价于arr[:,1:2]冒号的写法。但是省略号在使用过程中只能出现一次:>>>ndarr=np.arange(24).reshape((2,3,4))>>>ndarrarray([[[0,1,2,3],[4,5,6,7],[8,9,10,11]],[[12,13,14,15],[16,17,18,19],[20,21,22,23]]])>>ndarr[:,:,:]数组([[0,1,2,3],[4,5,6,7],[8,9,10,11]],[[12,13,14,15],[16,17,18,19],[20,21,22,23]]])>>>ndarr[...,...,...]追溯(mostrecentcalllast):File"",line1,inIndexError:anindexcanonlyhaveasingleellipsis('...')Ellipsis出现在Numpy中的意义在于,当你的数组是高维数组时,那么你就可以使用它直接作为选择其他维度的等效方法。下面的例子来自Numpy官方文档:>>>z=np.arange(81).reshape(3,3,3,3)>>>z[1,...,2]#Equivalenttoz[1,:,:,2]array([[29,32,35],[38,41,44],[47,50,53]])TypeHintTypeAnnotations从PEP484开始,Python解释器支持类型注释。所谓类型注解无非是在实际的Python代码中像注解一样给一些参数或返回值加上类型注解,像这样:defadd(x:int,y:int)->int:returnx+yif如果你用过Java或Go等需要更严格类型注解的编译型语言,相信你不会陌生。不管是变量还是方法,都要写对应的类型,防止编译出错;但是即使没有接触过这种编译型语言也没关系,理解为注释就好了。这样的注释可以被编辑器或IDE支持,当你想查看函数定义或文档时会提示你。但TypeHint只是一个“契约”。它告诉别人你方法中的参数是什么,最后返回什么。就这样。不管加不加,都不影响最终代码的效果。它只影响代码。可读性。如果你的方法有多个返回值,我们不可能注解每个返回值的类型,那么Ellipsis对象就派上用场了。根据官方文档给出的说明,我们可以这样进行类型注解:fromtypingimportTupledefget_many_value(a:int,b:int,c:int,d:int,e:int,f:int)->Tuple[int,...]:return[a+b,c+d,e+f]本质上是*args的函数,表示相同类型的变长元组。如果用List替换Tuple,解释器会报错,因为方法中*args的表现是元组,所以应该把Ellipsis作为注解。这或许也可以解释为什么元组注解不报错。FastAPI中的强制参数Ellipsis也用于流行的高性能Web框架FastAPI。用于表示该参数是必填项,在Swagger页面上体现更直观。#pipinstallfastapi#pipinstalluvicornfromfastapiimportFastAPI,Queryapp=FastAPI()@app.get('/greetWithOutEllipsis')asyncdefgreet(name:str=None):ifname:return{"info":f"欢迎!{name}"}return{"info“:f”欢迎使用FastAPI!}"}return{"info":f"WelcometoFastAPI!"}if__name__=="__main__":importuvicornuvicorn.run(app,port=5000)启动服务后,在浏览器中输入http://127.0.0.1:5000/docs就能进入服务的Swagger页面。在上面的例子中,如果name参数不是必须的参数,即使不带name参数,Swagger页面也看不到logo。Request:nonNecessaryparameter但是当我们添加一个Query()方法,把它的Ellipsis对象扔进去的时候,不仅会将参数标记为必需,还会限制传入字符串的长度。必填参数除了参数,你还可以在FastAPI的请求体、路径、字段等多个地方使用Ellipsis对象。“伪”pass的写法Ellipsis有时也可以作为pass的“伪”写法,比如这样:defgreet():...#相当于pass这实际上是一长串#注释符号和六个引号marks注释类似。但实际上,这只是一个取巧的方法。其实我们可以把...替换成任何值或者对象,比如None,1,True等,因为声明返回的对象是不会在方法中显示的,所以无论我们写什么最终效果都是相同。但是用Ellipsis对象代替pass关键字可能还是有点“形象”的意思。当然,如果你在和同事协作时随便写出这样的省略号,可能暗示着你对同事Coding的无奈,或者你担心秃头(逃避)