Python 为什么会有个奇怪的“...”对象?
off999 2024-11-17 00:28 24 浏览 0 评论
本文出自“Python为什么”系列,请查看全部文章
在写上一篇《Python 为什么要有 pass 语句?》时,我想到一种特别的写法,很多人会把它当成 pass 语句的替代。在文章发布后,果然有三条留言提及了它。
所谓特别的写法就是下面这个:
# 用 ... 替代 pass
def foo():
...它是中文标点符号中的半个省略号,也即由英文的 3 个点组成。如果你是第一次看到,很可能会觉得奇怪:这玩意是怎么回事?(PS:如果你知道它,仔细看过本文后,你同样可能会觉得奇怪!)
1、认识一下“...”内置常量
事实上,它是 Python 3 中的一个内置对象,有个正式的名字叫作——Ellipsis,翻译成中文就是“省略号”。
更准确地说,它是一个内置常量(Built-in Constant),是 6 大内置常量之一(另外几个是 None、False、True、NotImplemented、__debug__)。
关于这个对象的基础性质,下面给出了一张截图,你们应该能明白我的意思:
“...“并不神秘,它只是一个可能不多见的符号型对象而已。用它替换 pass,在语法上并不会报错,因为 Python 允许一个对象不被赋值引用。
严格来说, 这是旁门左道,在语义上站不住脚——把“...”或其它常量或已被赋值的变量放在一个空的缩进代码块中,它们是与动作无关的,只能表达出“这有个没用的对象,不用管它”。
Python 允许这些不被实际使用的对象存在,然而聪明的 IDE 应该会有所提示(我用的是 Pycharm),比如告诉你:Statement seems to have no effect 。
但是“...”这个常量似乎受到了特殊对待,我的 IDE 上没有作提示。
很多人已经习惯上把它当成 pass 那样的空操作来用了(在最早引入它的邮件组讨论中,就是举了这种用法的例子)。但我本人还是倾向于使用 pass,不知道你是怎么想的呢?
2、奇怪的 Ellipsis 和 ...
... 在 PEP-3100 中被引入,最早合入在 Python 3.0 版本,而 Ellipsis 则在更早的版本中就已包含。
虽然官方说它们是同一个对象的两种写法,而且说成是单例的(singleton),但我还发现一个非常奇怪的现象,与文档的描述是冲突的:
如你所见,赋值给 ... 时会报错SyntaxError: cannot assign to Ellipsis ,然而 Ellipsis 却可以被赋值,它们的行为根本就不同嘛!被赋值之后,Ellipsis 的内存地址以及类型属性都改变了,它成了一个“变量”,不再是常量。
作为对比,给 True 或 None 之类的常量赋值时,会报错SyntaxError: cannot assign to XXX,但是给 NotImplemented 常量赋值时不会报错。
众所周知,在 Python 2 中也可以给布尔对象(True/False)赋值,然而 Python 3 已经把它们改造成不可修改的。
所以有一种可能的解释:Ellipsis 和 NotImplemented 是 Python 2 时代的遗留产物,为了兼容性或者只是因为核心开发者遗漏了,所以它们在当前版本(3.8)中还可以被赋值修改。
... 出生在 Python 3 的时代,或许在将来会完全取代 Ellipsis。目前两者共存,它们不一致的行为值得我们注意。我的建议:只使用"..."吧,就当 Ellipsis 已经被淘汰了。
3、为什么要使用“...”对象?
接下来,让我们回到标题的问题:Python 为什么要使用“...”对象?
这里就只聚焦于 Python 3 的“...”了,不去追溯 Ellipsis 的历史和现状。
之所以会问这个问题,我的意图是想知道:它有什么用处,能够解决什么问题?从而窥探到 Python 语言设计中的更多细节。
大概有如下的几种答案:
(1)扩展切片语法
官方文档中给出了这样的说明:
Special value used mostly in conjunction with extended slicing syntax for user-defined container data types.
这是个特殊的值,通常跟扩展的切片语法相结合,用在自定义的数据类型容器上。
文档中没有给出具体实现的例子,但用它结合__getitem__() 和 slice() 内置函数,可以实现类似于 [1, ..., 7] 取出 7 个数字的切片片段的效果。
由于它主要用在数据操作上,可能大部分人很少接触。听说 Numpy 把它用在了一些语法糖用法上,如果你在用 Numpy 的话,可以探索一下都有哪些玩法?
(2)表达“未完成的代码”语义
... 可以被用作占位符,也就是我在《Python 为什么要有 pass 语句?》中提到 pass 的作用。前文中对此已有部分分析。
有人觉得这样很 cute,这种想法获得了 Python 之父 Guido 的支持 :
(3)Type Hint 用法
Python 3.5 引入的 Type Hint 是“...”的主要使用场合。
它可以表示不定长的参数,比如Tuple[int, ...] 表示一个元组,其元素是 int 类型,但数量不限。
它还可以表示不确定的变量类型,比如文档中给出的这个例子:
from typing import TypeVar, Generic
T = TypeVar('T')
def fun_1(x: T) -> T: ... # T here
def fun_2(x: T) -> T: ... # and here could be different
fun_1(1) # This is OK, T is inferred to be int
fun_2('a') # This is also OK, now T is strT 在函数定义时无法确定,当函数被调用时,T 的实际类型才被确定。
在 .pyi 格式的文件中,... 随处可见。这是一种存根文件(stub file),主要用于存放 Python 模块的类型提示信息,给 mypy、pytype 之类的类型检查工具 以及 IDE 来作静态代码检查。
(4)表示无限循环
最后,我认为有一个非常终极的原因,除了引入“...”来表示,没有更好的方法。
先看看两个例子:
两个例子的结果中都出现了“...”,它表示的是什么东西呢?
对于列表和字典这样的容器,如果其内部元素是可变对象的话,则存储的是对可变对象的引用。那么,当其内部元素又引用容器自身时,就会递归地出现无限循环引用。
无限循环是无法穷尽地表示出来的,Python 中用 ... 来表示,比较形象易懂,除了它,恐怕没有更好的选择。
最后,我们来总结一下本文的内容:
- ... 是 Python 3 中的一个内置常量,它是一个单例对象,虽然是 Python 2 中就有的 Ellipsis 的别称,但它的性质已经跟旧对象分道扬镳
- ... 可以替代 pass 语句作为占位符使用,但是它作为一个常量对象,在占位符语义上并不严谨。很多人已经在习惯上接受它了,不妨一用
- ... 在 Python 中不少的使用场景,除了占位符用法,还可以支持扩展切片语法、丰富 Type Hint 类型检查,以及表示容器对象的无限循环
- ... 对大多数人来说,可能并不多见(有人还可能因为它是一种符号特例而排斥它),但它的存在,有些时候能够带来便利。希望本文能让更多人认识它,那么文章的目的也就达成了~
本文属于“Python为什么”系列(Python猫出品),该系列主要关注 Python 的语法、设计和发展等话题,以一个个“为什么”式的问题为切入点,试着展现 Python 的迷人魅力。所有文章将会归档在 Github 上,项目地址:https://github.com/chinesehuazhou/python-whydo
相关推荐
- 阿里云国际站ECS:阿里云ECS如何提高网站的访问速度?
-
TG:@yunlaoda360引言:速度即体验,速度即业务在当今数字化的世界中,网站的访问速度已成为决定用户体验、用户留存乃至业务转化率的关键因素。页面加载每延迟一秒,都可能导致用户流失和收入损失。对...
- 高流量大并发Linux TCP性能调优_linux 高并发网络编程
-
其实主要是手里面的跑openvpn服务器。因为并没有明文禁p2p(哎……想想那么多流量好像不跑点p2p也跑不完),所以造成有的时候如果有比较多人跑BT的话,会造成VPN速度急剧下降。本文所面对的情况为...
- 性能测试100集(12)性能指标资源使用率
-
在性能测试中,资源使用率是评估系统硬件效率的关键指标,主要包括以下四类:#性能测试##性能压测策略##软件测试#1.CPU使用率定义:CPU处理任务的时间占比,计算公式为1-空闲时间/总...
- Linux 服务器常见的性能调优_linux高性能服务端编程
-
一、Linux服务器性能调优第一步——先搞懂“看什么”很多人刚接触Linux性能调优时,总想着直接改配置,其实第一步该是“看清楚问题”。就像医生看病要先听诊,调优前得先知道服务器“哪里...
- Nginx性能优化实战:手把手教你提升10倍性能!
-
关注△mikechen△,十余年BAT架构经验倾囊相授!Nginx是大型架构而核心,下面我重点详解Nginx性能@mikechen文章来源:mikechen.cc1.worker_processe...
- 高并发场景下,Spring Cloud Gateway如何抗住百万QPS?
-
关注△mikechen△,十余年BAT架构经验倾囊相授!大家好,我是mikechen。高并发场景下网关作为流量的入口非常重要,下面我重点详解SpringCloudGateway如何抗住百万性能@m...
- Kubernetes 高并发处理实战(可落地案例 + 源码)
-
目标场景:对外提供HTTPAPI的微服务在短时间内收到大量请求(例如每秒数千至数万RPS),要求系统可弹性扩容、限流降级、缓存减压、稳定运行并能自动恢复。总体思路(多层防护):边缘层:云LB...
- 高并发场景下,Nginx如何扛住千万级请求?
-
Nginx是大型架构的必备中间件,下面我重点详解Nginx如何实现高并发@mikechen文章来源:mikechen.cc事件驱动模型Nginx采用事件驱动模型,这是Nginx高并发性能的基石。传统...
- Spring Boot+Vue全栈开发实战,中文版高清PDF资源
-
SpringBoot+Vue全栈开发实战,中文高清PDF资源,需要的可以私我:)SpringBoot致力于简化开发配置并为企业级开发提供一系列非业务性功能,而Vue则采用数据驱动视图的方式将程序...
- Docker-基础操作_docker基础实战教程二
-
一、镜像1、从仓库获取镜像搜索镜像:dockersearchimage_name搜索结果过滤:是否官方:dockersearch--filter="is-offical=true...
- 你有空吗?跟我一起搭个服务器好不好?
-
来人人都是产品经理【起点学院】,BAT实战派产品总监手把手系统带你学产品、学运营。昨天闲的没事的时候,随手翻了翻写过的文章,发现一个很严重的问题。就是大多数时间我都在滔滔不绝的讲理论,却很少有涉及动手...
- 部署你自己的 SaaS_saas如何部署
-
部署你自己的VPNOpenVPN——功能齐全的开源VPN解决方案。(DigitalOcean教程)dockovpn.io—无状态OpenVPNdockerized服务器,不需要持久存储。...
- Docker Compose_dockercompose安装
-
DockerCompose概述DockerCompose是一个用来定义和管理多容器应用的工具,通过一个docker-compose.yml文件,用YAML格式描述服务、网络、卷等内容,...
- 京东T7架构师推出的电子版SpringBoot,从构建小系统到架构大系统
-
前言:Java的各种开发框架发展了很多年,影响了一代又一代的程序员,现在无论是程序员,还是架构师,使用这些开发框架都面临着两方面的挑战。一方面是要快速开发出系统,这就要求使用的开发框架尽量简单,无论...
- Kubernetes (k8s) 入门学习指南_k8s kubeproxy
-
Kubernetes(k8s)入门学习指南一、什么是Kubernetes?为什么需要它?Kubernetes(k8s)是一个开源的容器编排系统,用于自动化部署、扩展和管理容器化应用程序。它...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
慕ke 前端工程师2024「完整」
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
