Python 获取当前目录的多种方法

目录

概述

[1. 获取当前工作目录(CWD)](#1. 获取当前工作目录(CWD))

[2. 获取当前脚本文件所在目录](#2. 获取当前脚本文件所在目录)

[3. 两种目录的区别](#3. 两种目录的区别)

[4. 实用工具函数](#4. 实用工具函数)

[5. 动态改变当前目录](#5. 动态改变当前目录)

[6. 特殊场景处理](#6. 特殊场景处理)

[7. 实用示例:项目目录管理](#7. 实用示例:项目目录管理)

[8. 性能比较](#8. 性能比较)


概述

在 Python 中,获取当前目录通常指的是两种不同的概念:当前工作目录(Current Working Directory)和当前脚本文件所在目录。下面详细介绍各种方法。

1. 获取当前工作目录(CWD)

当前工作目录是程序执行时所在的目录。

python 复制代码
import os

# 1. 使用 os.getcwd() - 最常用方法
current_working_dir = os.getcwd()
print(f"当前工作目录 (os.getcwd()): {current_working_dir}")

# 2. 使用 os.path 的快捷方式
cwd = os.path.curdir  # 返回 '.',表示当前目录
print(f"当前目录符号 (os.path.curdir): {cwd}")
print(f"转换为绝对路径: {os.path.abspath(cwd)}")

# 3. 使用 pathlib 模块 (Python 3.4+)
from pathlib import Path

cwd_pathlib = Path.cwd()
print(f"当前工作目录 (Path.cwd()): {cwd_pathlib}")
print(f"转换为字符串: {str(cwd_pathlib)}")

# 4. 使用 os.environ 获取 PWD 环境变量(Linux/Mac)
if 'PWD' in os.environ:
    print(f"环境变量 PWD: {os.environ['PWD']}")

# 5. 工作目录操作示例
print("\n工作目录相关操作:")
print(f"目录是否存在: {os.path.exists(current_working_dir)}")
print(f"是否为目录: {os.path.isdir(current_working_dir)}")
print(f"目录下的文件列表: {os.listdir(current_working_dir)[:5]}")  # 显示前5个

2. 获取当前脚本文件所在目录

python 复制代码
import os
import sys

# 1. 使用 __file__ 属性
def get_script_directory():
    """获取当前脚本文件所在目录"""
    # 获取当前文件的绝对路径
    if '__file__' in globals():
        script_path = os.path.abspath(__file__)
        return os.path.dirname(script_path)
    else:
        # 如果在交互式环境或某些特殊情况下
        return os.getcwd()

script_dir = get_script_directory()
print(f"脚本文件所在目录: {script_dir}")

# 2. 使用 pathlib
from pathlib import Path

def get_script_dir_pathlib():
    """使用 pathlib 获取脚本目录"""
    try:
        # 获取当前文件的绝对路径
        script_path = Path(__file__).resolve()
        return script_path.parent
    except NameError:
        # 如果没有 __file__ 属性
        return Path.cwd()

script_dir_pathlib = get_script_dir_pathlib()
print(f"脚本目录 (pathlib): {script_dir_pathlib}")

# 3. 在函数内部获取调用者脚本的目录
def get_caller_directory():
    """获取调用者脚本的目录"""
    import inspect
    
    # 获取调用栈信息
    frame = inspect.currentframe()
    try:
        # 向上回溯一帧,获取调用者的信息
        caller_frame = frame.f_back
        caller_file = caller_frame.f_globals.get('__file__', '')
        
        if caller_file:
            return os.path.dirname(os.path.abspath(caller_file))
        else:
            return os.getcwd()
    finally:
        del frame  # 避免循环引用

print(f"调用者目录: {get_caller_directory()}")

3. 两种目录的区别

python 复制代码
import os

def demonstrate_directory_differences():
    """演示工作目录和脚本目录的区别"""
    
    print("=== 目录类型比较 ===")
    
    # 获取工作目录
    working_dir = os.getcwd()
    print(f"当前工作目录: {working_dir}")
    
    # 获取脚本目录
    script_dir = os.path.dirname(os.path.abspath(__file__)) if '__file__' in globals() else working_dir
    print(f"脚本文件目录: {script_dir}")
    
    # 比较两者是否相同
    if working_dir == script_dir:
        print("✓ 工作目录和脚本目录相同")
    else:
        print("✗ 工作目录和脚本目录不同")
        print(f"   差异: {os.path.relpath(script_dir, working_dir)}")
    
    # 查看父目录
    working_parent = os.path.dirname(working_dir)
    script_parent = os.path.dirname(script_dir)
    
    print(f"\n工作目录的父目录: {working_parent}")
    print(f"脚本目录的父目录: {script_parent}")
    
    # 显示目录结构
    print("\n工作目录内容:")
    for item in sorted(os.listdir(working_dir))[:5]:  # 只显示前5个
        item_path = os.path.join(working_dir, item)
        item_type = "目录" if os.path.isdir(item_path) else "文件"
        print(f"  {item_type}: {item}")

demonstrate_directory_differences()

4. 实用工具函数

python 复制代码
import os
import sys
from pathlib import Path

class DirectoryUtils:
    """目录工具类"""
    
    @staticmethod
    def get_working_dir():
        """获取当前工作目录"""
        return os.getcwd()
    
    @staticmethod
    def get_script_dir():
        """获取脚本文件所在目录"""
        if getattr(sys, 'frozen', False):
            # 如果是打包后的可执行文件
            return os.path.dirname(sys.executable)
        elif '__file__' in globals():
            # 正常Python脚本
            return os.path.dirname(os.path.abspath(__file__))
        else:
            # 交互式环境
            return os.getcwd()
    
    @staticmethod
    def get_home_dir():
        """获取用户主目录"""
        return os.path.expanduser('~')
    
    @staticmethod
    def get_temp_dir():
        """获取临时目录"""
        return os.path.join(os.path.expanduser('~'), 'tmp')
    
    @staticmethod
    def list_subdirectories(path=None):
        """列出指定目录下的所有子目录"""
        if path is None:
            path = os.getcwd()
        
        dirs = []
        for item in os.listdir(path):
            item_path = os.path.join(path, item)
            if os.path.isdir(item_path):
                dirs.append(item)
        return sorted(dirs)
    
    @staticmethod
    def list_files(path=None, extensions=None):
        """列出指定目录下的文件"""
        if path is None:
            path = os.getcwd()
        
        files = []
        for item in os.listdir(path):
            item_path = os.path.join(path, item)
            if os.path.isfile(item_path):
                if extensions is None:
                    files.append(item)
                else:
                    ext = os.path.splitext(item)[1].lower()
                    if ext in extensions:
                        files.append(item)
        return sorted(files)
    
    @staticmethod
    def create_directory(path):
        """创建目录(如果不存在)"""
        if not os.path.exists(path):
            os.makedirs(path)
            return True
        return False

# 使用工具类
print("目录工具类演示:")
utils = DirectoryUtils()

print(f"工作目录: {utils.get_working_dir()}")
print(f"脚本目录: {utils.get_script_dir()}")
print(f"用户主目录: {utils.get_home_dir()}")
print(f"临时目录: {utils.get_temp_dir()}")

# 列出当前目录的子目录
print(f"\n当前目录下的子目录: {utils.list_subdirectories()[:5]}...")  # 显示前5个

# 列出当前目录的Python文件
print(f"当前目录下的Python文件: {utils.list_files(extensions=['.py'])}")

5. 动态改变当前目录

python 复制代码
import os

def demonstrate_directory_changes():
    """演示如何改变当前目录"""
    
    original_dir = os.getcwd()
    print(f"原始工作目录: {original_dir}")
    
    # 1. 使用 os.chdir() 改变目录
    try:
        # 尝试切换到上级目录
        parent_dir = os.path.dirname(original_dir)
        os.chdir(parent_dir)
        print(f"切换到父目录: {os.getcwd()}")
        
        # 再切换回来
        os.chdir(original_dir)
        print(f"切换回原始目录: {os.getcwd()}")
        
    except Exception as e:
        print(f"切换目录出错: {e}")
        # 确保切换回原始目录
        os.chdir(original_dir)
    
    # 2. 临时切换目录(使用上下文管理器)
    class ChangeDirectory:
        """上下文管理器,用于临时切换目录"""
        
        def __init__(self, new_path):
            self.new_path = new_path
            self.original_path = None
        
        def __enter__(self):
            self.original_path = os.getcwd()
            os.chdir(self.new_path)
            print(f"进入目录: {os.getcwd()}")
            return self
        
        def __exit__(self, exc_type, exc_val, exc_tb):
            os.chdir(self.original_path)
            print(f"退出到目录: {os.getcwd()}")
            return False  # 不抑制异常
    
    # 使用上下文管理器
    print("\n使用上下文管理器切换目录:")
    
    # 尝试切换到临时目录
    temp_dir = os.path.join(original_dir, "temp_test")
    os.makedirs(temp_dir, exist_ok=True)
    
    with ChangeDirectory(temp_dir):
        print(f"在临时目录中,创建测试文件...")
        test_file = os.path.join(temp_dir, "test.txt")
        with open(test_file, "w") as f:
            f.write("测试内容")
        print(f"已创建文件: {test_file}")
    
    print(f"返回后的目录: {os.getcwd()}")
    
    # 清理测试目录
    os.remove(test_file)
    os.rmdir(temp_dir)

demonstrate_directory_changes()

6. 特殊场景处理

python 复制代码
import os
import sys

def handle_special_cases():
    """处理获取目录的特殊场景"""
    
    print("=== 特殊场景处理 ===")
    
    # 1. 打包为可执行文件的情况
    if getattr(sys, 'frozen', False):
        # 如果是 PyInstaller 或类似工具打包的
        application_path = os.path.dirname(sys.executable)
        print(f"打包应用路径: {application_path}")
    else:
        print("正常Python脚本环境")
    
    # 2. 交互式环境(如 Jupyter, IPython)
    try:
        # 检查是否在 Jupyter 环境中
        from IPython import get_ipython
        if get_ipython() is not None:
            print("在 Jupyter/IPython 环境中")
            print(f"启动目录: {os.getcwd()}")
    except ImportError:
        pass
    
    # 3. 模块导入的情况
    def get_module_path(module_name):
        """获取已安装模块的路径"""
        try:
            import importlib
            module = importlib.import_module(module_name)
            if hasattr(module, '__file__'):
                return os.path.dirname(os.path.abspath(module.__file__))
            return None
        except ImportError:
            return None
    
    # 获取 os 模块的路径
    os_module_path = get_module_path('os')
    if os_module_path:
        print(f"os 模块路径: {os_module_path}")
    
    # 4. 处理符号链接
    if '__file__' in globals():
        real_path = os.path.realpath(__file__)
        link_path = __file__
        
        if real_path != os.path.abspath(link_path):
            print(f"脚本是符号链接")
            print(f"  链接位置: {link_path}")
            print(f"  实际位置: {real_path}")
    
    # 5. 处理相对路径
    relative_path = "./test_dir"
    absolute_path = os.path.abspath(relative_path)
    print(f"\n相对路径转换:")
    print(f"  相对路径: {relative_path}")
    print(f"  绝对路径: {absolute_path}")
    
    # 标准化路径
    normalized = os.path.normpath(absolute_path)
    print(f"  标准化路径: {normalized}")

handle_special_cases()

7. 实用示例:项目目录管理

python 复制代码
import os
from pathlib import Path

class ProjectDirectory:
    """项目目录管理类"""
    
    def __init__(self, project_root=None):
        """初始化项目目录管理器"""
        if project_root is None:
            # 默认使用当前脚本的父目录作为项目根目录
            self.project_root = self._find_project_root()
        else:
            self.project_root = Path(project_root).resolve()
        
        # 创建标准目录结构
        self.dirs = {
            'data': self.project_root / 'data',
            'logs': self.project_root / 'logs',
            'src': self.project_root / 'src',
            'tests': self.project_root / 'tests',
            'docs': self.project_root / 'docs',
            'config': self.project_root / 'config'
        }
        
        # 确保所有目录都存在
        self._ensure_directories()
    
    def _find_project_root(self):
        """查找项目根目录"""
        # 从当前脚本目录开始,向上查找包含特定标识的目录
        current = Path(__file__).resolve().parent if '__file__' in globals() else Path.cwd()
        
        # 查找包含 .git 或 setup.py 等标识的目录
        markers = ['.git', 'setup.py', 'pyproject.toml', 'requirements.txt']
        
        for marker in markers:
            for parent in [current] + list(current.parents):
                if (parent / marker).exists():
                    return parent
        
        # 如果没有找到标识,使用当前目录的父目录
        return current
    
    def _ensure_directories(self):
        """确保所有必要的目录都存在"""
        for name, path in self.dirs.items():
            path.mkdir(parents=True, exist_ok=True)
    
    def get_path(self, name, *subpaths):
        """获取目录路径"""
        if name not in self.dirs:
            raise ValueError(f"未知的目录名: {name}")
        
        return str(self.dirs[name].joinpath(*subpaths))
    
    def list_files(self, directory, pattern="*"):
        """列出目录中的文件"""
        if directory not in self.dirs:
            raise ValueError(f"未知的目录名: {directory}")
        
        return [str(f) for f in self.dirs[directory].glob(pattern)]
    
    def report(self):
        """生成目录结构报告"""
        print("=== 项目目录结构 ===")
        print(f"项目根目录: {self.project_root}")
        print("\n目录列表:")
        for name, path in sorted(self.dirs.items()):
            files_count = len(list(path.glob("*")))
            print(f"  {name:10}: {path} ({files_count} 个项目)")
        
        print(f"\n当前工作目录: {os.getcwd()}")
        print(f"相对路径: {os.path.relpath(os.getcwd(), self.project_root)}")

# 使用示例
if __name__ == "__main__":
    print("项目目录管理示例:")
    
    # 创建项目目录管理器
    project = ProjectDirectory()
    
    # 显示目录结构
    project.report()
    
    # 获取特定目录的路径
    print(f"\n数据目录: {project.get_path('data')}")
    print(f"日志目录: {project.get_path('logs')}")
    
    # 创建文件示例
    log_file = os.path.join(project.get_path('logs'), 'app.log')
    with open(log_file, 'w') as f:
        f.write("日志开始\n")
    print(f"已创建日志文件: {log_file}")

8. 性能比较

python 复制代码
import os
import time
from pathlib import Path

def compare_performance():
    """比较不同获取目录方法的性能"""
    
    test_count = 100000
    
    print("性能比较(执行100,000次):")
    
    # 测试 os.getcwd()
    start = time.time()
    for _ in range(test_count):
        _ = os.getcwd()
    end = time.time()
    print(f"os.getcwd():      {(end-start)*1000:.2f} ms")
    
    # 测试 Path.cwd()
    start = time.time()
    for _ in range(test_count):
        _ = Path.cwd()
    end = time.time()
    print(f"Path.cwd():       {(end-start)*1000:.2f} ms")
    
    # 测试 os.path.abspath('.')
    start = time.time()
    for _ in range(test_count):
        _ = os.path.abspath('.')
    end = time.time()
    print(f"os.path.abspath('.'): {(end-start)*1000:.2f} ms")
    
    # 测试 os.path.dirname(os.path.abspath(__file__))
    if '__file__' in globals():
        start = time.time()
        for _ in range(test_count):
            _ = os.path.dirname(os.path.abspath(__file__))
        end = time.time()
        print(f"os.path.dirname(__file__): {(end-start)*1000:.2f} ms")

# 运行性能测试
print("获取目录方法性能比较:")
compare_performance()

这些方法涵盖了 Python 中获取当前目录的各种场景和需求。根据具体需求选择合适的方法:

  1. 获取工作目录 :使用 os.getcwd()Path.cwd()

  2. 获取脚本目录 :使用 os.path.dirname(os.path.abspath(__file__))Path(__file__).resolve().parent

  3. 需要路径操作 :推荐使用 pathlib 模块

  4. 需要兼容旧版本 :使用 os.path 模块

相关推荐
晨非辰2 小时前
C++波澜壮阔40年|类和对象篇:拷贝构造与赋值重载的演进与实现
运维·开发语言·c++·人工智能·后端·python·深度学习
多米Domi0112 小时前
0x3f 第36天 外卖8,9,树
数据结构·python·算法·leetcode
天才测试猿2 小时前
Chrome浏览器+Postman做接口测试
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
zckui2 小时前
conda常用命令
python·conda
张3蜂2 小时前
Label-Studio图片标注初体验
python·开源
深蓝电商API2 小时前
Scrapy LinkExtractor参数详解与复杂链接提取
爬虫·python·scrapy
我是一只小青蛙8882 小时前
Python入门指南:从安装到Hello World
python
FJW0208142 小时前
Python中的闭包
开发语言·python
小北方城市网2 小时前
SpringBoot 集成 Redis 实战(缓存与分布式锁):提升系统性能与并发能力
spring boot·python·rabbitmq·java-rabbitmq·数据库架构