conda虚拟环境原理:为什么能创建不同Python版本而venv不能?
很多人第一次被“虚拟环境”这个概念搞晕往往都是从这个问题开始的我已经用 venv 建好了一个环境为什么python --version还显示系统自带那个版本更奇怪的是别人在教程里写一句conda create -n py311 python3.11好像就能得到一个干净且独立于系统的 Python 3.11 环境conda 到底做了什么事让它能创建出不同 Python 版本的虚拟环境这个问题的核心答案说起来其实并不复杂conda 没有把“虚拟环境”理解成“给现有 Python 加一层隔离”而是把 Python 解释器本身也当成一个可以安装、可以卸载、可以指定版本的普通软件包。只要你理解了这一点后面所有行为——为什么能切换版本、为什么能装系统级依赖、为什么环境之间互不污染——就都能顺下来了。这篇文章就从头拆一遍这条逻辑链顺便把实际操作中容易踩的坑也一起讲清楚。1. venv 解决不了的版本隔离问题1.1 venv 只是“借”解释器环境目录里根本没有真正的 Python要理解 conda 的特殊之处先得搞明白常规的python -m venv到底做了什么。执行之后你会得到一个目录里面通常有bin/、lib/、include/、pyvenv.cfg看起来像是一个完整的 Python 环境但实际上它创建的bin/python大多只是指向系统里某个既有 Python 的软链接或者是一个几十 KB 的启动器脚本。也就是说venv 环境里的 Python 可执行文件并没有把自己的“本体”——也就是解释器核心代码、配套标准库的那些动态链接库——复制进来。它只是把系统 Python 的启动入口包装了一下同时额外建立了一个独立的site-packages目录用来安装第三方库。这样做的好处是轻量、快速代价也很明显解释器版本完全由外部那个 Python 决定。你用系统 Python 3.9 建的 venv里面永远只会是 Python 3.9。很多人第一次在建好的 venv 里运行which python看到路径确实指向了venv/bin/python就以为解释器已经独立了。其实你再跑一下python -c import sys; print(sys.base_prefix)看到的结果仍然是系统 Python 的安装目录。这个base_prefix就是隔层纱戳破之后你就知道venv 只是“借用”了解释器并没有拥有一份真正的 Python。1.2 解释器一旦编译版本和路径就基本固定在二进制里Python 解释器的版本信息不是单纯靠读取某个文本文件得到的它在 CPython 编译阶段就被写进了二进制程序里。python --version打印出来的 3.9、3.11、3.12本质上是二进制文件中内置的字符串和常量。因此只要底层解释器还是旧版本无论你在环境变量上做多少手脚输出的版本都不会变。更进一步解释器在启动之后还会根据自己编译时设置的路径去定位标准库、动态链接库等资源。在正常情况下它几乎不会去“猜”系统里还有没有其他 Python而是直接按照内部记录的安装前缀去找。这也就是为什么手动改一改 PATH、把某个新 Python 目录塞在前面经常会遇到各种诡异错误新的解释器也许能启动但它会据此找到旧的标准库路径进而加载到错误版本的标准库模块或者因为找不到 libpython 而直接崩溃。1.3 靠手工改 PATH 换 Python 版本的代价有人会说那我把新下载的 Python 3.12 放在/opt/python312然后修改~/.bashrc里的 PATH不也能做到“切换版本”吗短时间看好像可行但长期维护会很难受。你安装的第三方包会被塞进新解释器对应的site-packages系统里的旧 Python 和系统管理工具使用的还是旧路径两条链交织在一起命令能跑到哪个版本完全取决于当前 shell 从哪里开始查找。一旦你装了一个依赖系统 Python 的工具而它错误地指向了新路径就会平白多出无数排查时间。所以结论很清楚仅靠 venv 或 PATH能解决包的隔离但解决不了解释器版本的隔离。真正要做到“不同项目用不同 Python 版本”必须把解释器本体纳入可管理的范围。2. conda 的思路把 Python 解释器也变成“可以装进环境的包”2.1 环境目录树里有什么还是先看行为。当你执行conda create -n py311 python3.11conda 会在安装目录下的envs/py311里生成一棵完整的目录树bin/python真实的解释器可执行文件或者是环境内部指向同目录解释器的硬链接lib/libpython3.11*Python 的动态库lib/python3.11/这个版本的标准库include/python3.11/配套的头文件conda-meta/记录这个环境安装过哪些包的元数据状态文件也在里边。注意这里的 Python 是完整的一套不是外部软链接。它就像是在环境目录里“解压安装”了一个全新的解释器。既然解释器本体已经在环境内那么python --version显示的版本自然就由这个环境决定你造 3.9 就是 3.9造 3.12 就是 3.12互不冲突。2.2 包管理的单位不是 Python 包而是“带元数据的软件包”conda 和 pip 最大的理念差异在于“包”的定义。pip 管理的包绝大多数是 Python 库包含.py文件和一些编译产物依赖关系靠包内的元数据表达。而 conda 管理的是更广义的软件包它可以是一个 Python 解释器、一个 C 库、一个编译器工具链甚至是一套完整的科学计算运行时。每个 conda 包都有明确的版本、构建号和依赖声明。因此 conda 的包索引里既有python-3.11.8这样的条目也有python-3.12.4这样的条目。当用户指定python3.11时conda 不是去“切换”当前系统的某个 Python而是在软件仓库里选一个符合版本条件的解释器包把它安装到新建的环境目录中。这就像一个应用商店你可以同时下载到不同版本的同一个软件分别安装到不同沙盒里而不是只能操作电脑上已经安装好的那一个。2.3 每个环境都是一座独立的小岛conda 环境的独立性不只是“目录不同”。因为解释器、标准库、第三方库、动态依赖都在环境内它几乎不依赖外部系统状态。你可以在同一台机器的 conda 目录下同时存在envs/py39和envs/py311两个环境前者装着为 Python 3.9 编译的包后者装着为 Python 3.11 编译的包两者互不干扰。这种“完整运行时栈”的模型比单纯托管 Python 包要重不少但也正是因为重它才能做到很多轻量方案做不到的事。后面我们会看到这个模型对依赖解析的要求也会高得多。3. 从一条命令看版本解析的全过程3.1 模型构建给出一系列约束条件当你写下conda create -n py311 python3.11时conda 其实收到的是一个很模糊的目标创建一个环境里面要装 Python 3.11但 3.11 是什么后面跟着哪些依赖包列表起初是空的。conda 做的第一件事是读取配置的 channel默认源或 conda-forge 等中的repodata.json这是所有可用软件包的清单。然后启动依赖解析器相当于把这些包全部放入一个巨大的约束求解问题中。它要同时满足环境中必须安装pythonpython的版本必须匹配 3.11 这个约束每个包声明的依赖项如libgcc、openssl、zlib都能找到可兼容的版本不存在依赖冲突。这里有个很实用的小知识conda 自带软件包大多是以“解释器版本构建号”命名的比如numpy-1.26.4-py311_0就是专门为 Python 3.11 编译的版本。求解器必须把 Python 版本和所有这些依赖包版本放到一起求解绝不会出现把一个为 Python 3.12 编译的二进制包塞进 Python 3.11 环境的情况。3.2 下载与链接pkgs 缓存、硬链接和环境内的最终落地求出可行方案之后conda 会检查本地缓存目录一般位于~/miniconda3/pkgs或~/anaconda3/pkgs看看这些包是否已经下载过。如果缓存里已经有完全相同的版本就直接使用没有才联网下载。包下载完成后conda 会解压它们并把文件链接到目标环境目录。为了节省空间和时间conda 优先采用硬链接多个环境里如果引用同一个版本的同一个包磁盘上只存一份真实文件环境目录里只是对应目录项的多个引用。这是 conda 多环境模型里非常优雅的一点环境之间逻辑上隔离物理上却能共享内容。当你删除某个环境时只是删掉这些目录项真正释放磁盘空间得等缓存清理。如果使用容器类比你会发现 conda 的环境更像“基于相同文件系统的多个挂载视图”而不是完全孤立的沙箱。这种行为对用户来说基本透明但理解它有助于解释为什么同一个包创建多个环境会快很多。3.3 “锁定”在环境中后版本还能不能改创建完成后conda-meta里会留下每个包的 JSON 文件和一段安装历史。这些元数据是 conda 后续维护环境的依据它知道环境当前处于什么状态知道某个包是哪个版本、从哪个 channel 来的。但这不意味着环境被永久冻结。你执行conda install python3.12时conda 会重新读入当前元数据把“python 版本必须匹配 3.12”这个新约束加进去再次启动整套求解流程。原有的依赖包如果和 Python 3.12 冲突求解器会尝试升级或替换如果找不到可行方案它就会报错告诉你哪些包造成了冲突。这种设计也让“改 Python 版本”成为一个有成本的完整重算过程而不是改一个配置文件就完事。理解了求解器的工作方式之后你就会明白conda 之所以能创建不同 Python 版本的环境是因为每一条pythonxxx都会被作为软件包的真实约束参与求解。4. 为什么激活环境后 Python 版本就变了4.1 环境激活和 PATH 的相互作用很多人对“激活”有误解以为它执行了什么魔法般的东西。其实 conda activate 做的主要事情就是把当前环境的bin目录插入到进程PATH的最前面并设置一些环境变量比如CONDA_PREFIX、CONDA_DEFAULT_ENV。当你在激活状态下输入pythonshell 会从 PATH 的第一个目录开始查找可执行文件。由于环境自身的bin排在最前找到的自然是这个环境里的 Python。整个过程和“终端默认使用哪个 Python”本质上是一样的查找逻辑只不过 conda 把查找目标从“系统目录”换成了“环境目录”。之所以要区分这一点是因为很多人把“理解 conda 核心原理”想得太玄。你只要记住conda 的核心能力是管理“环境内应该存在哪些软件文件”而激活只是决定当前 shell 用哪一套文件。4.2 验证一用三个环境演示版本切换我在终端里做一个小实验。先创建两个环境分别指定不同 Python 版本然后逐个激活并查看版本conda create -y -n py39 python3.9 conda create -y -n py311 python3.11 conda activate py39 python --version python -c import sys; print(sys.prefix); print(sys.base_prefix) conda activate py311 python --version python -c import sys; print(sys.prefix); print(sys.base_prefix)在 py39 环境中python --version显示 3.9.xsys.prefix指向envs/py39sys.base_prefix通常也指向同一个位置切到 py311 环境后版本变成 3.11.x路径自动切换。这里的sys.prefix已经等于sys.base_prefix看出来的关键点就是当前跑的是环境内完整的解释器不是外部借来的。如果拿 venv 做同样实验你会看到sys.prefix指向 venv 目录但sys.base_prefix仍然指向系统 Python。正是这个差异把 conda 和 venv 彻底区分开来。4.3 版本切换没有干扰系统 Python 的逻辑激活 py311 之后系统里/usr/bin/python3依然是原封不动的。conda 没有修改系统文件没有覆盖系统二进制只是在当前 shell 的环境变量层面做了一张“优先使用哪套解释器”的决策表。所以你在一个终端里激活 Python 3.11 写新项目在另一个终端继续让 Python 3.9 运行旧服务两者可以同时存在。conda 不会像某些安装器那样把新版本直接写进系统 PATH 造成全局污染。这也是它适合做本机多版本并行开发的关键原因。5. 与 pipvenv 相比conda 的隔离方式在哪几个维度完全不同5.1 维度对比表格下面这张表可以帮助你把两类工具的能力边界看得更清楚。对比维度venv pipconda env解释器是否跟随环境否借用外部 Python是环境内独立安装能否随意指定 Python 版本基本不行取决于外部解释器可以python3.11直接指定是否只是 Python 包管理是主要管 Python 二进制包否还能管 C 库、编译器、运行库依赖解析的颗粒度按包逐个安装遇到冲突靠报错求解器整体计算避免版本冲突包来源PyPIconda channel也可加入 PyPI环境复制速度创建快但不含解释器自由度创建稍慢利用缓存后也很快这张表最强的对比点在第一行。很多人以为“conda 环境”就是“更好的 venv”其实 conda 环境自带完整运行时这是一种完全不同级别的隔离。5.2 它为什么不只是“Python 的虚拟环境”因为 conda 的包模型不限于 Python同一个环境里可以安装 Python 解释器、C 编译器、数值库、甚至某些命令行工具。只要这些软件以 conda 包的形式发布在 channel 里conda 就能统一安装、统一升级、统一卸载。这对科学计算特别有价值。很多 Python 库底层依赖 OpenBLAS、MKL、CUDA 这类原生库如果你用 pip 安装常常需要自己手工处理系统级依赖用 conda 安装这些原生依赖会成为包依赖的一部分被自动带上。正因为这样conda 环境里不只隔离了 Python 库还隔离了整个运行时栈。理解这一点你就能明白为什么 conda 不是单纯回答“Python 版本怎么办”而是在回答“整个软件栈怎么保持一致”。5.3 在 conda 环境里用 pip 时的边界不过conda 的包模型也不是银弹尤其是你把 pip 引入环境以后。pip 安装的包并不会写进conda-meta也就是说 conda 根本不知道它们的存在。一旦你执行conda env export导出环境文件这些 pip 包可能丢失如果之后有人用这份文件重建环境就会悄悄少掉一批依赖。更麻烦的是pip 在某些情况下会把包安装到当前 Python 对应的site-packages如果随后 conda 侧对依赖包做了升级有可能破坏因 pip 安装而被覆盖的二进制文件。所以我现在对 conda 环境里 pip 的使用原则很明确能用 conda 装的先装 conda实在找不到的再用 pip并且记录到一个 requirements.txt 里和环境文件一起保存。混用本身没问题怕的是混用后不做记录。6. 多版本共存时最值得注意的几个细节6.1 谨慎调整环境内的 Python 版本前面说过conda install python3.12会触发新一轮完整求解。听起来很方便但实际操作里要非常小心。假设你现在有一个 Python 3.9 的环境里面装了大量已经编译好的第三方包你直接把 python 升到 3.12求解器会尝试把所有依赖包也换成兼容 3.12 的新版本。这个升级往往比你预想的范围更大甚至可能出现某些包新版本还不存在、环境直接无法解析的情况。如果你只是想打一个补丁版本比如把 3.11.6 升到 3.11.8通常风险会小很多因为同小版本内的 ABI 基本稳定。但如果跨了主版本最好先导出当前环境再新建一个环境试验。不要抱着“升一下应该没事”的心态直接在重要环境里操作。6.2 删除环境、清理缓存与磁盘占用conda 环境虽然逻辑隔离却因为硬链接和缓存的存在物理上共享大量文件。当你删除一个环境时命令conda env remove -n 环境名会移除该环境目录但不会自动清空pkgs缓存。如果你长期创建、删除环境会发现磁盘占用越来越大。这时可以用conda clean --all来清除未使用的缓存和临时文件。注意这个操作不会影响现存环境只会删除那些不再被任何环境引用的缓存包。我的习惯是每隔一段时间跑一次避免缓存无限膨胀。6.3 环境导出与复现让一个环境在另一台机器“重生”最常用的文件是environment.yml。但默认的conda env export会把环境中几乎所有包都写进去包括隐式依赖产生一份非常长的清单。交给别人时对方很容易安装缓慢甚至因为平台差异无法复现。更推荐的做法是conda env export --from-history environment.yml这个命令只导出你显式安装过的包。重建时conda env create -f environment.yml会重新解析出完整的依赖树让目标机器得到和当前环境尽量一致的软件栈。如果你同时用了 pip 包就在文件里单独记录一份requirements.txt让重建过程更可控。多版本 Python 并行这件事很多人在 conda 里养成习惯后会觉得“理所当然”但只要亲自动手拆开来看就会发现它背后是一整套和 pip 完全不同的设计解释器打包、依赖求解、环境级隔离、缓存共享。理解这些机制之后你再遇到环境问题就不会只知道“重新创建一个环境试试”而是能判断问题到底出在依赖解析、PATH 顺序还是包来源上。

相关新闻

FPGA入门必做:可逆计数器设计与实现全解析

FPGA入门必做:可逆计数器设计与实现全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 3:02:43 阅读更多 →
机器学习六步工程闭环:从数据清洗到模型解释

机器学习六步工程闭环:从数据清洗到模型解释

简介:本资源是一套面向高校机器学习课程学习者的期末大作业实战合集,覆盖KNN手写数字识别、回归建模、参数与非参数估计、朴素贝叶斯分类、层次聚类及决策树六大核心实验,配套完整可运行代码与规范实验报告,适合课程设计、期末考核…

2026/10/13 3:02:43 阅读更多 →
jstatd原理与实战:JVM远程监控的零侵入诊断方案

jstatd原理与实战:JVM远程监控的零侵入诊断方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 3:02:43 阅读更多 →

最新新闻

软件配置管理实战:从版本控制到变更治理的完整解析

软件配置管理实战:从版本控制到变更治理的完整解析

软件配置管理(SCM)这个东西,刚入行的开发可能觉得它就是“用Git管代码”,干了两三年以后,会发现事情远没有这么简单。我在带团队和做技术管理的这些年里,见过太多次因为配置管理没做好而引发的线上事故、团…

2026/10/13 3:54:57 阅读更多 →
Linux磁盘管理全攻略:分区、挂载、LVM、配额与排障实践

Linux磁盘管理全攻略:分区、挂载、LVM、配额与排障实践

做运维这些年,我越来越发现一个事实:很多看似诡异的问题,最后都能在磁盘管理上找到病灶。要么是分区规划不合理导致空间浪费,要么是挂载配置写错开机直接进不了系统,要么是日志把根分区撑爆才发现当初没做配额。磁盘管…

2026/10/13 3:54:56 阅读更多 →
RAID10 单盘失效降级处理实战指南:从报警到重建全流程解析

RAID10 单盘失效降级处理实战指南:从报警到重建全流程解析

1. 故障场景还原:最先收到的是那封监控报警邮件先说一个我自己的真事儿。某天凌晨两点半,手机连续震了三条,都是某台业务服务器的监控报警,内容很统一:RAID 阵列降级,请立即检查。说实话,凌晨被…

2026/10/13 3:54:56 阅读更多 →
OpenClaw与Dify融合:政企智能体云端管控与端侧执行双向奔赴

OpenClaw与Dify融合:政企智能体云端管控与端侧执行双向奔赴

1. 为什么“云端管控”与“端侧执行”是政企智能体的刚需我做智能体落地这几年,一个很深的感受是:政企项目真正卡脖子的地方,从来不是某一个模型有多聪明,而是“模型跟业务现场之间隔着一堵墙”。墙的一边是集中化的算力、数据和管…

2026/10/13 3:54:56 阅读更多 →
Dify与OpenClaw融合:政企智能体云端管控与端侧执行落地指南

Dify与OpenClaw融合:政企智能体云端管控与端侧执行落地指南

前一阵子有个做政企大模型项目的朋友跟我聊,说他们内部最头疼的其实不是模型效果,而是智能体怎么落地——云端平台搭得很漂亮,可真正干活的现场在用户电脑和业务内网里,数据出不去、系统连不上,再聪明的Agent也只能在沙…

2026/10/13 3:54:56 阅读更多 →
Rust Tokio 多线程调度器架构刨析:任务队列、工作窃取与延迟优化

Rust Tokio 多线程调度器架构刨析:任务队列、工作窃取与延迟优化

我第一次把#[tokio::main]换成裸的 multi_thread runtime 手动调参时,心里其实完全没底:worker 线程设多了会不会反而更慢?为什么文档里反复强调“不要阻塞 worker 线程”?一个任务到底挂在哪个线程上,真的能控制吗&am…

2026/10/13 3:53:56 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →