Module 3
Module 3
Dictionaries
Dictionaries are yet another kind of compound type. They are Python’s built-in mapping
type. They map keys, which can be any immutable type, to values, which can be any type
(heterogeneous), just like the elements of a list or tuple. In other languages, they are called
associative arrays since they associate a key with a value.
Python dictionary is a data structure that stores the value in key: value pairs. Values in a
dictionary can be of any data type and can be duplicated, whereas keys can't be repeated and
must be immutable.
Keys are case sensitive which means same name but different cases of Key will be
treated distinctly.
Keys must be immutable which means keys can be strings, numbers or tuples but not
lists.
Duplicate keys are not allowed and any duplicate key will overwrite the previous
value.
Internally uses hashing. Hence, operations like search, insert, delete can be performed
in Constant Time.
One way to create a dictionary is to start with the empty dictionary and add key:value pairs.
The empty dictionary is denoted {}:
Dictionary can be created by placing a sequence of elements within curly {} braces, separated
by a 'comma'. Each pair contains a key and a value separated by a colon.
As an example, we will create a dictionary to translate English words into Spanish. For this
dictionary, the keys are strings.
>>> english_spanish = {}
>>> english_spanish["one"] = "uno"
>>> english_spanish["two"] = "dos"
>>> print(english_spanish)
{"two": "dos", "one": "uno"}
We can access a value from a dictionary by using the key within square brackets
or get() method.
We can add new key-value pairs or update existing keys by using assignment.
print(d)
Output
Output
{1: 'Python', 2: 'Programming', 3: 'Class'}
Python
Key: 3, Value: ‘Class’
{}
We can iterate over keys [using keys() method] , values [using values() method] or both
[using item() method] with a for loop.
d = {1: 'abc', 2: 'For', 'age':22}
Hashing
The order of the pairs may not be what was expected. Python uses complex algorithms,
designed for very fast access, to determine where the key:value pairs are stored in a
dictionary. For our purposes we can think of this ordering as unpredictable. We also might
wonder why we use dictionaries at all when the same concept of mapping a key to a value
could be
implemented using a list of tuples:
The reason is dictionaries are very fast, implemented using a technique called hashing, which
allows us to access a value very quickly. By contrast, the list of tuples implementation is
slow. If we wanted to find a value associated with a key, we would have to iterate over every
tuple, checking the 0th element. What if the key wasn’t even in the list? We would have to get
to the end of it to find out.
Another way to create a dictionary is to provide a list of key:value pairs using the same
syntax as the previous output:
>>> english_spanish = {"one": "uno", "two": "dos", "three": "tres"}
It doesn’t matter what order we write the pairs. The values in a dictionary are accessed with
keys, not with indices, so there is no need to care about ordering.
Lists, tuples, and strings have been called sequences, because their items occur in order.
Dictionary operations
The del statement removes a key:value pair from a dictionary. For example, the following
dictionary contains the names of various fruits and the number of each fruit in stock:
>>> inventory = {"apples": 430, "bananas": 312, "oranges": 525, "pears": 217}
>>> print(inventory)
{'pears': 217, 'apples': 430, 'oranges': 525, 'bananas': 312}
If someone buys all of the bananas, we can remove the entry from the dictionary:
If we’re expecting more bananas soon, we might just change the value associated with
bananas:
>>> inventory["bananas"] = 0
>>> print(inventory)
{'pears': 217, 'apples': 430, 'oranges': 525, 'bananas': 0}
The len function also works on dictionaries; it returns the number of key:value pairs:
>>> len(inventory)
4
Dictionary methods
Dictionaries have a number of useful built-in methods. The keys method returns what Python
3 calls a view of its underlying keys.
It is so common to iterate over the keys in a dictionary that we can omit the keys method call
in the for loop —iterating over a dictionary implicitly iterates over its keys:
The values method is similar; it returns a view object which can be turned into a list:
>>> list(english_spanish.values())
['tres', 'dos', 'uno']
The items method also returns a view, which promises a list of tuples— one tuple for each
key:value pair:
>>> list(english_spanish.items())
[('three', 'tres'), ('two', 'dos'), ('one', 'uno')]
Tuples are often useful for getting both the key and the value at the same time while we are
looping:
for (key,value) in english_spanish.items():
print("Got",key,"that maps to",value)
This produces:
Got three that maps to tres
Got two that maps to dos
Got one that maps to uno
This method can be very useful, since looking up a non-existent key in a dictionary causes a
runtime error:
>>> english_spanish["dog"]
Traceback (most recent call last):
...
KeyError: 'dog'
As in the case of lists, because dictionaries are mutable, we need to be aware of aliasing.
Whenever two variables refer to the same object, changes to one affect the other.
If we want to modify a dictionary and keep a copy of the original, use the copy method. For
example, opposites is a dictionary that contains pairs of opposites:
alias and opposites refer to the same object; copy refers to a fresh copy of the same
dictionary. If we modify alias, opposites is also changed:
>>> opposites["right"]
'left'
>>> opposites["right"]
'left'
Counting letters
We have seen a Function that counted the number of occurrences of a letter in a string. A
more general version of this problem is to form a frequency table of the letters in the string,
that is, how many times each letter appears. Such a frequency table might be useful for
compressing a text file. Because different letters appear with different frequencies, we can
compress a file by using shorter codes for common letters and longer codes for letters that
appear less frequently.
>>> letter_counts = {}
letter_counts[letter] = letter_counts.get(letter, 0) + 1
>>> letter_counts
We start with an empty dictionary. For each letter in the string, we find the current count
(possibly zero) and increment it. At the end, the dictionary contains pairs of letters and their
frequencies. It might be more appealing to display the frequency table in alphabetical order.
We can do that with the items and sort methods (more precisely, sort orders
lexicographically):
>>> letter_items.sort()
>>> print(letter_items)
Notice in the first line we had to call the type conversion function list. That turns the promise
we get from items into a list, a step that is needed before we can use the list’s sort method.
Numpy
NumPy (Numerical Python) is a fundamental Python library for numerical computing,
particularly known for its efficient handling of large, multi-dimensional arrays and matrices.
ndarray object:
Broadcasting:
NumPy's broadcasting mechanism allows operations on arrays of different shapes and sizes
by automatically "stretching" the smaller array to match the larger one, provided their
dimensions are compatible.
NumPy provides a rich set of universal functions (ufuncs) that enable element-wise
operations on arrays without explicit Python loops, leading to significant performance gains.
Mathematical Functions:
NumPy arrays support powerful and flexible indexing and slicing, similar to Python lists but
extended for multi-dimensional access.
NumPy includes a robust module for generating various types of random numbers, essential
for simulations and statistical analyses.
NumPy integrates seamlessly with other scientific computing libraries in Python, such as
SciPy, Matplotlib, and Pandas, forming the backbone of the data science ecosystem.
Performance:
NumPy operations are often implemented in optimized C/C++ or Fortran code, resulting in
significantly faster execution compared to native Python list operations, especially for large
datasets.
Open your Command Prompt or terminal and run the following code
The standard Python data types are not very suited for mathematical operations. For example,
suppose we have the
>>> a = [2, 3, 8]
>>> 2 * a
[2, 3, 8, 2, 3, 8]
>>> a = [2, 3, 8]
>>> 2 * a
>>> 2.1 * a
In order to solve this using Python lists, we would have to do something like:
values = [2, 3, 8]
result = []
for x in values:
[Link](2.1 * x)
because Python list’s are not designed as mathematical objects. Rather, they are purely a
collection of items. In order to get a type of list which behaves like a mathematical array or
matrix, we use Numpy.
>>> 2.1 * a
>>> a * a
array([ 4, 9, 64])
>>> a**2
array([ 4, 9, 64])
In Python we have lists that serve the purpose of arrays, but they are slow to process.
NumPy aims to provide an array object that is up to 50x faster than traditional Python lists.
The array object in NumPy is called ndarray, it provides a lot of supporting functions that
make working with ndarray very easy.
Arrays are very frequently used in data science, where speed and resources are very
important.
NumPy arrays are stored at one continuous place in memory unlike lists, so processes can
access and manipulate them very efficiently.
This is the main reason why NumPy is faster than lists. Also it is optimized to work with
latest CPU architectures.
NumPy is a Python library and is written partially in Python, but most of the parts that require
fast computation are written in C or C++.
Import NumPy
Once NumPy is installed, import it in your applications by adding the import keyword:
import numpy
import numpy
arr = [Link]([1, 2, 3, 4, 5])
print(arr)
import numpy as np
import numpy as np
arr = [Link]([1, 2, 3, 4, 5])
print(arr)
Example
import numpy as np
print(np.__version__)
NumPy is used to work with arrays. The array object in NumPy is called ndarray.
import numpy as np
arr = [Link]([1, 2, 3, 4, 5])
print(arr)
print(type(arr))
To create an ndarray, we can pass a list, tuple or any array-like object into the array() method,
and it will be converted into an ndarray:
Example
Use a tuple to create a NumPy array:
import numpy as np
arr = [Link]((1, 2, 3, 4, 5))
print(arr)
Dimensions in Arrays
A dimension in arrays is one level of array depth (nested arrays).
0-D Arrays
0-D arrays, or Scalars, are the elements in an array. Each value in an array is a 0-D array.
Example
Create a 0-D array with value 42
import numpy as np
arr = [Link](42)
print(arr)
1-D Arrays
An array that has 0-D arrays as its elements is called uni-dimensional or 1-D array.
These are the most common and basic arrays.
Example
Create a 1-D array containing the values 1,2,3,4,5:
Dept. of CSE (Data Science) 13
Python Programming [1BPLC105B] Module 3
import numpy as np
arr = [Link]([1, 2, 3, 4, 5])
print(arr)
2-D Arrays
An array that has 1-D arrays as its elements is called a 2-D array.
These are often used to represent matrix or 2nd order tensors.
Create a 2-D array containing two arrays with the values 1,2,3 and 4,5,6:
import numpy as np
arr = [Link]([[1, 2, 3], [4, 5, 6]])
print(arr)
a = [Link](42)
b = [Link]([1, 2, 3, 4, 5])
c = [Link]([[1, 2, 3], [4, 5, 6]])
print([Link])
print([Link])
print([Link])
import numpy as np
arr = [Link]([1, 2, 3, 4])
print(arr[1])
import numpy as np
arr = [Link]([1, 2, 3, 4])
print(arr[2] + arr[3])
Shape
One of the most important properties an array is its shape. We have already seen 1
dimensional (1D) arrays, but arrayscan have any dimensions you like. Images for example,
consist of a 2D array of pixels. But in color images everypixel is an RGB tuple: the intensity
in red, green and blue. Every pixel itself is therefore an array as well. This makes a color
image 3D overall.
To get the shape of an array, we use shape:
>>> import numpy as np
>>> a = [Link]([2, 3, 8])
>>> [Link]
(3,)
>>> b = [Link]([
[2, 3, 8],
Dept. of CSE (Data Science) 15
Python Programming [1BPLC105B] Module 3
[4, 5, 6],
])
>>> [Link]
(2, 3)
Slicing
Slicing in python means taking elements from one given index to another given index.
We pass slice instead of index like this: [start:end].
We can also define the step, like this: [start:end:step].
If we don't pass start its considered 0
If we don't pass end its considered length of array in that dimension
If we don't pass step its considered 1
import numpy as np
arr = [Link]([1, 2, 3, 4, 5, 6, 7])
print(arr[1:5])
import numpy as np
arr = [Link]([1, 2, 3, 4, 5, 6, 7])
print(arr[4:])
numpy as np
arr = [Link]([1, 2, 3, 4, 5, 6, 7])
print(arr[:4])
Slice from the index 3 from the end to index 1 from the end:
import numpy as np
arr = [Link]([1, 2, 3, 4, 5, 6, 7])
print(arr[-3:-1])
import numpy as np
arr = [Link]([1, 2, 3, 4, 5, 6, 7])
print(arr[1:5:2])
import numpy as np
arr = [Link]([1, 2, 3, 4, 5, 6, 7])
print(arr[::2])
From the second element, slice elements from index 1 to index 4 (not included):
import numpy as np
arr = [Link]([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
print(arr[1, 1:4])
From both elements, slice index 1 to index 4 (not included), this will return a 2-D array:
import numpy as np
arr = [Link]([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
print(arr[0:2, 1:4])
>>> b = [Link]([
[2, 3, 8],
[4, 5, 6],
])
>>> b[1]
array([4, 5, 6])
>>> b[1][2]
6
Masking
This is the most common and straightforward method. A boolean array (the mask) is used
directly to index another array, returning only the elements where the mask is True.
import numpy as np
arr = [Link]([10, 20, 30, 40, 50])
mask = arr > 25 # Create a boolean mask for values greater than 25
filtered_arr = arr[mask]
print(filtered_arr)
2. Using [Link]():
The [Link]() function allows conditional selection of elements from two arrays or
conditional modification of an array. It takes a condition, a value to use if the condition
is True, and a value to use if the condition is False.
import numpy as np
arr = [Link]([1, 2, 3, 4, 5])
modified_arr = [Link](arr % 2 == 0, arr * 10, arr) # Multiply even numbers by 10
print(modified_arr)
import [Link] as ma
data = [Link]([1, 2, [Link], 4, 5])
masked_arr = ma.masked_invalid(data) # Mask NaN values
print(masked_arr)
print(masked_arr.mean()) # The mean will ignore the masked NaN value
Suppose we have an array, and we want to throw away all values above a certain cutoff:
>>> a = [Link]([230, 10, 284, 39, 76])
>>> cutoff = 200
>>> a > cutoff
[Link]([True, False, True, False, False])
Simply using the larger than operator lets us know in which cases the test was positive. Now
we set all the values above 200 to zero:
>>> a = [Link]([230, 10, 284, 39, 76])
>>> cutoff = 200
>>> a[a > cutoff] = 0
>>> a
[Link]([0, 10, 0, 39, 76])
The crucial line is a[a > cutoff] = 0. This selects all the points in the array where the test was
positive and assigns 0 to that position. Without knowing this trick we would have had to loop
over the array:
>>> a = [Link]([230, 10, 284, 39, 76])
>>> cutoff = 200
>>> new_a = []
>>> for x in a:
>>> if x > cutoff:
>>> new_a.append(0)
>>> else:
>>> new_a.append(x)
>>> a = [Link](new_a)
Broadcasting
arrays of different shapes. For instance
>>> a = [Link]([
[0, 1],
[2, 3],
[4, 5],
])
>>> b = [Link]([10, 100])
>>> a * b
array([[ 0, 100],
[ 20, 300],
[ 40, 500]])
The shapes of a and b don’t match. In order to proceed, Numpy will stretch b into a second
dimension, as if it were stacked three times upon itself. The operation then takes place
element-wise.
One of the rules of broadcasting is that only dimensions of size 1 can be stretched (if an array
only has one dimension, all other dimensions are considered for broadcasting purposes to
have size 1). In the example above b is 1D, and has shape (2,). For broadcasting with a,
which has two dimensions, Numpy adds another dimension of size 1 to b. b now has shape
(1, 2). This new dimension can now be stretched three times so that b’s shape matches a’s
shape of (3, 2).
The other rule is that dimensions are compared from the last to the first. Any dimensions that
do not match must be stretched to become equally sized. However, according to the previous
rule, only dimensions of size 1 can stretch. This means that some shapes cannot broadcast and
Numpy will give you an error:
>>> c = [Link]([
[0, 1, 2],
[3, 4, 5],
])
>>> b = [Link]([10, 100])
>>> c * b
ValueError: operands could not be broadcast together with shapes (2,3) (2,)
Dept. of CSE (Data Science) 19
Python Programming [1BPLC105B] Module 3
What happens here is that Numpy, again, adds a dimension to b, making it of shape (1, 2).
The sizes of the last dimensions of b and c (2 and 3, respectively) are then compared and
found to differ. Since none of these dimensions is of size 1 (therefore, unstretchable) Numpy
gives up and produces an error.
The solution to multiplying c and b above is to specifically tell Numpy that it must add that
extra dimension as the second dimension of b. This is done by using None to index that
second dimension. The shape of b then becomes (2, 1), which is compatible for broadcasting
with c:
>>> c = [Link]([
[0, 1, 2],
[3, 4, 5],
])
>>> b = [Link]([10, 100])
>>> c * b[:, None]
array([[ 0, 10, 20],
[300, 400, 500]])
dtype
A commonly used term in working with numpy is dtype - short for data type. This is typically
int or float, followed by some number, e.g. int8. This means the value is integer with a size of
8 bits.
As an example, let’s discuss the properties of an int8. Each bit is either 0 or 1. With 8 of
them, we have 28 = 256 possible values. Since we also have to count zero itself, the largest
possible value is 255. The data type we have now described is called uint8, where the u
stands for unsigned: only positive values are allowed. If we want to allow negative numbers
we use int8. The range then shifts to -128 to +127.
If you add two uint8, the result of 200 + 200 cannot be 400, because that doesn’t fit in a
uint8. In standard Python, Python does a lot of magic in the background to make sure the
result is the 400 you would expect. But numpy doesn’t, and will return 144. To fix this, we
should make sure that our numbers where not stored as uint8, but as something larger; uint16
for example. That way the resulting 400 will fit.
>>> import numpy as np
>>> a = [Link]([200], dtype='uint16')
>>> a + a
array([400], dtype=uint16)
Dept. of CSE (Data Science) 20
Python Programming [1BPLC105B] Module 3
Changing dtype
To change the dtype of an existing array, you can use the astype method:
>>> import numpy as np
>>> a = [Link]([200], dtype='uint8')
>>> [Link]('uint64')
FILES
File handling refers to the process of performing operations on a file, such as creating,
opening, reading, writing and closing it through a programming interface. It involves
managing the data flow between the program and the file system on the storage device,
ensuring that data is handled safely and efficiently.
Why do we need File Handling
To store data permanently, even after the program ends.
To access external files like .txt, .csv, .json, etc.
To process large files efficiently without using much memory.
To automate tasks like reading configs or saving outputs.
While a program is running, its data is stored in random access memory (RAM). RAM is fast
and inexpensive, but it is also volatile, which means that when the program ends, or the
computer shuts down, data in RAM disappears. To make data available the next time the
computer is turned on and the program is started, it has to be written to a non-volatile storage
medium, such a hard drive, usb drive, or CD-RW. Data on non-volatile storage media is
stored in named locations called files. By reading and writing files, programs can save
information between program runs.
Opening a File
To open a file, we can use open() function, which requires file-path and mode as arguments:
Syntax:
file = open('[Link]', 'mode')
[Link]: name (or path) of the file to be opened.
mode: mode in which you want to open the file (read, write, append, etc.).
Note: If we don’t specify the mode, Python uses 'r' (read mode) by default.
f = open('[Link]')
When you specify the filename only, it is assumed that the file is located in the same folder as
Python. If it is somewhere else, you can specify the exact path where the file is located.
f = open(r'C:\Python33\Scripts\[Link]')
While specifying the exact path, characters prefaced by \ (like \n \r \t etc.) are interpreted
as special characters. We can escape them using:
raw strings like r'C:\new\[Link]'
double backslashes like 'C:\\new\\[Link]'
Here are five different modes you can use to open the file:
Character Mode Description
Text
‘t’ Read and write strings from and to the file.
(default)
Read and write bytes objects from and to the [Link] mode
‘b’ Binary is used for all files that don’t contain text (e.g. images).
Because read mode ‘r’ and text mode ‘t’ are default modes, you do not need to specify them.
Read a File
Suppose you have the following file.
[Link]
First line of the file.
Second line of the file.
Third line of the file.
# Prints:
#
First line of the file.
# Second line of the file.
# Third line of the file.
By default, the read() method reads the entire file. However, you can specify the maximum
number of characters to read.
f = open('[Link]')
print([Link](3))
# Prints Fir
print([Link](5))
# Prints First
Read Lines
To read a single line from the file, use readline() method.
f = open('[Link]')
print([Link]())
# Prints First line of the file.
You can loop through an entire file line-by-line using a simple for loop.
f = open('[Link]')
Dept. of CSE (Data Science) 23
Python Programming [1BPLC105B] Module 3
for line in f:
print(line)
# Prints:
# First line of the file.
# Second line of the file.
# Third line of the file.
If we want to read all the lines in a file into a list of strings, use readlines() method.
# Read all the lines in a file into a list of strings
f = open('[Link]')
print([Link]())
# Prints:
# ['First line of the file.\n', 'Second line of the file.\n', 'Third line of the file.']
Write a File
Use the write() built-in method to write to an existing file. Remember that you need to open
the file in one of the writing modes (‘w’, ‘a’ or ‘r+’) first.
[Link]
Overwrite existing data.
[Link]
First line of the file.
Second line of the file.
Third line of the file. Append this text.
[Link]
Dept. of CSE (Data Science) 24
Python Programming [1BPLC105B] Module 3
---Overwrite content---
Second line of the file.
Third line of the file.
[Link]
New line 1
New line 2
New line 3
Close a File
It’s a good practice to close the file once you are finished with it. You don’t want an open file
running around taking up resources!
Use the close() function to close an open file.
f = open('[Link]')
[Link]()
There are two approaches to ensure that a file is closed properly, even in cases of error. The
first approach is to use the with keyword, which Python recommends, as it automatically
takes care of closing the file once it leaves the with block (even in cases of error).
with open('[Link]') as f:
print([Link]())
finally:
[Link]()
# write mode
f = open('[Link]', 'w')
# append mode
f = open('[Link]', 'a')
Delete a File
You can delete a file by importing the OS module and using its remove() method.
import os
[Link]('[Link]')
import os
if [Link]('[Link]'):
f = open('[Link]')
else:
print('The file does not exist.')
Random Access
Sometimes you need to read from the record in the middle of the file at that time you can use
the seek(offset, from_what) method. This function moves the file pointer from its current
position to a specific position.
The offset is the number of characters from the from_what parameter which has three
possible values:
0 – indicates the beginning of the file (default)
Here’s how you can use the seek() method to randomly access the contents of a file.
f = open('[Link]', 'rb+')
If you want to check the current position of the file pointer, use the tell() method.
f = open('[Link]')
# initial position
print([Link]())
# Prints 0
[Link]("Hello, world!\n")
with block make sure that the file get close even if an error occurs (power outages excluded).
Dept. of CSE (Data Science) 27
Python Programming [1BPLC105B] Module 3
all_lines.sort()
with open("[Link]") as f:
content = [Link]()
words = [Link]()
print("There are {0} words in the file.".format(len(words)))
An example
Here is a filter that copies one file to another, omitting any lines that begin with #:
def filter(oldfile, newfile):
with open(oldfile, "r") as infile, open(newfile, "w") as outfile:
for line in infile:
# Put any processing logic here
if not [Link]('#'):
[Link](line)
Directories
Files on non-volatile storage media are organized by a set of rules known as a file system.
File systems are made up of files and directories, which are containers for both files and other
directories. When we create a new file by opening it and writing, the new file goes in the
current directory (wherever we were when we ran the program). Similarly, when we open a
file for reading, Python looks for it in the current directory. If we want to open a file
somewhere else, we have to specify the path to the file, which is the name of the directory
(or folder) where the file is located:
>>> wordsfile = open("/usr/share/dict/words", "r")
Dept. of CSE (Data Science) 28
Python Programming [1BPLC105B] Module 3
This (Unix) example opens a file named words that resides in a directory named dict, which
resides in share, which resides in usr, which resides in the top-level directory of the system,
called /. It then reads in each line into a list using readlines, and prints out the first 5 elements
from that list. A Windows path might be "c:/temp/[Link]" or "c:\\temp\\[Link]".
Because backslashes are used to escape things like newlines and tabs, we need to write two
backslashes in a literal string to get one! So the length of these two strings is the same!
We cannot use / or \ as part of a filename; they are reserved as a delimiter between directory
and filenames. When working with files in directories it is a good idea to let Python deal with
all the slashes and escaping them. The [Link] module does this for different operating
systems. Using [Link]. join("directory", "filename") will automatically return
"directory/filename" on Unix/Linux, and "directory\\filename" on Windows. This can not
only be of great help when moving code from one system to another, or when sharing with
colleagues. It also means that you do not have to take care of it yourself, and it might save
you some issues with string handling. You can also explore the [Link] module for other
handy features that will help you handling files. The file /usr/share/dict/words should exist on
Unix-based systems, and contains a list of words in alphabetical order.
The Python libraries are pretty messy in places. But here is a very simple example that copies
the contents at some web URL to a local file.
import [Link]
url = "[Link]
destination_filename = "[Link]"
[Link](url, destination_filename)
The urlretrieve function— just one call— could be used to download any kind of content
from the Internet.
import requests
url = "[Link]
response = [Link](url)
print([Link])
import requests
url = "[Link]
response = [Link](url)
for line in response:
print(line)
Binary file handling in Python involves working with files that store data in a raw,
uninterpreted format, rather than as human-readable text. This includes files like images,
audio, video, or serialized Python objects.
Operations:
Text Files:
Can be opened and read by any text editor, allowing for easy viewing and modification of the
character content. Operations typically involve reading and writing character strings, with the
system handling character encoding and newline conversions.
Binary Files:
Require specialized software or code to interpret and manipulate their content. Operations
involve reading and writing raw bytes, often in chunks or according to a defined data
structure. Direct modification with a text editor can corrupt the file as the text editor might
misinterpret byte sequences or alter them during saving.