Python File I/O
In this tutorial, you'll learn about Python file operations. More specifically,
opening a file, reading from it, writing into it, closing it, and various file
methods that you should be aware of.
Video: Reading and Writing Files in Python
Files
Files are named locations on disk to store related information. They are used
to permanently store data in a non-volatile memory (e.g. hard disk).
Since Random Access Memory (RAM) is volatile (which loses its data when
the computer is turned off), we use files for future use of the data by
permanently storing them.
When we want to read from or write to a file, we need to open it first. When we
are done, it needs to be closed so that the resources that are tied with the file
are freed.
Hence, in Python, a file operation takes place in the following order:
1. Open a file
2. Read or write (perform operation)
3. Close the file
Opening Files in Python
Python has a built-in open() function to open a file. This function returns a file
object, also called a handle, as it is used to read or modify the file accordingly.
>>> f = open("test.txt") # open file in current directory
>>> f = open("C:/Python38/README.txt") # specifying full path
We can specify the mode while opening a file. In mode, we specify whether
we want to read r , write w or append a to the file. We can also specify if we
want to open the file in text mode or binary mode.
The default is reading in text mode. In this mode, we get strings when reading
from the file.
On the other hand, binary mode returns bytes and this is the mode to be used
when dealing with non-text files like images or executable files.
Mode Description
r Opens a file for reading. (default)
Opens a file for writing. Creates a new file if it does not exist or
w
truncates the file if it exists.
Opens a file for exclusive creation. If the file already exists, the
x
operation fails.
Opens a file for appending at the end of the file without truncating it.
a
Creates a new file if it does not exist.
t Opens in text mode. (default)
b Opens in binary mode.
+ Opens a file for updating (reading and writing)
f = open("test.txt") # equivalent to 'r' or 'rt'
f = open("test.txt",'w') # write in text mode
f = open("img.bmp",'r+b') # read and write in binary mode
Unlike other languages, the character a does not imply the number 97 until it
is encoded using ASCII (or other equivalent encodings).
Moreover, the default encoding is platform dependent. In windows, it
is cp1252 but utf-8 in Linux.
So, we must not also rely on the default encoding or else our code will behave
differently in different platforms.
Hence, when working with files in text mode, it is highly recommended to
specify the encoding type.
f = open("test.txt", mode='r', encoding='utf-8')
Closing Files in Python
When we are done with performing operations on the file, we need to properly
close the file.
Closing a file will free up the resources that were tied with the file. It is done
using the close() method available in Python.
Python has a garbage collector to clean up unreferenced objects but we must
not rely on it to close the file.
f = open("test.txt", encoding = 'utf-8')
# perform file operations
f.close()
This method is not entirely safe. If an exception occurs when we are
performing some operation with the file, the code exits without closing the file.
A safer way is to use a try...finally block.
try:
f = open("test.txt", encoding = 'utf-8')
# perform file operations
finally:
f.close()
This way, we are guaranteeing that the file is properly closed even if an
exception is raised that causes program flow to stop.
The best way to close a file is by using the with statement. This ensures that
the file is closed when the block inside the with statement is exited.
We don't need to explicitly call the close() method. It is done internally.
with open("test.txt", encoding = 'utf-8') as f:
# perform file operations
Writing to Files in Python
In order to write into a file in Python, we need to open it in write w , append a or
exclusive creation x mode.
We need to be careful with the w mode, as it will overwrite into the file if it
already exists. Due to this, all the previous data are erased.
Writing a string or sequence of bytes (for binary files) is done using
the write() method. This method returns the number of characters written to
the file.
with open("test.txt",'w',encoding = 'utf-8') as f:
f.write("my first file\n")
f.write("This file\n\n")
f.write("contains three lines\n")
This program will create a new file named test.txt in the current directory if it
does not exist. If it does exist, it is overwritten.
We must include the newline characters ourselves to distinguish the different
lines.
Reading Files in Python
To read a file in Python, we must open the file in reading r mode.
There are various methods available for this purpose. We can use
the read(size) method to read in the size number of data. If the size parameter
is not specified, it reads and returns up to the end of the file.
We can read the text.txt file we wrote in the above section in the following
way:
>>> f = open("test.txt",'r',encoding = 'utf-8')
>>> f.read(4) # read the first 4 data
'This'
>>> f.read(4) # read the next 4 data
' is '
>>> f.read() # read in the rest till end of file
'my first file\nThis file\ncontains three lines\n'
>>> f.read() # further reading returns empty sting
''
We can see that the read() method returns a newline as '\n' . Once the end of
the file is reached, we get an empty string on further reading.
We can change our current file cursor (position) using the seek() method.
Similarly, the tell() method returns our current position (in number of bytes).
>>> f.tell() # get the current file position
56
>>> f.seek(0) # bring file cursor to initial position
0
>>> print(f.read()) # read the entire file
This is my first file
This file
contains three lines
We can read a file line-by-line using a for loop. This is both efficient and fast.
>>> for line in f:
... print(line, end = '')
...
This is my first file
This file
contains three lines
In this program, the lines in the file itself include a newline character \n . So,
we use the end parameter of the print() function to avoid two newlines when
printing.
Alternatively, we can use the readline() method to read individual lines of a
file. This method reads a file till the newline, including the newline character.
>>> f.readline()
'This is my first file\n'
>>> f.readline()
'This file\n'
>>> f.readline()
'contains three lines\n'
>>> f.readline()
''
Lastly, the readlines() method returns a list of remaining lines of the entire file.
All these reading methods return empty values when the end of file (EOF) is
reached.
>>> f.readlines()
['This is my first file\n', 'This file\n', 'contains three lines\n']
Python File Methods
There are various methods available with the file object. Some of them have
been used in the above examples.
Here is the complete list of methods in text mode with a brief description:
Method Description
Closes an opened file. It has no effect if the
close()
file is already closed.
detach() Separates the underlying binary buffer from
the TextIOBase and returns it.
Returns an integer number (file descriptor) of
fileno()
the file.
flush() Flushes the write buffer of the file stream.
isatty() Returns True if the file stream is interactive.
Reads at most n characters from the file. Reads
read( n )
till end of file if it is negative or None .
Returns True if the file stream can be read
readable()
from.
Reads and returns one line from the file. Reads
readline( n =-1)
in at most n bytes if specified.
Reads and returns a list of lines from the file.
readlines( n =-1) Reads in at most n bytes/characters if
specified.
seek( offset , from = SEEK_SET Changes the file position to offset bytes, in
) reference to from (start, current, end).
Returns True if the file stream supports
seekable()
random access.
Returns an integer that represents the current
tell()
position of the file's object.
Resizes the file stream to size bytes. If size
truncate( size = None )
is not specified, resizes to current location.
Returns True if the file stream can be written
writable()
to.
Writes the string s to the file and returns the
write( s )
number of characters written.
writelines( lines ) Writes a list of lines to the file.
Python Directory and Files
Management
In this tutorial, you'll learn about file and directory management in Python, i.e.
creating a directory, renaming it, listing all directories, and working with them.
Video: Python os Module
Python Directory
If there are a large number of files to handle in our Python program, we can
arrange our code within different directories to make things more manageable.
A directory or folder is a collection of files and subdirectories. Python has
the os module that provides us with many useful methods to work with
directories (and files as well).
Get Current Directory
We can get the present working directory using the getcwd() method of
the os module.
This method returns the current working directory in the form of a string. We
can also use the getcwdb() method to get it as bytes object.
>>> import os
>>> os.getcwd()
'C:\\Program Files\\PyScripter'
>>> os.getcwdb()
b'C:\\Program Files\\PyScripter'
The extra backslash implies an escape sequence. The print() function will
render this properly.
>>> print(os.getcwd())
C:\Program Files\PyScripter
Changing Directory
We can change the current working directory by using the chdir() method.
The new path that we want to change into must be supplied as a string to this
method. We can use both the forward-slash / or the backward-slash \ to
separate the path elements.
It is safer to use an escape sequence when using the backward slash.
>>> os.chdir('C:\\Python33')
>>> print(os.getcwd())
C:\Python33
List Directories and Files
All files and sub-directories inside a directory can be retrieved using
the listdir() method.
This method takes in a path and returns a list of subdirectories and files in that
path. If no path is specified, it returns the list of subdirectories and files from
the current working directory.
>>> print(os.getcwd())
C:\Python33
>>> os.listdir()
['DLLs',
'Doc',
'include',
'Lib',
'libs',
'LICENSE.txt',
'NEWS.txt',
'python.exe',
'pythonw.exe',
'README.txt',
'Scripts',
'tcl',
'Tools']
>>> os.listdir('G:\\')
['$RECYCLE.BIN',
'Movies',
'Music',
'Photos',
'Series',
'System Volume Information']
Making a New Directory
We can make a new directory using the mkdir() method.
This method takes in the path of the new directory. If the full path is not
specified, the new directory is created in the current working directory.
>>> os.mkdir('test')
>>> os.listdir()
['test']
Renaming a Directory or a File
The rename() method can rename a directory or a file.
For renaming any directory or file, the rename() method takes in two basic
arguments: the old name as the first argument and the new name as the
second argument.
>>> os.listdir()
['test']
>>> os.rename('test','new_one')
>>> os.listdir()
['new_one']
Removing Directory or File
A file can be removed (deleted) using the remove() method.
Similarly, the rmdir() method removes an empty directory.
>>> os.listdir()
['new_one', 'old.txt']
>>> os.remove('old.txt')
>>> os.listdir()
['new_one']
>>> os.rmdir('new_one')
>>> os.listdir()
[]
Note: The rmdir() method can only remove empty directories.
In order to remove a non-empty directory, we can use the rmtree() method
inside the shutil module.
>>> os.listdir()
['test']
>>> os.rmdir('test')
Traceback (most recent call last):
...
OSError: [WinError 145] The directory is not empty: 'test'
>>> import shutil
>>> shutil.rmtree('test')
>>> os.listdir()
[]
Python Errors and Built-in
Exceptions
In this tutorial, you will learn about different types of errors and exceptions that
are built-in to Python. They are raised whenever the Python interpreter
encounters errors.
Video: Python Exception Handling
We can make certain mistakes while writing a program that lead to errors
when we try to run it. A python program terminates as soon as it encounters
an unhandled error. These errors can be broadly classified into two classes:
1. Syntax errors
2. Logical errors (Exceptions)
Python Syntax Errors
Error caused by not following the proper structure (syntax) of the language is
called syntax error or parsing error.
Let's look at one example:
>>> if a < 3
File "<interactive input>", line 1
if a < 3
^
SyntaxError: invalid syntax
As shown in the example, an arrow indicates where the parser ran into the
syntax error.
We can notice here that a colon : is missing in the if statement.
Python Logical Errors (Exceptions)
Errors that occur at runtime (after passing the syntax test) are
called exceptions or logical errors.
For instance, they occur when we try to open a file(for reading) that does not
exist ( FileNotFoundError ), try to divide a number by zero ( ZeroDivisionError ), or
try to import a module that does not exist ( ImportError ).
Whenever these types of runtime errors occur, Python creates an exception
object. If not handled properly, it prints a traceback to that error along with
some details about why that error occurred.
Let's look at how Python treats these errors:
>>> 1 / 0
Traceback (most recent call last):
File "<string>", line 301, in runcode
File "<interactive input>", line 1, in <module>
ZeroDivisionError: division by zero
>>> open("imaginary.txt")
Traceback (most recent call last):
File "<string>", line 301, in runcode
File "<interactive input>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'imaginary.txt'
Python Built-in Exceptions
Illegal operations can raise exceptions. There are plenty of built-in exceptions
in Python that are raised when corresponding errors occur. We can view all
the built-in exceptions using the built-in local() function as follows:
print(dir(locals()['__builtins__']))
Run Code
locals()['__builtins__'] will return a module of built-in exceptions, functions,
and attributes. dir allows us to list these attributes as strings.
Some of the common built-in exceptions in Python programming along with
the error that cause them are listed below:
Exception Cause of Error
AssertionError Raised when an assert statement fails.
AttributeError Raised when attribute assignment or reference fails.
Raised when the input() function hits end-of-file
EOFError
condition.
FloatingPointError Raised when a floating point operation fails.
GeneratorExit Raise when a generator's close() method is called.
ImportError Raised when the imported module is not found.
IndexError Raised when the index of a sequence is out of range.
KeyError Raised when a key is not found in a dictionary.
Raised when the user hits the interrupt key ( Ctrl+C
KeyboardInterrupt
or Delete ).
MemoryError Raised when an operation runs out of memory.
Raised when a variable is not found in local or
NameError
global scope.
NotImplementedError Raised by abstract methods.
Raised when system operation causes system
OSError
related error.
Raised when the result of an arithmetic operation is
OverflowError
too large to be represented.
Raised when a weak reference proxy is used to
ReferenceError
access a garbage collected referent.
Raised when an error does not fall under any other
RuntimeError
category.
Raised by next() function to indicate that there is
StopIteration
no further item to be returned by iterator.
SyntaxError Raised by parser when syntax error is encountered.
IndentationError Raised when there is incorrect indentation.
Raised when indentation consists of inconsistent
TabError
tabs and spaces.
SystemError Raised when interpreter detects internal error.
SystemExit Raised by sys.exit() function.
Raised when a function or operation is applied to an
TypeError
object of incorrect type.
Raised when a reference is made to a local variable
UnboundLocalError in a function or method, but no value has been
bound to that variable.
Raised when a Unicode-related encoding or
UnicodeError
decoding error occurs.
Raised when a Unicode-related error occurs during
UnicodeEncodeError
encoding.
Raised when a Unicode-related error occurs during
UnicodeDecodeError
decoding.
UnicodeTranslateErro Raised when a Unicode-related error occurs during
r translating.
Raised when a function gets an argument of correct
ValueError
type but improper value.
Raised when the second operand of division or
ZeroDivisionError
modulo operation is zero.
If required, we can also define our own exceptions in Python. To learn more
about them, visit Python User-defined Exceptions.
We can handle these built-in and user-defined exceptions in Python
using try , except and finally statements. To learn more about them,
visit Python try, except and finally statements.
Python Exception Handling Using
try, except and finally statement
In this tutorial, you'll learn how to handle exceptions in your Python program
using try, except and finally statements with the help of examples.
Exceptions in Python
Python has many built-in exceptions that are raised when your program
encounters an error (something in the program goes wrong).
When these exceptions occur, the Python interpreter stops the current
process and passes it to the calling process until it is handled. If not handled,
the program will crash.
For example, let us consider a program where we have a function A that calls
function B , which in turn calls function C . If an exception occurs in
function C but is not handled in C , the exception passes to B and then to A .
If never handled, an error message is displayed and our program comes to a
sudden unexpected halt.
Catching Exceptions in Python
In Python, exceptions can be handled using a try statement.
The critical operation which can raise an exception is placed inside
the try clause. The code that handles the exceptions is written in
the except clause.
We can thus choose what operations to perform once we have caught the
exception. Here is a simple example.
# import module sys to get the type of exception
import sys
randomList = ['a', 0, 2]
for entry in randomList:
try:
print("The entry is", entry)
r = 1/int(entry)
break
except:
print("Oops!", sys.exc_info()[0], "occurred.")
print("Next entry.")
print()
print("The reciprocal of", entry, "is", r)
Run Code
Output
The entry is a
Oops! <class 'ValueError'> occurred.
Next entry.
The entry is 0
Oops! <class 'ZeroDivisionError'> occured.
Next entry.
The entry is 2
The reciprocal of 2 is 0.5
In this program, we loop through the values of the randomList list. As previously
mentioned, the portion that can cause an exception is placed inside
the try block.
If no exception occurs, the except block is skipped and normal flow
continues(for last value). But if any exception occurs, it is caught by
the except block (first and second values).
Here, we print the name of the exception using the exc_info() function
inside sys module. We can see
that a causes ValueError and 0 causes ZeroDivisionError .
Since every exception in Python inherits from the base Exception class, we can
also perform the above task in the following way:
# import module sys to get the type of exception
import sys
randomList = ['a', 0, 2]
for entry in randomList:
try:
print("The entry is", entry)
r = 1/int(entry)
break
except Exception as e:
print("Oops!", e.__class__, "occurred.")
print("Next entry.")
print()
print("The reciprocal of", entry, "is", r)
Run Code
This program has the same output as the above program.
Catching Specific Exceptions in Python
In the above example, we did not mention any specific exception in
the except clause.
This is not a good programming practice as it will catch all exceptions and
handle every case in the same way. We can specify which exceptions
an except clause should catch.
A try clause can have any number of except clauses to handle different
exceptions, however, only one will be executed in case an exception occurs.
We can use a tuple of values to specify multiple exceptions in an except
clause. Here is an example pseudo code.
try:
# do something
pass
except ValueError:
# handle ValueError exception
pass
except (TypeError, ZeroDivisionError):
# handle multiple exceptions
# TypeError and ZeroDivisionError
pass
except:
# handle all other exceptions
pass
Raising Exceptions in Python
In Python programming, exceptions are raised when errors occur at runtime.
We can also manually raise exceptions using the raise keyword.
We can optionally pass values to the exception to clarify why that exception
was raised.
>>> raise KeyboardInterrupt
Traceback (most recent call last):
...
KeyboardInterrupt
>>> raise MemoryError("This is an argument")
Traceback (most recent call last):
...
MemoryError: This is an argument
>>> try:
... a = int(input("Enter a positive integer: "))
... if a <= 0:
... raise ValueError("That is not a positive number!")
... except ValueError as ve:
... print(ve)
...
Enter a positive integer: -2
That is not a positive number!
Python try with else clause
In some situations, you might want to run a certain block of code if the code
block inside try ran without any errors. For these cases, you can use the
optional else keyword with the try statement.
Note: Exceptions in the else clause are not handled by the preceding except
clauses.
Let's look at an example:
# program to print the reciprocal of even numbers
try:
num = int(input("Enter a number: "))
assert num % 2 == 0
except:
print("Not an even number!")
else:
reciprocal = 1/num
print(reciprocal)
Run Code
Output
If we pass an odd number:
Enter a number: 1
Not an even number!
If we pass an even number, the reciprocal is computed and displayed.
Enter a number: 4
0.25
However, if we pass 0, we get ZeroDivisionError as the code block
inside else is not handled by preceding except .
Enter a number: 0
Traceback (most recent call last):
File "<string>", line 7, in <module>
reciprocal = 1/num
ZeroDivisionError: division by zero
Python try...finally
The try statement in Python can have an optional finally clause. This clause
is executed no matter what, and is generally used to release external
resources.
For example, we may be connected to a remote data center through the
network or working with a file or a Graphical User Interface (GUI).
In all these circumstances, we must clean up the resource before the program
comes to a halt whether it successfully ran or not. These actions (closing a
file, GUI or disconnecting from network) are performed in the finally clause to
guarantee the execution.
Here is an example of file operations to illustrate this.
try:
f = open("test.txt",encoding = 'utf-8')
# perform file operations
finally:
f.close()
This type of construct makes sure that the file is closed even if an exception
occurs during the program execution.
Python Custom Exceptions
In this tutorial, you will learn how to define custom exceptions depending upon
your requirements with the help of examples.
Python has numerous built-in exceptions that force your program to output an
error when something in the program goes wrong.
However, sometimes you may need to create your own custom exceptions
that serve your purpose.
Creating Custom Exceptions
In Python, users can define custom exceptions by creating a new class. This
exception class has to be derived, either directly or indirectly, from the built-
in Exception class. Most of the built-in exceptions are also derived from this
class.
>>> class CustomError(Exception):
... pass
...
>>> raise CustomError
Traceback (most recent call last):
...
__main__.CustomError
>>> raise CustomError("An error occurred")
Traceback (most recent call last):
...
__main__.CustomError: An error occurred
Here, we have created a user-defined exception called CustomError which
inherits from the Exception class. This new exception, like other exceptions,
can be raised using the raise statement with an optional error message.
When we are developing a large Python program, it is a good practice to
place all the user-defined exceptions that our program raises in a separate
file. Many standard modules do this. They define their exceptions separately
as exceptions.py or errors.py (generally but not always).
User-defined exception class can implement everything a normal class can
do, but we generally make them simple and concise. Most implementations
declare a custom base class and derive others exception classes from this
base class. This concept is made clearer in the following example.
Example: User-Defined Exception in Python
In this example, we will illustrate how user-defined exceptions can be used in
a program to raise and catch errors.
This program will ask the user to enter a number until they guess a stored
number correctly. To help them figure it out, a hint is provided whether their
guess is greater than or less than the stored number.
# define Python user-defined exceptions
class Error(Exception):
"""Base class for other exceptions"""
pass
class ValueTooSmallError(Error):
"""Raised when the input value is too small"""
pass
class ValueTooLargeError(Error):
"""Raised when the input value is too large"""
pass
# you need to guess this number
number = 10
# user guesses a number until he/she gets it right
while True:
try:
i_num = int(input("Enter a number: "))
if i_num < number:
raise ValueTooSmallError
elif i_num > number:
raise ValueTooLargeError
break
except ValueTooSmallError:
print("This value is too small, try again!")
print()
except ValueTooLargeError:
print("This value is too large, try again!")
print()
print("Congratulations! You guessed it correctly.")
Run Code
Here is a sample run of this program.
Enter a number: 12
This value is too large, try again!
Enter a number: 0
This value is too small, try again!
Enter a number: 8
This value is too small, try again!
Enter a number: 10
Congratulations! You guessed it correctly.
We have defined a base class called Error .
The other two exceptions ( ValueTooSmallError and ValueTooLargeError ) that are
actually raised by our program are derived from this class. This is the
standard way to define user-defined exceptions in Python programming, but
you are not limited to this way only.
Customizing Exception Classes
We can further customize this class to accept other arguments as per our
needs.
To learn about customizing the Exception classes, you need to have the basic
knowledge of Object-Oriented programming.
Visit Python Object Oriented Programming to start learning about Object-
Oriented programming in Python.
Let's look at one example:
class SalaryNotInRangeError(Exception):
"""Exception raised for errors in the input salary.
Attributes:
salary -- input salary which caused the error
message -- explanation of the error
"""
def __init__(self, salary, message="Salary is not in (5000, 15000) range"):
self.salary = salary
self.message = message
super().__init__(self.message)
salary = int(input("Enter salary amount: "))
if not 5000 < salary < 15000:
raise SalaryNotInRangeError(salary)
Run Code
Output
Enter salary amount: 2000
Traceback (most recent call last):
File "<string>", line 17, in <module>
raise SalaryNotInRangeError(salary)
__main__.SalaryNotInRangeError: Salary is not in (5000, 15000) range
Here, we have overridden the constructor of the Exception class to accept our
own custom arguments salary and message . Then, the constructor of the
parent Exception class is called manually with the self.message argument
using super() .
The custom self.salary attribute is defined to be used later.
The inherited __str__ method of the Exception class is then used to display the
corresponding message when SalaryNotInRangeError is raised.
We can also customize the __str__ method itself by overriding it.
class SalaryNotInRangeError(Exception):
"""Exception raised for errors in the input salary.
Attributes:
salary -- input salary which caused the error
message -- explanation of the error
"""
def __init__(self, salary, message="Salary is not in (5000, 15000) range"):
self.salary = salary
self.message = message
super().__init__(self.message)
def __str__(self):
return f'{self.salary} -> {self.message}'
salary = int(input("Enter salary amount: "))
if not 5000 < salary < 15000:
raise SalaryNotInRangeError(salary)
Run Code
Output
Enter salary amount: 2000
Traceback (most recent call last):
File "/home/bsoyuj/Desktop/Untitled-1.py", line 20, in <module>
raise SalaryNotInRangeError(salary)
__main__.SalaryNotInRangeError: 2000 -> Salary is not in (5000, 15000) range
To learn more about how you can handle exceptions in Python, visit Python
Exception Handling.