According to the Zen of Python, "there should be one-- and preferably only one --obvious way to do it."
In the book Python Crash Course I learned to use the pathlib module to read and write files:
from pathlib import Path
p = Path("my_file.txt")
contents = p.read_text()However, the Python documentation uses open( ) instead:
with open("my_file.txt", encoding="utf-8") as f:
contents = f.read()What is the best, or "obvious" approach?
Videos
If all you wanted to do was read or write a small blob of text (or bytes), then you no longer need to use a with-statement when using pathlib:
>>> import pathlib
>>> path = pathlib.Path("/tmp/example.txt")
>>> path.write_text("hello world")
11
>>> path.read_text()
'hello world'
>>> path.read_bytes()
b'hello world'
These methods still use a context-manager internally (src).
Opening a large file to iterate lines should still use a with-statement, as the docs show:
>>> with path.open() as f:
... for line in f:
... print(line)
...
hello world
Keep in mind that a Path object is for working with filesystem paths. Just like the built-in library of Python, there is an open method but no close in a Path object.
The .close is in the file handle that is returned by either the built-in open or by using the Path object's open method:
>>> from pathlib import Path
>>> p=Path(some_file)
>>> p
PosixPath('/tmp/file')
You can open that Path object either with the built-in open function or the open method in the Path object:
>>> fh=open(p) # open built-in function
>>> fh
<_io.TextIOWrapper name='/tmp/file' mode='r' encoding='UTF-8'>
>>> fh.close()
>>> fh=p.open() # Path open method which aliases to os.open
>>> fh
<_io.TextIOWrapper name='/tmp/file' mode='r' encoding='UTF-8'>
>>> fh.close()
You can have a look at the source code for pathlib on Github as an indication of how the authors of pathlib do it in their own code.
What I observe is one of three things.
The most common by far is to use with:
from pathlib import Path
p=Path('/tmp/file')
#create a file
with p.open(mode='w') as fi:
fi.write(f'Insides of: {str(p)}')
# read it back and test open or closed
with p.open(mode='r') as fi:
print(f'{fi.read()} closed?:{fi.closed}')
# prints 'Insides of: /tmp/file closed?:False'
As you likely know, at the end of the with block the __exit__ methods are called. For a file, that means the file is closed. This is the most common approach in the pathlib source code.
Second, you can also see in the source that a pathlib object maintains an entry and exit status and a flag of the file being open and closed. The os.close functions is not explicitly called however. You can check that status with the .closed accessor.
fh=p.open()
print(f'{fh.read()} closed?:{fh.closed}')
# prints Insides of: /tmp/file closed?:False
# fi will only be closed when fi goes out of scope...
# or you could (and should) do fh.close()
with p.open() as fi:
pass
print(f'closed?:{fi.closed}')
# fi still in scope but implicitly closed at the end of the with bloc
# prints closed?:True
Third, on cPython, files are closed when the file handle goes out of scope. This is not portable or considered 'good practice' to rely on, but commonly it is. There are instances of this in the pathlib source code.