Traditionally programmers in Python handle file and directory paths as strings, eighter writing the string directly or using modules like os
. That can be ugly and sometimes can lead to bugs.
A better way to handle paths is by using pathlib
module which is part of the Python standard library, so if you have Python installed, you are good to go.
With pathlib
, instead of using strings, we use Path
object:
from pathlib import Path
path_to_my_file = Path('/home/my_user/my_file.txt')
# PosixPath('/home/my_user/my_file.txt')
The Path
class also has some useful methods that help you start with your path like Path.home()
that returns Path object with the path to the home directory of the user, and Path.cwd()
that returns the path to the current working directory.
If you look closely at the previous example, you see that we didn’t get a Path
object, but PosixPath
. The Path
class let us keep our code platform-independent, meaning it will work on both Windows and Unix-based platforms. In a Unix-based platform such as Ubuntu and MacOS the Path
class returns a PosixPath
object, while on Windows it returns WindowsPath
. You don’t need to worry about it, the Path
class handles the differences between the platforms for you.
Joining paths is much more simple and intuitive than with os.path.join
, using only the /
operator to join paths
path_to_my_file = Path.home() / 'my_file.txt'
# PosixPath('/home/my_user/my_file.txt')
An absolute path can be generated by
Path('.').absolute()
# PosixPath('/home/my_user)
You can always convert Path
object back to a string using casting
str(path_to_my_file)
# '/home/my_user/my_file.txt'
Extract Components of a Path
The Path
object can also help us to get different components of the full path.
Get the file name (without extension):
path_to_my_file.stem
# 'my_file'
Get the file name (with extension):
path_to_my_file.name
# 'my_file.txt'
Get only the extension:
path_to_my_file.suffix
# '.txt'
Get the parent directory as another Path
object:
path_to_file.parent
# PosixPath('/home/my_user)
Get the base path (before the directories):
path_to_file.anchor
# '/' on linux
# 'C:\\' on Windows
Get all parts as a tuple:
path_to_file.parts
# ('/', 'home', 'my_user', 'my_file.txt')
Create, Remove, and Change Files and Directories
A file can be removed by:
path_to_my_file.unlink(missing_ok=True)
if the missing_ok
flag is set to True
an error will not be raised if the file doesn’t exist.
We can now check if the file exists (that also works for directories):
path_to_my_file.exists()
# False
Other methods we can use to check files and directories are:
path_to_my_file.is_file()
# False
path_to_my_file.is_dir()
# False
path_to_my_file.parent.is_dir()
# True
Now let’s recreate the file and write text to it
path_to_my_file.touch(exist_ok=True)
path_to_my_file.write_text('Hello')
if the exist_ok
flag is set to True
an error will not be raised if the file exists.
To read the content
print(path_to_my_file.read_text())
# Hello
To rename a file, we need to create a new path with the new name. We can use the with_stem
method to replace the name of the file (without the extension) and then rename the file
new_name_path = path_to_my_file.with_stem('new_file')
path_to_my_file.rename(new_name_path)
We can also change the extension
new_suffix = new_name_path.with_suffix('.md')
new_name_path.replace(new_suffix)
Creating a directory can be done using mkdir
new_dir_path = path_to_my_file.parent / 'new_dir'
new_dir_path.mkdir(parents=True, exist_ok=True)
where the parents
flag, if set to True will also create the intermediate directories.
To remove the last child directory
new_dir_path.rmdir()
List Files by Pattern
Finally, we can use the glob
method to search for files by pattern inside a directory
current_dir = Path.cwd()
txt_files = current_dir.glob('*.txt')
or, for a recursive search inside subdirectories
txt_files = current_dir.rglob('*.txt')
Final Thoughts
The pathlib module is a powerful tool to handle paths, files, and directories. In this article, I showed just a little bit of the Path
class and I hope you’ll explore it more, and move from using the old os
module.