跳转至

6.10.os.path

import os
Windows 10
Python 3.7.3 @ MSC v.1915 64 bit (AMD64)
Latest build date 2020.04.28

os标准模块只有一个子模块——os.path模块。os.path 模块提供了跨平台处理文件路径的函数。实际上,在windows系统上,os.path模块是ntpath模块。这些处理路径的函数并不依赖于真实存在的路径,本质上它们只是操作字符串对象。

os.path模块定义了一些变量,os.path对路径字符串的操作依赖于这些变量。

explanation code output
路径各部分的分隔符 os.path.sep \
路径各部分的可选分隔符 os.path.altsep /
系统变量中PATH变量的分隔符 os.path.pathsep ;
文件名和扩展名之间的分隔符 os.path.extsep .
表示当前目录的路径组件 os.path.curdir .
表示上级目录的路径组件 os.path.pardir ..
系统默认搜索路径 os.path.defpath .;C:\bin
NULL设备路径 os.path.devnull nul
是否支持任意Unicode字符用作文件名 os.path.supports_unicode_filenames TRUE

拆分路径

函数 示例 说明
.basename() C:\one\two\threethree 返回路径的最后一层
.dirname() C:\one\two\threeC:\one\two 返回路径的第一层至倒数第二层
.splitext() one.txtonetxt 根据os.extsep分割路径字符串
.splitdrive() C:/oneC:/one 分割路径的盘符和其余部分

.basename()返回路径的最后一层

os.path.basename(r"C:\one\two\three")
# 等价于
# r"C:\one\two\three".rsplit("\\", maxsplit=1)[1]
'three'

如果路径是以目录分隔符os.sep结尾,那么这个路径的基础部分将被认为是空的:

os.path.basename(r"C:/one/two/three/")
''

.dirname()返回路径的第一层到倒数第二层:

os.path.dirname(r"C:\one\two\three")
# 等价于
# "C:/one/two/three/".rsplit("/", maxsplit=1)[0]
'C:\\one\\two'

basename()dirname() 的结果组合起来,就会得到原始的路径。

使用.split可以同时得到basename()dirname()的结果:

print(os.path.split(p=r"C:\one\two\three"))
print(os.path.split(p=r"C:\one\two\three.txt"))
('C:\\one\\two', 'three')
('C:\\one\\two', 'three.txt')

.splitext()根据os.extsep分割路径字符串:

print(os.path.splitext(p=r"C:/one/two/three.txt"))
# 等价于
print(r"C:/one/two/three.txt".rsplit(".", maxsplit=1))
('C:/one/two/three', '.txt')
['C:/one/two/three', 'txt']

查找文件扩展名的时,只使用最后一次出现的os.extsep

print(os.path.splitext(p=r"C:/one/two/three.txt.md"))
('C:/one/two/three.txt', '.md')

.splitdrive()将路径的盘符和其余部分分开:

print(os.path.splitdrive(p=r"C:/one/two/three.txt"))
print(os.path.splitdrive(p=r"/one/two/three.txt"))
('C:', '/one/two/three.txt')
('', '/one/two/three.txt')

构建路径

函数 示例 说明
.join() onetwo'one\\two 拼接路径
.expanduser() ~C:\\Users\\Username ~替换为用户目录
.expandvars() $Env_varEnv_var $Env_var替换为环境变量的值

os.path.join可以将多个字符串拼接为一个路径。如果某个参数是以 os.sep 开头,那么它之前的所有参数都将被丢弃,并且返回值将以它作为新的开始。

PATHS = [
    ('one', 'two', 'three'),
    ('/', 'one', 'two', 'three'),
    ('/one', '/two', '/three'),
]

for parts in PATHS:
    print('{0:<30} : {1!r}'.format(str(parts), os.path.join(*parts)))
('one', 'two', 'three')        : 'one\\two\\three'
('/', 'one', 'two', 'three')   : '/one\\two\\three'
('/one', '/two', '/three')     : '/three'

.expanduser() 将会转化波浪号~为用户主目录的名称。如果没有找到用户的主目录,那么这个字符串将被原样返回。

for user in ['', 'dhellmann', 'nosuchuser']:
    lookup = '~' + user
    print('{!r:>15} : {!r}'.format(
        lookup, os.path.expanduser(lookup)))
            '~' : 'C:\\Users\\Android'
   '~dhellmann' : 'C:\\Users\\dhellmann'
  '~nosuchuser' : 'C:\\Users\\nosuchuser'

.expandvars() 更一般化,它将会解析路径中所有 shell 环境变量。

os.environ['MYVAR'] = 'VALUE'
os.path.expandvars('/path/to/$MYVAR')
'/path/to/VALUE'

规范路径

使用 .join() 或者嵌入变量的独立字符串组合的路径结尾可能有额外的路径分隔符或者相对路径组件。使用 .normpath() 来清理他们:

PATHS = [
    'one//two//three',
    'one/./two/./three',
    'one/../alt/two/three',
]

for path in PATHS:
    print('{!r:>22} : {!r}'.format(path, os.path.normpath(path)))
     'one//two//three' : 'one\\two\\three'
   'one/./two/./three' : 'one\\two\\three'
'one/../alt/two/three' : 'alt\\two\\three'

可以发现,os.curdir 和 os.pardir 组成的路径片段将被清除。

使用 abspath() 可以将相对路径转化为绝对路径。实际上,就是将当前工作目录拼接到相对路径之前。

absolute path = os.getcwd() + relative path

使用 relpath() 可以将绝对路径转化为相对路径,也就是将路径字符串中的当前工作目录剔除。

relative path = absolute path.replace(os.getcwd(), "")

使用 normcase() 可以将路径字符串所有字符转为小写,同时将斜杠统一转换为os.sep。

os.path.normcase('User\\Desktop/One')
'user\\desktop\\one'

公共路径

.commonprefix() 函数会接受一个路径列表作为参数而返回一个字符串表示所有路径中的公共前缀。这个字符串可能表示一个实际不存在的路径,因为路径分隔符并没有被考虑在内,所以这个公共前缀可能并没有落在分隔符边界上。

paths = ['/one/two/three/four',
         '/one/two/threefold',
         '/one/two/three/',
         ]

for path in paths:
    print('PATH:', path)

print()
print('PREFIX:', os.path.commonprefix(paths))
PATH: /one/two/three/four
PATH: /one/two/threefold
PATH: /one/two/three/

PREFIX: /one/two/three

.commonpath() 充分考虑路径分隔符,会返回路径序列中各个部分的最长公共有效子路径。

paths = ['/one/two/three/four',
         '/one/two/threefold',
         '/one/two/three/',
         ]
for path in paths:
    print('PATH:', path)

print()
print('PREFIX:', os.path.commonpath(paths))
PATH: /one/two/three/four
PATH: /one/two/threefold
PATH: /one/two/three/

PREFIX: \one\two

文件信息

函数 说明
.getctime() 返回文件或文件夹的创建时间
.getmtime() 返回文件或文件夹的修改时间
.getatime() 返回文件或文件夹的访问时间
.getsize() 返回文件或文件夹的大小(以字节为单位)

检查路径

函数 说明
.samefile(path1, path2) 判断目录或文件是否相同(不是真实的路径,会报错)
.sameopenfile(fp1, fp2) 判断fp1和fp2是否指向同一文件
.samestat(stat1, stat2) 判断stat tuple stat1和stat2是否指向同一个文件
.exists(path) 检查路径是否存在
.lexists(path) 检查路径是否存在,对于broken symbolic links也返回True
.isabs(path) 判断是否为绝对路径
.isfile(path) 判断路径是否为文件
.isdir(path) 判断路径是否为目录
.islink(path) 判断路径是否为symbolic link,Windows系统下总是返回False
.ismount(path) 判断路径是否为挂载点
FILENAMES = [
    __file__,
    os.path.dirname(__file__),
    '/',
    './broken_link',
]

for file in FILENAMES:
    print('File        : {!r}'.format(file))
    print('Absolute    :', os.path.isabs(file))
    print('Is File?    :', os.path.isfile(file))
    print('Is Dir?     :', os.path.isdir(file))
    print('Is Link?    :', os.path.islink(file))
    print('Mountpoint? :', os.path.ismount(file))
    print('Exists?     :', os.path.exists(file))
    print('Link Exists?:', os.path.lexists(file))
    print()
File        : os.getcwd() + '8.10.标准库_os_path.py'
Absolute    : True
Is File?    : True
Is Dir?     : False
Is Link?    : False
Mountpoint? : False
Exists?     : True
Link Exists?: True

File        : os.getcwd()
Absolute    : True
Is File?    : False
Is Dir?     : True
Is Link?    : False
Mountpoint? : False
Exists?     : True
Link Exists?: True

File        : '/'
Absolute    : True
Is File?    : False
Is Dir?     : True
Is Link?    : False
Mountpoint? : True
Exists?     : True
Link Exists?: True

File        : './broken_link'
Absolute    : False
Is File?    : False
Is Dir?     : False
Is Link?    : False
Mountpoint? : False
Exists?     : False
Link Exists?: False