os Module Complexity¶
The os module provides a way to use operating system-dependent functionality like reading or writing to the file system.
File Operations¶
| Operation | Time | Space | Notes |
|---|---|---|---|
os.path.exists(path) |
O(1) | O(1) | Check if path exists |
os.path.isfile(path) |
O(1) | O(1) | Check if file |
os.path.isdir(path) |
O(1) | O(1) | Check if directory |
os.listdir(path) |
O(n) | O(n) | List directory contents |
os.scandir(path) |
O(n) | O(1) | Lazy directory iterator |
os.walk(path) |
O(n) | O(d) | n = total entries, d = max depth (call stack + pending dirs) |
os.stat(path) |
O(1) | O(1) | Get file statistics |
os.lstat(path) |
O(1) | O(1) | Stat without following symlink |
os.remove(path) |
O(1) | O(1) | Delete file |
os.mkdir(path) |
O(1) | O(1) | Create directory |
os.makedirs(path) |
O(n) | O(1) | Create directories, n = depth |
os.rmdir(path) |
O(1) | O(1) | Remove empty directory |
os.rename(src, dst) |
O(1) | O(1) | Rename/move file |
os.chmod(path, mode) |
O(1) | O(1) | Change permissions |
Path Operations¶
| Operation | Time | Space | Notes |
|---|---|---|---|
os.path.join(paths) |
O(n) | O(n) | Join path components, n = total length |
os.path.split(path) |
O(n) | O(n) | Split head and tail |
os.path.dirname(path) |
O(n) | O(n) | Get directory part |
os.path.basename(path) |
O(n) | O(n) | Get filename part |
os.path.splitext(path) |
O(n) | O(n) | Split extension |
os.path.abspath(path) |
O(n) | O(n) | Get absolute path |
os.path.realpath(path) |
O(n) | O(n) | Resolve symlinks |
os.path.normpath(path) |
O(n) | O(n) | Normalize path |
Common Operations¶
Checking File Existence¶
import os
# Fast existence check - O(1)
if os.path.exists("/path/to/file"):
print("File exists")
# Check type - O(1)
if os.path.isfile("/path/to/file"):
print("Is a file")
if os.path.isdir("/path/to/dir"):
print("Is a directory")
Listing Files¶
import os
# Get all files - O(n) where n = number of items
files = os.listdir("/path/to/dir")
# Better: lazy iteration - O(1) per item
with os.scandir("/path/to/dir") as entries:
for entry in entries: # O(1) each
print(entry.name)
print(entry.is_file()) # O(1)
Walking Directory Tree¶
import os
# Walk recursively - O(n) where n = total files
for dirpath, dirnames, filenames in os.walk("/path/to/root"):
# Process files
for filename in filenames:
path = os.path.join(dirpath, filename)
# Do something - O(1) per file
File Metadata¶
import os
# Get file stats - O(1)
stats = os.stat("/path/to/file")
print(stats.st_size) # File size
print(stats.st_mtime) # Modification time
print(stats.st_mode) # Permissions
# Check if readable - O(1)
if os.access("/path/to/file", os.R_OK):
print("Readable")
Path Manipulation¶
import os
# Join paths - O(n)
path = os.path.join("/home", "user", "documents", "file.txt")
# Split path - O(n)
dirname, filename = os.path.split(path)
# dirname = "/home/user/documents"
# filename = "file.txt"
# Get extension - O(n)
name, ext = os.path.splitext("file.txt")
# name = "file"
# ext = ".txt"
# Get absolute path - O(n)
abs_path = os.path.abspath("./relative/path")
Creating and Removing¶
import os
# Create directory - O(1)
os.mkdir("/new/dir")
# Create all parent dirs - O(n) where n = depth
os.makedirs("/deep/nested/dir/structure")
# Remove file - O(1)
os.remove("/path/to/file")
# Remove empty dir - O(1)
os.rmdir("/empty/dir")
# Remove tree (careful!) - O(n)
import shutil
shutil.rmtree("/path/to/remove")
Environment Variables¶
import os
# Get environment - O(1)
home = os.environ['HOME']
# Get with default - O(1)
user = os.environ.get('USER', 'unknown')
# Set environment - O(1)
os.environ['MY_VAR'] = 'value'
# All environment variables - O(m) where m = number of vars
all_vars = os.environ.items()
Process Information¶
import os
# Current process ID - O(1)
pid = os.getpid()
# Parent process ID - O(1)
ppid = os.getppid()
# Current working directory - O(1)
cwd = os.getcwd()
# Change working directory - O(1)
os.chdir("/new/directory")
Performance Notes¶
Efficient File Listing¶
import os
# Less efficient - returns all names, no file type info
files = os.listdir(".") # Must separate files/dirs separately
# More efficient - lazy, includes file type info
with os.scandir(".") as entries:
files = [e.name for e in entries if e.is_file()]
Avoiding Unnecessary Stats¶
import os
path = "/path/to/file"
# Bad: two stat calls
if os.path.exists(path) and os.path.getsize(path) > 0:
pass
# Better: one stat call
try:
size = os.path.getsize(path) # O(1)
if size > 0:
pass
except FileNotFoundError:
pass
Version Notes¶
- Python 2.x: Basic functionality, limited Unicode support
- Python 3.x: Full Unicode path support
- Python 3.5+:
os.scandir()is faster for directory iteration - Python 3.10+: More type hints, better cross-platform support
Platform Differences¶
- POSIX (Linux, macOS): Full feature set
- Windows: Some path operations differ (backslashes vs forward slashes)
- Path handling: Use
os.path.join()or better:pathlib.Path
Related Modules¶
- pathlib - Object-oriented path handling (recommended)
- glob - Unix-style pathname expansion
- shutil - High-level file operations
- tempfile - Temporary files
Best Practices¶
✅ Do:
- Use
os.path.join()for cross-platform compatibility - Use
os.scandir()for directory listing (faster) - Use
pathlib.Pathfor modern code (more readable) - Check existence before accessing files
❌ Avoid:
- Assuming forward slashes work on all platforms
- Using string concatenation for paths
- Multiple stat calls when one suffices
- Hardcoding paths (use environment variables)