Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Python›os and pathlib Modules

Standard Library

os and pathlib Modules

The os and pathlib modules provide the essential interface between Python code and the underlying file system of the host machine. While os offers low-level access to operating system functionalities, pathlib introduces an object-oriented approach to handling paths as first-class objects. Mastering these libraries is vital for building robust applications that interact with directories, files, and environment variables reliably.

Introduction to the os Module

The os module is a foundational component of the Python standard library, serving as a wrapper for platform-specific system calls. When you import os, you gain the ability to interact with the environment in which your script is executing. This includes retrieving information about the current working directory, listing files, and managing environment variables. The module functions by translating your Python commands into the corresponding system calls required by the underlying operating system. Because different operating systems handle file paths and system permissions differently, os serves as an abstraction layer to normalize these interactions. Understanding os is crucial because it allows your code to be portable across different machine configurations. You reach for os when you need to perform administrative tasks, such as managing processes or modifying system-level settings, that go beyond simple file input and output operations.

import os

# Get the current working directory
cwd = os.getcwd()
print(f"Current directory: {cwd}")

# List all files in the current directory
files = os.listdir(cwd)
print(f"Files in folder: {files}")

The Object-Oriented Pathing Approach

In older Python codebases, developers often used string concatenation to construct file paths, which was notoriously error-prone due to differences in path separators—slashes versus backslashes—across operating systems. The pathlib module solves this by treating paths as objects rather than strings. By using the Path class, you instantiate an object that represents a location in the file system. These objects understand the semantics of their environment, meaning path joining happens automatically and correctly regardless of whether the script runs on a Unix-like system or Windows. This object-oriented design allows for expressive code where path operations are methods on the object itself. This shift from functional, string-based manipulation to object-based traversal reduces bugs significantly and improves readability. You should use pathlib for almost all modern file system interactions because it is cleaner, safer, and more intuitive than legacy approaches.

from pathlib import Path

# Create a path object
base_path = Path.home()

# Join paths using the / operator, which is highly readable
logs_path = base_path / "projects" / "app.log"

# Check if the path exists
if logs_path.exists():
    print(f"Log file found at: {logs_path}")

Navigating and Inspecting Directories

Navigating the file system involves more than just locating files; it requires inspecting the metadata and properties of those files or directories. The pathlib module simplifies this by providing intuitive methods to query the file system. For instance, you can determine if a path is a directory or a regular file using the is_dir() or is_file() methods. Furthermore, you can iterate over the contents of a directory using the iterdir() method, which returns an iterator of path objects. This is much more efficient than fetching a list of strings, especially when dealing with directories containing thousands of items. By leveraging these methods, you can build logic that performs recursive operations or filters files based on their extensions or modification times. Understanding this structural traversal is essential for tasks like building automated file organizers, data ingestion pipelines, or simple build scripts that clean up temporary directories.

from pathlib import Path

# Target a directory
dir_path = Path(".")

# Iterate through items and check file types
for item in dir_path.iterdir():
    if item.is_file() and item.suffix == ".py":
        print(f"Found Python script: {item.name}")

Manipulating Files and File Systems

Beyond merely navigating, you will often need to manipulate the file system by creating directories, deleting files, or renaming assets. The pathlib module offers a cohesive API for these tasks, such as mkdir() for directory creation and unlink() for file removal. A critical concept to understand here is the 'parents' and 'exist_ok' parameters in mkdir(); these allow you to create nested directory trees without risking a runtime error if the directories already exist. These methods provide a safer, more predictable way to modify the state of the disk compared to raw shell execution. Because these are method calls on path objects, they are easier to debug and test in isolation. When modifying the file system, always wrap your logic in try-except blocks, as file system operations are prone to external interference, such as permissions issues or competing processes that might lock files unexpectedly during execution.

from pathlib import Path

# Define a path for a new directory
new_dir = Path("./data/logs")

# Create directory with parents if missing, ignoring errors if it exists
new_dir.mkdir(parents=True, exist_ok=True)

# Create an empty file
new_file = new_dir / "temp.txt"
new_file.touch()

# Remove the file
new_file.unlink()

Environment Variables and System State

The os module remains the primary tool for interacting with the shell environment, particularly when dealing with environment variables. Environment variables are a set of dynamic values that can affect the way running processes behave on a computer. They are commonly used to store sensitive information like API keys or database connection strings outside of the source code. Using os.environ, you can access these variables as if they were a dictionary. This is a critical security practice because it prevents hardcoding credentials, which are often accidentally exposed in version control systems. By combining the environment configuration capabilities of the os module with the file system management capabilities of the pathlib module, you create a powerful architecture for application deployment. You should ensure that your application retrieves its configuration from these variables at startup to remain flexible across development, staging, and production environments.

import os

# Set an environment variable (often done via shell exports)
os.environ["APP_ENV"] = "production"

# Retrieve the variable safely with a default value
app_env = os.environ.get("APP_ENV", "development")
print(f"Current environment: {app_env}")

# Accessing system-wide path variables
path_vars = os.environ.get("PATH")
print(f"System PATH: {path_vars[:30]}...")

Key points

  • The os module provides a low-level interface to interact with the underlying operating system and environment variables.
  • The pathlib module offers an object-oriented approach to handle file paths that is significantly safer than string manipulation.
  • Always use the / operator in pathlib to join path components, ensuring compatibility across different operating systems.
  • Path objects are superior to strings because they provide built-in methods like exists(), is_dir(), and is_file().
  • The iterdir() method is the preferred way to traverse directory contents because it returns an efficient iterator.
  • You should use mkdir(parents=True, exist_ok=True) to ensure reliable directory creation without raising common errors.
  • Environment variables managed via os.environ are the industry-standard way to store sensitive configurations outside the code.
  • File system operations should always be wrapped in exception handling to manage permissions or availability issues gracefully.

Common mistakes

  • Mistake: Manually concatenating paths with '+' operators. Why it's wrong: This is platform-dependent and fails to handle slashes correctly. Fix: Use 'pathlib.Path / subpath' or 'os.path.join()'.
  • Mistake: Using 'os.path' for file operations instead of 'pathlib'. Why it's wrong: 'os.path' returns strings, making chaining and attribute access clunky. Fix: Use 'pathlib.Path' objects for cleaner, object-oriented syntax.
  • Mistake: Forgetting that 'os.listdir()' includes hidden files and directories. Why it's wrong: It can lead to unexpected errors when processing hidden config files. Fix: Add a check using 'name.startswith(".")' or use a glob pattern.
  • Mistake: Assuming 'os.getcwd()' will always point to the script's directory. Why it's wrong: 'getcwd' returns the current working directory from where the command was executed, not the file location. Fix: Use 'pathlib.Path(__file__).resolve().parent'.
  • Mistake: Using 'os.remove()' on a directory. Why it's wrong: 'os.remove()' is only for files; it raises an OSError on directories. Fix: Use 'os.rmdir()' for empty directories or 'shutil.rmtree()' for directories with contents.

Interview questions

What is the primary difference between the older 'os.path' module and the modern 'pathlib' module in Python?

The primary difference lies in how they handle paths. 'os.path' treats paths as simple strings, requiring you to constantly pass these strings into various functions like 'os.path.join()' or 'os.path.exists()'. In contrast, 'pathlib' treats paths as true objects. This object-oriented approach makes code more readable and intuitive, as you can perform operations directly on the path object, such as 'path.exists()' or 'path.joinpath()', rather than wrapping the path in procedural function calls.

How do you navigate and list files in a directory using the 'os' module versus 'pathlib'?

In the 'os' module, you typically use 'os.listdir()' or 'os.walk()' to iterate through files, which returns raw strings. You then have to manually concatenate these strings with the directory path to get the full file path. Using 'pathlib', you instantiate a 'Path' object and use the '.iterdir()' method. This is superior because '.iterdir()' yields 'Path' objects directly, allowing you to immediately call methods like '.is_file()' or '.suffix' without additional path manipulation logic, significantly reducing error-prone string concatenation.

Explain how to safely join path components together to ensure cross-platform compatibility.

To ensure cross-platform compatibility, you must avoid hardcoding forward or backslashes. If using 'os', you use 'os.path.join(dir, file)', which automatically uses the correct separator for the host operating system. With 'pathlib', you use the '/' operator, such as 'Path('folder') / 'file.txt'. This is preferred in modern Python because the syntax is cleaner, more readable, and explicitly handles the underlying separator logic consistently across different operating systems, preventing common path-related bugs.

Compare the approach of checking for a file's existence and reading it using both modules.

Using 'os.path', you would call 'os.path.exists(path)' and then open the file via the built-in 'open()' function. With 'pathlib', the 'Path' object has a built-in '.exists()' method and a '.read_text()' or '.read_bytes()' method. The 'pathlib' approach is more encapsulated; you treat the file path as an object that knows how to interact with its own content. This keeps your code focused on the file object rather than passing string paths back and forth between different modules.

How can you change the file extension or rename a file using the 'pathlib' module?

The 'pathlib' module makes these tasks highly expressive. To change an extension, you access the 'with_suffix()' method on a 'Path' object, which returns a new path with the requested extension updated, without altering the original unless explicitly told to do so. To rename, you use the '.rename()' method directly on the object. This is better than 'os.rename()' because it maintains the state of the path object and allows for chaining operations, making the file transformation process much more declarative and easier to debug.

When should you choose one module over the other in a professional Python codebase?

In modern professional Python development, you should almost always prefer 'pathlib' for new code because it is more robust, readable, and object-oriented. 'os' is mostly kept for legacy support or highly specific tasks like low-level environment variable manipulation or specific system calls that 'pathlib' does not cover. 'pathlib' abstracts away the complexities of different file systems, making your code cleaner and more maintainable while effectively preventing the common bugs associated with manual string handling.

All Python interview questions →

Check yourself

1. Which approach is the most robust way to create a path to a file inside a subdirectory on multiple operating systems?

  • A.Path('folder') + '/' + Path('file.txt')
  • B.Path('folder') / 'file.txt'
  • C.os.path.join('folder', 'file.txt').replace('\\', '/')
  • D.Path('folder').joinpath('file.txt').as_posix()
Show answer

B. Path('folder') / 'file.txt'
Using the / operator with pathlib handles OS-specific separators automatically. The + operator is meant for strings, manual replacements are error-prone, and as_posix() doesn't change how the path is joined.

2. Why is 'pathlib.Path(__file__).parent' preferred over 'os.getcwd()' when you need to access a file relative to your script?

  • A.It is faster than calling os functions.
  • B.It prevents race conditions during file access.
  • C.It is relative to the script file location, whereas getcwd() is relative to where the terminal is currently positioned.
  • D.It automatically resolves symbolic links before execution.
Show answer

C. It is relative to the script file location, whereas getcwd() is relative to where the terminal is currently positioned.
The script location is fixed regardless of where the user runs the command from, while getcwd() depends on the user's terminal state. Neither option affects speed or race conditions significantly.

3. What happens if you call 'Path('data/logs').mkdir()' and the directory already exists?

  • A.It silently does nothing.
  • B.It overwrites the existing directory.
  • C.It raises a FileExistsError.
  • D.It merges the new files into the existing folder.
Show answer

C. It raises a FileExistsError.
By default, mkdir() raises a FileExistsError if the target exists. Option 1 would be true if exist_ok=True was passed. Options 2 and 4 are incorrect behaviors for creating a directory.

4. You want to find all '.jpg' files in a directory recursively. Which method is most efficient?

  • A.Using os.listdir() and checking if each file ends with '.jpg'.
  • B.Using pathlib.Path.glob('**/*.jpg').
  • C.Using os.walk() and iterating through every file, checking the extension.
  • D.Using Path.iterdir() and an if-statement.
Show answer

B. Using pathlib.Path.glob('**/*.jpg').
glob('**/*.jpg') is designed specifically for recursive pattern matching. os.walk() and listdir() require significantly more manual boilerplate code to achieve the same result.

5. Which statement best describes the return value of 'pathlib.Path('file.txt').resolve()'?

  • A.It returns a string representing the absolute path.
  • B.It returns a Path object pointing to the absolute, symlink-resolved path.
  • C.It returns the size of the file in bytes.
  • D.It returns a boolean indicating if the file exists.
Show answer

B. It returns a Path object pointing to the absolute, symlink-resolved path.
resolve() returns an absolute Path object, expanding symlinks and '..' segments. It does not return a string (that would be str(path)), nor a size, nor a boolean.

Take the full Python quiz →

← PreviousType Hints and mypyNext →sys Module

Python

78 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app