简略版答案:
题目中的这个 Python 3.8.2 来自于苹果的一套命令行开发者工具(command line developer tools),其对应目录在:
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework
而在终端执行/usr/bin
目录下的python3
时,终端先根据环境变量PATH
扫描是否有 Python 3.x 的可执行程序,若不存在,则调出命令行开发者工具的安装程序,引导用户安装。
命令行开发者工具并不会对从官网下载安装的 Python 有任何影响,并且也不建议开发者删除(因为这也会将git
、clang
、gcc
、flex
等工具一并删除)。
只有当并不需要额外的其他工具,并认为此确实困扰到了自己的时候,才可以将其删掉,这个 Python 3.8.2 就随之消失了,执行如下命令即可:
sudo rm -rf /Library/Developer/CommandLineTools
(以下为原文)
这个问题提的很好,平常我用 macOS 的时候从未想到会有这个问题。
因为 macOS 是一个类 Unix 系统,系统文件结构与 Linux 类似,以我用 Linux 的经验来看,按理说,/usr/bin
目录下的python3
是一个链接文件(替身),可以从它找到相应指向的源文件(原身),进而可以得出系统内置 Python 3 所在的目录。
但我用
cd /usr/bin && ls -la@O | grep python3
查看了这个文件的属性,却发现“python3”是一个可执行文件:
-rwxr-xr-x 1 root wheel restricted,compressed 137600 1 1 2020 python3
这确实令我困惑,因为官方的帮助里并没有对这个细节作任何提及,不过我发现这样一个链接——
注意到了这样一句话:
A clean installation of Catalina includes a/usr/bin/python3
binary, but it's a stub for installing the command line developer tools, which includes Python 3.
“stub”这个词有“桩脚”的意思,但这里我没能理解,不过我注意到了“clean installation”和“command line developer tools”。
所以可猜:macOS “内置”的 Python 3 很可能与命令行开发者工具有关。
为验证猜想,我从头创建了一个 macOS 虚拟机,当我迫不及待地执行python3
这个命令的时候,神奇的一幕出现了:
系统竟然提示我安装命令行开发者工具 (以下简称 CLDT) ?!
安装好后,python3
命令可以正常使用了,当然这个版本就是 3.8.2,和题目一模一样。然后开始搜索 Python 3.8.2 的相应资源,结果发现全在这里:
/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework
相应的 CLDT 目录在:
/Library/Developer/CommandLineTools
接下来删掉 CLDT 的目录(以下就是把它卸载的方法):
sudo rm -rf /Library/Developer/CommandLineTools
结果发现删除目录之后python3
命令又会失效,回到前面的提示了。
但若从 python.org 安装好 Python,执行python3
命令后,其显示的 Python 版本却是已经安装的版本。
所以,这个诡异的执行文件/usr/bin/python3
的作用大概可以知道了:单独运行此程序时,调用 CLDT 中的 Python 3,若无 CLDT,且无手动安装的 Python,则激活“Install Command Line Developer Tools”应用,引导用户安装。[1]
可能有人会问了:为什么从终端调用python3
命令(而不是手动双击执行那个二进制文件)的时候,就只会运行手动安装的 Python 呢?
原因是环境变量 PATH 默认先加载配置在最前面的路径,如果某命令已经加载完毕,则不再继续往下寻找,否则会继续寻找,直到全部加载为止,若完全找不到,当然会输出“command not found”啦。
比方说我的用户名为administrator
(大雾),执行如下命令:
echo $PATH | awk '{ gsub(/:/,"
"); print $0 }'
输出结果如下(这就是我的 Mac 上的PATH
变量的设定,分行显示的):
/usr/local/sbin /usr/local/bin /Users/administrator/opt/anaconda3/bin /Library/Frameworks/Python.framework/Versions/3.9/bin /usr/local/bin /usr/bin /bin /usr/sbin /sbin /Applications/VMware Fusion.app/Contents/Public /Applications/Server.app/Contents/ServerRoot/usr/bin /Applications/Server.app/Contents/ServerRoot/usr/sbin /Library/Apple/usr/bin /Users/administrator/.cargo/bin
可以看到/usr/local/bin
排在/usr/bin
之前。
而我手动安装 Python 3.9.1 后,执行 which python3
命令可以看到,python3
命令执行的路径则在/usr/local/bin
目录下:[2]
/usr/local/bin/python3
因此系统只会加载 /usr/local/bin
下的 python3。
针对此,我们可以再验证一下:执行
type -a python3
来查看依据环境变量PATH
下能查找到python3
命令的所有执行文件的路径。
比方说在我的 Mac 上的是
python3 is /usr/local/bin/python3 python3 is /Library/Frameworks/Python.framework/Versions/3.9/bin/python3 python3 is /usr/local/bin/python3 python3 is /usr/bin/python3
只有排在最前的会首先加载,也就是/usr/local/bin
下的python3
。
苹果的 macOS 开发工程师们明知道大多数用 Python 的用户都会将解释器的目录设置在/usr/local/bin
目录下,为何在/usr/bin
塞入一个单独的二进制执行文件,而不是链接?
我个人最初觉得,这样设计是为了迎合某些 Linux 用户的编程习惯,因为这些人编写 .py 的脚本的时候,知道 Shell 解释器和一些工具的必需执行文件基本上要放在/usr/bin
目录下,会习惯性调用 Linux 内部的 Python,放到 macOS 上也一样。
但这个猜想似乎无法解释的通。后来我在不经意间有了新的发现:谜底竟然藏在xcode-select
命令的帮助文件里。[3]
很多使用 Homebrew 的朋友,在首次安装 CLDT 的时候,都用到xcode-select
这个命令:
xcode-select --install
但xcode-select
这个命令的主要作用,其实是为了方便开发者在安装了多个版本的 Xcode 下(尤其是安装有 Beta 版本的 Xcode,不在默认安装位置/Applications
的时候)切换不同的开发者目录,从而可以用不同版本的 CLDT 通过脚本和 makefile 进行有针对性的编译。
由此设计的/usr/bin/python3
这个可执行文件,就是从已经激活的开发者目录里查找并运行相匹配的 Python 版本。
相比 Windows 和某些 GNU/Linux 发行版找不到命令时简单粗暴的“直接报错”,这个让众多用户无法察觉到的设计,才是 macOS 的巧妙所在。
至此,这个问题才算真正解决了。
说真话,整个发现的过程虽然花了我很长的时间,但还是很值得回味的。
针对题主的提问,希望再啰嗦两点——
第一点是 macOS 内置的 Python 2 的消失。
Python 2 的最后一个版本是 2.7.16,已经于 2020 年的头一天就被官方停止支持了,但首次不支持 Python 2 的 macOS 版本是 Monterey 12.3 (21E230)。
对于该版本之前的 macOS,因为 Python 2 依然存在,仍可以用python
关键字来调用它。
第一次使用该命令时会显示一个警告:
WARNING: Python 2.7 is not recommended. This version is included in macOS for compatibility with legacy software. Future versions of macOS will not include Python 2.7. Instead, it is recommended that you transition to using 'python3' from within Terminal.
(警告:不推荐使用 Python 2.7。此版本包含在 macOS 中以与旧版软件兼容。未来的 macOS 版本将不再包含 Python 2.7。相反,建议您从终端过渡到使用“python3”。)
以及当安装了旧版的 pip package 时,你会看到如下的信息:
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7.
(声明:Python 2.7 将于 2020 年 1 月 1 日到期,请升级您的 Python,因为自此日起将不再维护 Python 2.7。pip 的未来版本将会放弃对 Python 2.7 的支持。)
这说明从 2020 年 1 月 1 日到 2022 年 3 月 16 日 Monterey 12.3 发布的这段时间内,macOS 内置 Python 2 的目的是为了让开发者进行老旧软件的过渡。
但自从 macOS Monterey 12.3 后,Python 2 已从系统中消失,因此只能通过python3
命令来调用 Python 3,原有的关键字python
不再可用。
如果想经常使用python
关键字来调用 Python 3,请首先用 vim 或 nano 编辑~/.zshenv
文件(若使用的 Shell 为 Bash 则编辑~/.bash_profile
文件);
然后在其中加入如下的一行:
alias python='python3'
保存后再执行source ~/.zshenv
( 若为 Bash 则执行source ~/.bash_profile
) ,重启终端即可生效。
另外需要说明的一点是,macOS 有一个安全机制,即系统完整性保护(System Integrity Protection,SIP),用来保护 macOS 的系统文件不受篡改,当然这个保护可以关闭,但会一定程度上降低 macOS 的安全性。即使关闭了 SIP,/usr/bin
目录下的文件仍然不能删除,哪怕 root 用户也不行,因此题主也不可能删得了这个二进制的文件python3
。
(这个细节也体现了 macOS 系统的健壮性)
✄- - - - - - - - - - - - - - - - -
退一步想,假如/usr/bin
下的文件像 Linux 一样可以通过 root 权限删除,那么在运行程序的时候,会导致无法预料的错误,甚至是系统的严重破坏。
Linux 上的sudo rm -rf /*
就是一个很惨痛的例子,不光所有的用户文件都会被删除,而且内核依赖的文件也因删除而彻底无法启动系统。
✄- - - - - - - - - - - - - - - - -
然而,对于题主来说,为了去掉这个多余的 Python 3.8.2,最佳的解决方法就是卸载命令行开发者工具,这个方法就在前面说过了。
当然这个问题的解决方案不止一种,比方说可以修改PATH
环境变量,或用 Anaconda。
如果你是第一次接触 Anaconda 的话,可以把 Anaconda 的 environment 理解为一个盛装 Python 的容器,而且 Python 的版本可以不同,pip package 则可以单独安装。
不同的是,执行命令
which python
时,base 环境下,返回的结果不是系统 Python 2 的解释器目录,而是下面的这个:
~/opt/anaconda3/bin/python
其他创建的环境下则是:
~/opt/anaconda3/envs/[自定义环境名]/bin/python
其中“~”代表你自己的用户文件夹。
而python3
命令则不受影响。
之前做了一些数据挖掘相关的东西,为了激活不同的环境,我会用 iTerm2 创建不同的配置文件,配合 Anaconda 使用。但比较郁闷的是 Tensorflow 死活配置不好,这一点非常想吐……
最后放一张很有意思的图。
不说了,我先笑一会儿……