Python os模块详解
1. 简介
os
就是“operating system”的缩写,顾名思义,os
模块提供的就是各种 Python 程序与操作系统进行交互的接口。通过使用os
模块,一方面可以方便地与操作系统进行交互,另一方面页也可以极大增强代码的可移植性。如果该模块中相关功能出错,会抛出OSError
异常或其子类异常。
注意,如果是读写文件的话,建议使用内置函数
open()
;如果是路径相关的操作,建议使用os
的子模块os.path
;如果要逐行读取多个文件,建议使用fileinput
模块;要创建临时文件或路径,建议使用tempfile
模块;要进行更高级的文件和路径操作则应当使用shutil
模块。
当然,使用os
模块可以写出操作系统无关的代码并不意味着os
无法调用一些特定系统的扩展功能,但要切记一点:一旦这样做就会极大损害代码的可移植性。
此外,导入os
模块时还要小心一点,千万不要为了图调用省事儿而将os
模块解包导入,即不要使用from os import *
来导入os
模块;否则os.open()
将会覆盖内置函数open()
,从而造成预料之外的错误。
2. 常用功能
注意,
os
模块中大多数接受路径作为参数的函数也可以接受“文件描述符”作为参数。文件描述符:file descriptor,在 Python 文档中简记为 fd,是一个与某个打开的文件对象绑定的整数,可以理解为该文件在系统中的编号。
2.1 os.name
该属性宽泛地指明了当前 Python 运行所在的环境,实际上是导入的操作系统相关模块的名称。这个名称也决定了模块中哪些功能是可用的,哪些是没有相应实现的。
目前有效名称为以下三个:posix
,nt
,java
。
其中posix
是 Portable Operating System Interface of UNIX(可移植操作系统接口)的缩写。Linux 和 Mac OS 均会返回该值;nt
全称应为“Microsoft Windows NT”,大体可以等同于 Windows 操作系统,因此 Windows 环境下会返回该值;java
则是 Java 虚拟机环境下的返回值。
因此在我的电脑(win10)上执行下述代码,返回值是nt
:
>>> import os
>>> os.name
'nt'
而在 WSL(Windows Subsystem Linux,Windows 下的 Linux 子系统)上的结果则是:
>>> import os
>>> os.name
'posix'
查看
sys
模块中的sys.platform
属性可以得到关于运行平台更详细的信息,在此不再赘述
2.2 os.environ
os.environ
属性可以返回环境相关的信息,主要是各类环境变量。返回值是一个映射(类似字典类型),具体的值为第一次导入os
模块时的快照;其中的各个键值对,键是环境变量名,值则是环境变量对应的值。在第一次导入os
模块之后,除非直接修改os.environ
的值,否则该属性的值不再发生变化。
比如其中键为“HOMEPATH”(Windows 下,Linux 下为“HOME”)的项,对应的值就是用户主目录的路径。Windows 下,其值为:
>>> os.environ["HOMEPATH"]
'd:\\justdopython'
Linux 下,其值为:
>>> os.environ["HOME"]
'/home/justdopython'
2.3 os.walk()
这个函数需要传入一个路径作为top
参数,函数的作用是在以top
为根节点的目录树中游走,对树中的每个目录生成一个由(dirpath, dirnames, filenames)
三项组成的三元组。
其中,dirpath
是一个指示这个目录路径的字符串,dirnames
是一个dirpath
下子目录名(除去“.”
和“..”
)组成的列表,filenames
则是由dirpath
下所有非目录的文件名组成的列表。要注意的是,这些名称并不包含所在路径本身,要获取dirpath
下某个文件或路径从top
目录开始的完整路径,需要使用os.path.join(dirpath, name)
。
注意最终返回的结果是一个迭代器,我们可以使用for
语句逐个取得迭代器的每一项:
>>> for item in os.walk("."):
... print(item)
...
('.', ['do'], ['go_go_go.txt'])
('.\\do', ['IAmDirectory', 'python'], [])
('.\\do\\IAmDirectory', [], [])
('.\\do\\python', [], ['hello_justdopython.txt'])
目录树结构
2.4 os.listdir()
“listdir”即“list directories”,列出(当前)目录下的全部路径(及文件)。该函数存在一个参数,用以指定要列出子目录的路径,默认为“.”
,即“当前路径”。
函数返回值是一个列表,其中各元素均为字符串,分别是各路径名和文件名。
通常在需要遍历某个文件夹中文件的场景下极为实用。
比如定义以下函数:
def get_filelists(file_dir='.'):
list_directory = os.listdir(file_dir)
filelists = []
for directory in list_directory:
# os.path 模块稍后会讲到
if(os.path.isfile(directory)):
filelists.append(directory)
return filelists
该函数的返回值就是当前目录下所有文件而非文件夹的名称列表。
2.5 os.mkdir()
“mkdir”,即“make directory”,用处是“新建一个路径”。需要传入一个类路径参数用以指定新建路径的位置和名称,如果指定路径已存在,则会抛出FileExistsError
异常。
该函数只能在已有的路径下新建一级路径,否则(即新建多级路径)会抛出FileNotFoundError
异常。
相应地,在需要新建多级路径的场景下,可以使用os.makedirs()
来完成任务。函数os.makedirs()
执行的是递归创建,若有必要,会分别新建指定路径经过的中间路径,直到最后创建出末端的“叶子路径”。
示例如下:
>>> os.mkdir("test_os_mkdir")
>>> os.mkdir("test_os_mkdir")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileExistsError: [WinError 183] 当文件已存在时,无法创建该文件。: 'test_os_mkdir'
>>>
>>> os.mkdir("test_os_mkdir/test_os_makedirs/just/do/python/hello")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [WinError 3] 系统找不到指定的路径。: 'test_os_mkdir/test_os_makedirs/just/do/python/hello'
>>>
>>> os.makedirs("test_os_mkdir/test_os_makedirs/just/do/python/hello")
2.6 os.remove()
用于删除文件,如果指定路径是目录而非文件的话,就会抛出IsADirectoryError
异常。删除目录应该使用os.rmdir()
函数。
同样的,对应于os.makedirs()
,删除路径操作os.rmdir()
也有一个递归删除的函数os.removedirs()
,该函数会尝试从最下级目录开始,逐级删除指定的路径,几乎就是一个os.makedirs()
的逆过程;一旦遇到非空目录即停止。
2.7 os.rename()
该函数的作用是将文件或路径重命名,一般调用格式为os.rename(src, dst)
,即将src
指向的文件或路径重命名为dst
指定的名称。
注意,如果指定的目标路径在其他目录下,该函数还可实现文件或路径的“剪切并粘贴”功能。但无论直接原地重命名还是“剪切粘贴”,中间路径都必须要存在,否则就会抛出FileNotFoundError
异常。如果目标路径已存在,Windows 下会抛出FileExistsError
异常;Linux 下,如果目标路径为空且用户权限允许,则会静默覆盖原路径,否则抛出OSError
异常,
和上两个函数一样,该函数也有对应的递归版本os.renames()
,能够创建缺失的中间路径。
注意,这两种情况下,如果函数执行成功,都会调用os.removedir()
函数来递归删除源路径的最下级目录。
2.8 os.getcwd()
“getcwd”实际上是“get the current working directory”的简写,顾名思义,也就是说这个函数的作用是“获取当前工作路径”。在程序运行的过程中,无论物理上程序在实际存储空间的什么地方,“当前工作路径”即可认为是程序所在路径;与之相关的“相对路径”、“同目录下模块导入”等相关的操作均以“当前工作路径”为准。
在交互式环境中,返回的就是交互终端打开的位置;而在 Python 文件中,返回的则是文件所在的位置。
在 Windows 下会有如下输出:
>>> os.getcwd()
'd:\\justdopython\\just\\do\\python'
Linux 下的输出则是:
>>> os.getcwd()
'/home/justdopython/just/do/python'
2.9 os.chdir()
“chdir”其实是“change the directory”的简写,因此os.chdir()
的用处实际上是切换当前工作路径为指定路径。其中“指定路径”需要作为参数传入函数os.chdir()
,该参数既可以是文本或字节型字符串,也可以是一个文件描述符,还可以是一个广义的类路径(path-like)对象。若指定路径不存在,则会抛出FileNotFoundError
异常。
在 Windows 下,调用该函数的效果为:
>>> os.chdir("d:/justdopython/just/do")
>>> os.getcwd()
'd:\\justdopython\\just\\do'
在 Linux 下的效果则是:
>>> os.chdir("/home/justdopython/just/do") # 也可将参数指定为"..",即可切换到父目录
>>> os.getcwd()
'/home/justdopython/just/do'
有了这个函数,跨目录读写文件和调用模块就会变得非常方便了,很多时候也就不必再反复将同一个文件在各个目录之间复制粘贴运行,脚本完全可以坐镇中军,在一个目录下完成对其他目录文件的操作,正所谓“运筹帷幄之中,决胜于千里之外”也。
举例来说,可以通过将“当前工作目录”切换到父目录,从而直接访问父目录的文件内容:
>>> os.chdir("..")
>>> os.getcwd()
'D:\\justdopython\\just'
>>> with open("hello_justdopython.txt", encoding="utf-8") as f:
... f.read()
...
'欢迎访问 justdopython.com,一起学习 Python 技术~'
>>> os.listdir()
['hello_justdopython.txt']
3. os.path 模块
其实这个模块是os
模块根据系统类型从另一个模块导入的,并非直接由os
模块实现,比如os.name
值为nt
,则在os
模块中执行import ntpath as path
;如果os.name
值为posix
,则导入posixpath
。
使用该模块要注意一个很重要的特性:os.path
中的函数基本上是纯粹的字符串操作。换句话说,传入该模块函数的参数甚至不需要是一个有效路径,该模块也不会试图访问这个路径,而仅仅是按照“路径”的通用格式对字符串进行处理。
更进一步地说,os.path
模块的功能我们都可以自己使用字符串操作手动实现,该模块的作用是让我们在实现相同功能的时候不必考虑具体的系统,尤其是不需要过多关注文件系统分隔符的问题。
3.1 os.path.join()
这是一个十分实用的函数,可以将多个传入路径组合为一个路径。实际上是将传入的几个字符串用系统的分隔符连接起来,组合成一个新的字符串,所以一般的用法是将第一个参数作为父目录,之后每一个参数即是下一级目录,从而组合成一个新的符合逻辑的路径。
但如果传入路径中存在一个“绝对路径”格式的字符串,且这个字符串不是函数的第一个参数,那么其他在这个参数之前的所有参数都会被丢弃,余下的参数再进行组合。更准确地说,只有最后一个“绝对路径”及其之后的参数才会体现在返回结果中。
>>> os.path.join("just", "do", "python", "dot", "com")
'just\\do\\python\\dot\\com'
>>>
>>> os.path.join("just", "do", "d:/", "python", "dot", "com")
'd:/python\\dot\\com'
>>>
>>> os.path.join("just", "do", "d:/", "python", "dot", "g:/", "com")
'g:/com'
3.2 os.path.abspath()
将传入路径规范化,返回一个相应的绝对路径格式的字符串。
也就是说当传入路径符合“绝对路径”的格式时,该函数仅仅将路径分隔符替换为适应当前系统的字符,不做其他任何操作,并将结果返回。所谓“绝对路径的格式”,其实指的就是一个字母加冒号,之后跟分隔符和字符串序列的格式:
>>> os.path.abspath("a:/just/do/python")
'a:\\just\\do\\python'
>>> # 我的系统中并没有 a 盘
当指定的路径不符合上述格式时,该函数会自动获取当前工作路径,并使用os.path.join()
函数将其与传入的参数组合成为一个新的路径字符串。示例如下:
>>> os.path.abspath("ityouknow")
'D:\\justdopython\\ityouknow'
3.3 os.path.basename()
该函数返回传入路径的“基名”,即传入路径的最下级目录。
>>> os.path.basename("/ityouknow/justdopython/IAmBasename")
'IAmBasename'
>>> # 我的系统中同样没有这么一个路径。可见 os.path.basename() 页也是单纯进行字符串处理
整这个函数要注意的一点是,返回的“基名”实际上是传入路径最后一个分隔符之后的子字符串,也就是说,如果最下级目录之后还有一个分隔符,得到的就会是一个空字符串:
>>> os.path.basename("/ityouknow/justdopython/IAmBasename/")
''
3.4 os.path.dirname()
与上一个函数正好相反,返回的是最后一个分隔符前的整个字符串:
>>> os.path.dirname("/ityouknow/justdopython/IAmBasename")
'/ityouknow/justdopython'
>>>
>>> os.path.dirname("/ityouknow/justdopython/IAmBasename/")
'/ityouknow/justdopython/IAmBasename'
3.5 os.path.split()
哈哈 ,实际上前两个函数都是弟弟,这个函数才是老大。
函数os.path.split()
的功能就是将传入路径以最后一个分隔符为界,分成两个字符串,并打包成元组的形式返回;前两个函数os.path.dirname()
和os.path.basename()
的返回值分别是函数os.path.split()
返回值的第一个、第二个元素。就连二者的具体实现都十分真实:
def basename(p):
"""Returns the final component of a pathname"""
return split(p)[1]
def dirname(p):
"""Returns the directory component of a pathname"""
return split(p)[0]
通过os.path.join()
函数又可以把它们组合起来得到原先的路径。
3.6 os.path.exists()
这个函数用于判断路径所指向的位置是否存在。若存在则返回True
,不存在则返回False
:
>>> os.path.exists(".")
True
>>> os.path.exists("./just")
True
>>> os.path.exists("./Inexistence") # 不存在的路径
False
一般的用法是在需要持久化保存某些数据的场景,为避免重复创建某个文件,需要在写入前用该函数检测一下相应文件是否存在,若不存在则新建,若存在则在文件内容之后增加新的内容。
3.7 os.path.isabs()
该函数判断传入路径是否是绝对路径,若是则返回True
,否则返回False
。当然,仅仅是检测格式,同样不对其有效性进行任何核验:
>>> os.path.isabs("a:/justdopython")
True
3.8 os.path.isfile() 和 os.path.isdir()
这两个函数分别判断传入路径是否是文件或路径,注意,此处会核验路径的有效性,如果是无效路径将会持续返回False
。
>>> # 无效路径
>>> os.path.isfile("a:/justdopython")
False
>>>
>>> # 有效路径
>>> os.path.isfile("./just/plain_txt")
True
>>>
>>> # 无效路径
>>> os.path.isdir("a:/justdopython/")
False
>>> # 有效路径
>>> os.path.isdir("./just/")
True
总结
本文详细介绍了与操作系统交互的os
模块中一些常用的属性和函数,基本可以覆盖初阶的学习和使用。有了这些功能,我们已经可以写出一些比较实用的脚本了。
相关文章:

Python os模块详解
1. 简介 os就是“operating system”的缩写,顾名思义,os模块提供的就是各种 Python 程序与操作系统进行交互的接口。通过使用os模块,一方面可以方便地与操作系统进行交互,另一方面页也可以极大增强代码的可移植性。如果该模块中相…...

Oracle PL/SQL基础语法学习13:比较运算符
系列文章目录 Oracle PL/SQL基础语法学习12:短路求值 Oracle PL/SQL基础语法学习13:比较运算符 Oracle PL/SQL基础语法学习14:BOOLEAN表达式 文章目录 系列文章目录Oracle PL/SQL基础语法学习13:比较运算符比较运算符介绍官方文档…...

金仓数据库适配记录
金仓数据库适配记录 人大金仓数据库管理系统KingbaseES(简称:金仓数据库或KingbaseES)是北京人大金仓信息技术股份有限公司自主研制开发的具有自主知识产权的通用关系型数据库管理系统。 金仓数据库主要面向事务处理类应用,兼顾各类数据分析类应用,可用做管理信息系统、…...

ElasticSearch 学习 ==ELK== 进阶
二、ElasticSearch 学习 ELK 进阶 (1)文档局部更新 我们也说过文档是不可变的——它们不能被更改,只能被替换。 update API必须遵循相同的规则。表面看来,我们似乎是局部更新了文档的位置,内部却是像我们之前说的一样…...

【数据结构 -- C语言】 双向带头循环链表的实现
目录 1、双向带头循环链表的介绍 2、双向带头循环链表的接口 3、接口实现 3.1 开辟结点 3.2 创建返回链表的头结点 3.3 判断链表是否为空 3.4 打印 3.5 双向链表查找 3.6 双向链表在pos的前面进行插入 3.6.1 头插 3.6.2 尾插 3.6.3 更新头插、尾插写法 3.7 双向链…...

自然语言处理与其Mix-up数据增强方法报告
自然语言处理与其Mix-up数据增强方法 1绪论1.课题背景与意义1.2国内外研究现状 2 自然语言经典知识简介2.1 贝叶斯算法2.2 最大熵模型2.3神经网络模型 3 Data Augmentation for Neural Machine Translation with Mix-up3.1 数据增强3.2 对于神经机器翻译的软上下文的数据增强3.…...

Vue(组件化编程:非单文件组件、单文件组件)
一、组件化编程 1. 对比传统编写与组件化编程(下面两个解释图对比可以直观了解) 传统组件编写:不同的HTML引入不同的样式和行为文件 组件方式编写:组件单独,复用率高(前提组件拆分十分细致) 理…...

【MATLAB数据处理实用案例详解(22)】——基于BP神经网络的PID参数整定
目录 一、问题描述二、算法仿真2.1 BP_PID参数整定初始化2.2 优化PID2.3 绘制图像 三、运行结果四、完整程序 一、问题描述 基于BP神经网络的PID控制的系统结构如下图所示: 考虑仿真对象,输入为r(k)1.0,输入层为4,隐藏层为5&…...

第11章 项目人力资源管理
文章目录 项目人力资源管理 过程11.2.1 编制项目人力资源计划的工具与技术(1)层次结构图(工作、组织、资源 分解结构)(2)矩阵图(责任分配矩阵,RAM)(3…...

07-Vue技术栈之(组件之间的通信方式)
目录 1、组件的自定义事件1.1 绑定自定义事件:1.1.1 第一种方式1.1.2 第二种方式1.1.3 自定义事件只触发一次 1.2 解绑自定义事件1.3绑定原生DOM事件1.4 总结 2、全局事件总线(GlobalEventBus)2.1 应用全局事件总线 3、 消息订阅与发布&#…...

度量学习Metirc Learning和基于负例的对比学习Contrastive Learning的异同点思考
参考:对比学习(Contrastive Learning):研究进展精要 - 知乎 参考:对比学习论文综述【论文精读】_哔哩哔哩_bilibili 参考:度量学习DML之Contrastive Loss及其变种_对比损失的变种_胖胖大海的博客-CSDN博客 参考&…...

3.编写油猴脚本之-helloword
3.编写油猴脚本之-helloword Start 通过上一篇文章的学习,我们安装完毕了油猴插件。今天我们来编写一个helloword的脚步,体验一下油猴。 1. 开始 点击油猴插件>添加新脚本 默认生成的脚本 // UserScript // name New Userscript // name…...

openwrt的openclash提示【更新失败,请确认设备闪存空间足够后再试】
网上搜索了一下,问题应该是出在“无法从网络下载内核更新包”或者“无法识别内核的版本号” 解决办法:手动下载(我是只搞了DEV内核就搞定了TUN和Meta没有动) --> 上传到路由器上 --> 解压缩 --> 回到openclash界面更新配…...

torch.nn.Module
它是所有的神经网络的根父类! 你的神经网络必然要继承 可以看一下这篇文章...

论文解析-基于 Unity3D 游戏人工智能的研究与应用
1.重写 AgentAction 方法 1.1 重写 AgentAction 方法 这段代码是一个重写了 AgentAction 方法的方法。以下是对每行代码解释: ①public override void AgentAction(float[] vectorAction) 这行代码声明了一个公共的、重写了父类的 AgentAction 方法的方法。它接受…...

6、Flutterr聊天界面网络请求
一、准备网络数据 1.1 数据准备工作 来到网络数据制造的网址,注册登录后,新建仓库,名为WeChat_flutter;点击进入该仓库,删掉左侧的示例接口,新建接口. 3. 接着点击右上角‘编辑’按钮,新建响应内容,类型为Array,一次生成50条 4. 点击chat_list左侧添加按钮,新建chat_list中的…...

Java 8 腰斩!Java 17 暴涨 430%!!(文末福利)
New Relic 最新发布了一份 “2023 年 Java 生态系统状况报告”,旨在提供有关当今 Java 生态系统状态的背景和见解。该报告基于从数百万个提供性能数据的应用程序中收集的数据,对生产中使用最多的版本、最受欢迎的 JDK 供应商、容器的兴起等多方面进行了调…...

如何手写一个支持H.265的高清播放器
概述 音视频编解码技术在当前的互联网行业中十分热门,特别是高清视频播放器的开发,其中包括4K、8K等超高清分辨率的播放器,具有极高的市场需求和广泛的应用场景。H265编码技术更是实现高清视频压缩的重要手段之一。如果想要掌握音视频编解码…...

Day 1 认识软件测试——(软件测试定义、目的、原则)
Day 1 认识软件测试——(软件测试定义、目的、原则) 文章目录 Day 1 认识软件测试——(软件测试定义、目的、原则)软件测试的定义软件测试的目的软件测试的经济学问题黑盒测试白盒测试软件测试原则小结所谓软件测试,就是一个过程或一系列过程,用来确定计算机代码完成了其…...

Docker Harbor
目录 一、Docker Harbor概述 1、Harbor的优势 2、Harbor知识点 3、Docker私有仓库架构 二、Harbor构建Docker私有仓库 1、环境配置 2、案例需求 3、部署docker-compose服务 4、部署harbor服务 5、启动harbor ① 访问 ② 添加项目并填写项目名称 ③ 通过127.0.0.1来…...

第三十四章 Unity人形动画(上)
在我们DirectX课程中,我们讲过一个模型最少拥有网格和材质,可以没有动画。游戏场景中的静态物体就可以是这样的模型,例如花草树木,建筑物等等,他们通过MeshRenderer就可以渲染。对于一个带有动画的FBX文件,…...

计算机图形学-GAMES101-7
引言 场景中有很多的三角形,如果实现可见性和遮挡呢? 一个简单的想法是,从远到近画,近处的物体自然会覆盖掉远处的物体,这种画法也叫画家算法。 但是实际绘制中物体的顺序是不容易确定的,比如如下图绘制…...

AndroidAuto 解决PCTS NF7
直接上代码 public void handleNavigationFocusRequest(int focusType) {// Always grant requested focus in this example.-mGal.galReceiver.sendNavigationFocusState(focusType);+mGal.galReceiver.sendNavigationFocusState...

GPT:你知道这五年我怎么过的么?
时间轴 GPT 首先最初版的GPT,来源于论文Improving Language Understanding by Generative Pre-Training(翻译过来就是:使用通用的预训练来提升语言的理解能力)。GPT这个名字其实并没有在论文中提到过,后人将论文名最后…...

Python一行命令搭建HTTP服务器并外网访问 - 内网穿透
文章目录 1.前言2.本地http服务器搭建2.1.Python的安装和设置2.2.Python服务器设置和测试 3.cpolar的安装和注册3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 转载自远程内网穿透的文章:【Python】快速简单搭建HTTP服务器并公网访问「cpolar内网穿透…...

TypeScript5-泛型
泛型是 TS 中一个重要的概念,它可以创建可复用的组件,同时保持对类型信息的一致性。 泛型提供了一种方式使得类型可以被参数化,这样就可以创建可以适用于各种数据类型的函数或类,而不仅仅限于一个数据类型。 一、泛型 先来看一…...

IMX6ULL裸机篇之DDR3的时钟配置
一. MMDC 控制器 对于 I.MX6U 来说,有 DDR 内存控制器,否则的话它怎么连接 DDR 呢?MMDC控制器 就是 I.MX6U 的 DDR内存控制器。 MMDC 外设包含一个内核(MMDC_CORE)和 PHY(MMDC_PHY),内核和 PHY 的功能如下: MMDC 内…...

PBDB Data Service:Specimens and measurements(标本和测量)
Specimens and measurements(标本和测量) 描述摘要1. [Single specimen(单个标本)](https://blog.csdn.net/whitedrogen/article/details/130685099)2. [Add specimen records or update existing records(添加标本记录…...

Zookeeper(一)
简介 设计模式角度 Zookeeper:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那…...

Maven(五):Maven的使用——依赖的测试
Maven(五):Maven的使用——依赖的测试 前言一、实验六:测试依赖的范围1、依赖范围1.1 compile 和 test 对比1.2 compile 和 provided 对比1.3 结论 二、实验七:测试依赖的传递性1、依赖的传递性1.1 概念1.2 传递的原则…...