本文介绍一下Python中进行文件路径处理的一些技巧。从字符串连接、os.path.join()到Python3中处理文件路径的简单方法:pathlib,以及如何获取当前工作路径以及原始脚本路径。

文件路径处理的几种方法

1. 通过字符串连接

import pandas as pd
parent_path = 'parent_path/'
target_path = 'target_path/'
target_file = 'target.csv'
full_path = parent_path + target_path + target_file
df = pd.read_csv(full_path)

这样的方法很简陋,跨平台可能会出现问题,所以不推荐使用。 因为在linux和Windows上路径的分隔符是不一样的。 比如下面代码在Windows上运行OK,但是在Linux或者Mac上就会报错。

import pandas as pd
parent_path = 'parent_path\\'
target_path = 'target_path\\'
target_file = 'target.csv'
full_path = parent_path + target_path + target_file
df = pd.read_csv(full_path)

2. os.path.join()

这是在Python中常用的一种路径拼接方式,支持跨平台。缺点就是语法较为冗长,对于多个路径的拼接,需要把每个路径的字符串传入os.path.join(),这样也不够直观。

import pandas as pd
import os 
parent_path = 'parent_path'
target_path = 'target_path'
target_file = 'target.csv'
full_path = os.path.join(parent_path,target_path,target_file)
df = pd.read_csv(full_path)

3. pathlib

Python 3.4引入了更好的路径处理方式:pathlib!支持不同的操作系统。我们只需要新建一个Path()对象,将路径或者文件传入,然后用/将它们连接即可,pathlib会帮我们做系统判断。

import pandas as pd
from pathlib import Path
full_path = Path(a)/b/c
df = pd.read_csv(full_path)

是不是特别简洁?相比于os.path.join()更加直观。 而且也不需要反复的写os.path.join()了。

4. pathlib更多用法

from pathlib import Path

# 当前工作路径:
Path.cwd()
# PosixPath('/Users/test')

# home路径
Path.home()
# PosixPath('/Users/test')

# 列出当前目录的子目录:
p = Path('.')
[x for x in p.iterdir() if x.is_dir()]

p.iterdir() # 当路径指向一个目录时,产生该路径下的对象的路径

# 将路径绝对化
p.resolve()

# 列出当前目录下所有的`csv`文件:
list(p.glob('**/*.csv'))

# 查看路径是否存在
a = Path('data/data2/Iris.csv')
a.exists() # True
b = Path('data/data2/NoIris.csv')
b.exists() # False

# 判断是否为文件夹
a.is_dir()  # False
p.is_dir() # True
a.is_dir() # True
p.is_file() # False

# 读取文件内容
a.read_text()

# 获取文件名和后缀
print(a.name)
# prints "Iris.csv"
print(a.suffix)
# prints ".csv"

# 分离路径 
a.parts  # ('data', 'data2', 'Iris.csv')

# 修改目录权限
p.chmod(777)  

# 删除目录
path_to_remove = Path('to_remove')
path_to_remove.rmdir()  

5. pathlibos的一些对应关系

os and os.path pathlib
os.path.abspath() Path.resolve()
os.chmod() Path.chmod()
os.mkdir() Path.mkdir()
os.rename() Path.rename()
os.replace() Path.replace()
os.rmdir() Path.rmdir()
os.remove(), os.unlink() Path.unlink()
os.getcwd() Path.cwd()
os.path.exists() Path.exists()
os.path.expanduser() Path.expanduser() andPath.home()
os.path.isdir() Path.is_dir()
os.path.isfile() Path.is_file()
os.path.islink() Path.is_symlink()
os.stat() Path.stat(), Path.owner(),Path.group()
os.path.isabs() PurePath.is_absolute()
os.path.join() PurePath.joinpath()
os.path.basename() PurePath.name
os.path.dirname() PurePath.parent
os.path.samefile() Path.samefile()
os.path.splitext() PurePath.suffix

6. 例子

例1:获取MNIST data

from pathlib import Path
import requests
import pickle
import gzip

DATA_PATH = Path("data")
PATH = DATA_PATH / "mnist"

PATH.mkdir(parents=True, exist_ok=True)

URL = "http://deeplearning.net/data/mnist/"
FILENAME = "mnist.pkl.gz"

if not (PATH / FILENAME).exists():
    content = requests.get(URL + FILENAME).content 
    (PATH / FILENAME).open("wb").write(content)

with gzip.open((PATH / FILENAME).as_posix(), "rb") as f:
    ((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")

例2:判断文件夹是否存在,如果不存在则新建

# 如果文件夹不存在则创建文件夹
def mk_dir(dir_nm):
    import os
    folder = os.path.exists(dir_nm)
    if not folder:
        os.makedirs(dir_nm)           	

获取当前路径及原始脚本路径

假设我们从脚本A.py调用脚本path_test.py,它们的路径分别为:

A.py: /Users/test/python_data_basic/machine_learning/lightgbm/A.py

path_test.py: /Users/test/python_data_basic/utils/path_test.py

# A.py
# /Users/test/python_data_basic/machine_learning/lightgbm/A.py
import os
cur_dir = os.getcwd() # 当前工作路径,如果被其他脚本调用,会返回调用脚本的路径
# 或者:cur_dir = os.path.dirname(__file__)
base_dir = os.path.dirname(os.path.dirname(cur_dir))
import sys
sys.path.append(base_dir)
from utils.path_test import *
# path_test.py
# /Users/test/python_data_basic/utils/path_test.py
import os
import subprocess
from pathlib import Path

# 返回当前工作路径(调用脚本A.py的路径)
print(os.getcwd())
# /Users/test/python_data_basic/machine_learning/lightgbm
print(os.popen("pwd").read().split()[0])
# /Users/test/python_data_basic/machine_learning/lightgbm
print(subprocess.run(['pwd'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.strip().decode('utf-8'))
# /Users/test/python_data_basic/machine_learning/lightgbm
print(Path.cwd())  
# /Users/test/python_data_basic/machine_learning/lightgbm

# 返回被调用脚本的路径
print(Path(__file__).resolve()) 
# /Users/test/python_data_basic/utils/path_test.py
print(Path(__file__).resolve().parent)
# /Users/test/python_data_basic/utils
print(__file__)
# /Users/test/python_data_basic/utils/path_test.py
print(os.path.dirname(__file__))
# /Users/test/python_data_basic/utils

说明:

import os
# 当前脚本目录
os.path.abspath(__file__) 
dir_path = os.path.dirname(os.path.realpath(__file__))

# 当前脚本的上两级目录
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

The only difference I could find is that os.path.realpath resolves symlinks and os.path.abspath doesn’t.

os.path.realpath会解析软链接,而os.path.abspath 不会。例如:

import os 
os.path.realpath("/usr/bin/python")
# '/usr/bin/python2.7'
os.path.abspath("/usr/bin/python")
# '/usr/bin/python'

Reference