100% (1) 100% found this document useful (1 vote) 835 views 185 pages The Python Bible For Beginners
The step by step roadmap for aspiring programmers to achieve codeing mastery
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content,
claim it here .
Available Formats
Download as PDF or read online on Scribd
Go to previous items Go to next items
Save The Python Bible for Beginners For Later PYTHON
= :y]:{ 0
FOR BEGINNERS
ae eE Incy
creer grt PCr accra
3 BONUS
INCLUDEDThe Python Bible for Beginners
The Step-by-Step Roadmap for Aspiring Programmers to Achieve
Coding Mastery and Unlock Rewarding Tech Careers
Pacey IslasThe Python Bible for Beginners
© Copyright 2024 by Pacey Islas
Allrights reserved
This document is geared towards providing exact and reliable information with regards to the topic and
issue covered. The publication is sold with the idea that the publisher is not required to render accounting,
officially permitted, or otherwise, qualified services. If advice is necessary, legal or professional, a practiced
individual in the profession should be ordered. From a Declaration of Principles which was accepted and
approved equally by a Committee of the American Bar Association and a Committee of Publishers and
Associations. In no way is it legal to reproduce, duplicate, or transmit any part of this document in either
electronic means or in printed format. Recording of this publication is strictly prohibited and any storage
of this document is not allowed unless with written permission from the publisher.
Allrights reserved.
The information provided herein is stated to be truthful and consistent, in that any liability, in terms of
inattention or otherwise, by any usage or abuse of any policies, processes, or directions contained within
is the solitary and utter responsibility of the recipient reader. Under no circumstances will any legal
responsibility or blame be held against the publisher for any reparation, damages, or monetary loss due to
the information herein, either directly or indirectly. Respective authors own all copyrights not held by the
publisher. The information herein is offered for informational purposes solely, and is universal as so. The
presentation of the information is without contract or any type of guarantee assurance. The trademarks
that are used are without any consent, and the publication of the trademark is without permission or back-
ing by the trademark owner, All trademarks and brands within this book are for clarifying purposes only
and are the owned by the owners themselves, not affiliated with this document.TABLE OF CONTENTS
CHAPTER 1: DECODING THE FUNDAMENTALS OF PYTHON
What is Python, and Why Should You Learn It?
Setting Up Your Python Development Environment
Understanding Data Types and Variables
Operators and Expressions
Control structures: Conditional Statements and Loops
Debugging Your Code: Error Handling Essentials
CHAPTER 2: MASTERING PYTHON'S CORE DATA STRUCTURES
Exploring Strings and String Manipulation
Understanding Lists, Tuples, and Sets
Unleashing the Power of Dictionaries
Functional Programming with Python
Working with Files and File Handling
CHAPTER 3: LEVELING UP WITH FUNCTIONS AND MODULES
Defining and Calling Functions
Function Arguments and Return Values
Scope and Namespaces in Python
Creating and Importing ModulesExploring Python's Standard Library
Managing Dependencies with Pip and Virtual Environments
CHAPTER 4: OBJECT-ORIENTED PROGRAMMING WITH PYTHON
Introduction to Object-Oriented Programming (OOP)
Creating Classes and Instances
Class Attributes and Methods
. .
Special Methods and Operator Overloading
CHAPTER 5: DIVING INTO DATA ANALYSIS AND VISUALIZATION
Introduction to NumPy and sciPy
Pandas: Working with Tabular Data
Data Visualization with Matplotlib
Advanced Data Analysis Techniques
CHAPTER 6: BUILDING WEB APPLICATIONS WITH PYTHON
Introduction to Web Development with Python
Working with Flask: A Lightweight Web Framework
Django: A High-Level Web Framework
APIs and RESTful ServicesDeploying Your Python Web Applications
CHAPTER 7: AUTOMATING TASKS WITH PYTHON
Introduction to Automation with Python
Working with Files and Directories
Interacting with the Operating System
Web Scraping and Data Extraction
‘Task Scheduling and Cron Jobs
Automating Email and Text Messages
CHAPTER 8: ADVANCING YOUR PYTHON SKILLS
Re; Expressions in Python
Working with Databases (SQL and NoSQL)
Concurrent Programming with Python
Testing and Del ing Technic
3 EBOOK BONUS
AUTHOR BIO: PACEY ISLASCHAPTER 1
DECODING THE FUNDAMENTALS OF PYTHON
What is Python, and Why Should You Learn It?
Python is a high-level, general-purpose programming language that has gained immense popularity
among developers worldwide. Its simplicity, versatility, and extensive library support make it an ideal
choice for beginners and experienced programmers alike. Python's syntaxis clean and easy to read, empha-
sizing code readability and reducing the cost of program maintenance. This user-friendly language allows
you to express concepts in fewer lines of code compared to other programming languages, making it an
efficient tool for rapid development.
One of the primary reasons to learn Python is its versatility. Python is used across a wide range of domains,
including web development, scientific computing, data analysis, artificial intelligence, machine learning,
and more. Its extensive standard library and vast ecosystem of third-party packages enable developers to
tackle complex tasks with ease, Whether you want to build web applications, automate repetitive tasks,
analyze data, or create machine learning models, Python provides the tools and frameworks to accomplish
your goals.
Python's popularity has also led to a thriving community of developers who contribute to its growth and
provide support to newcomers. The Python Package Index (PyPI) hosts thousands of open-source libraries
and frameworks that extend Python's functionality, allowing you to leverage pre-built solutions and save
development time. The community actively maintains and updates these packages, ensuring that you have
access to the latest features and security patches.Learning Python opens up a world of opportunities in terms of career prospects. As businesses increasingly
rely on technology and data-driven decision-making, the demand for Python developers continues to rise.
Python's versatility makes it applicable to various industries, including finance, healthcare, e-commerce,
and more. Whether you aspire to become a web developer, data scientist, machine learning engineer, or au-
tomation specialist, Python provides a solid foundation to build your skills upon.
‘Moreover, Python's gentle learning curve makes it an excellent language for beginners. Its straightforward
syntax and readable code allow you to focus on problem-solving and logic rather than getting bogged down
by complex language constructs. Python's interactive shell enables you to experiment with code snippets
and get immediate feedback, facilitating a hands-on learning experience. The wealth of online resources,
tutorials, and books available for Python further supports your learning journey, providing guidance and
examples to help you master the language.
In addition to its technical advantages, Python also places a strong emphasis on code quality and best prac-
tices. The Python community values code readability, maintainability, and efficiency. By learning Python,
you develop good programming habits and gain exposure to industry-standard practices such as version
control, testing, and documentation. These skills are transferable to other programming languages and
contribute to your overall growth as a developer.
Furthermore, Python's integration capabilities make it a valuable tool in various workflows. It can seam-
lessly interact with other languages, databases, and tools, allowing you to leverage existing systems and
incrementally introduce Python into your projects. Python's ability to serve as a "glue" language enables
you to connect different components and create powerful solutions by combining the strengths of multi-
ple technologies.Setting Up Your Python Development Environment
Installing Python and an Integrated Development Environment (IDE) is the first step in your journey as a
Python programmer. Python is a versatile and beginner-friendly programming language that can be used
for a wide range of applications, from web development to data analysis and machine learning. To get
started, you'll need to download and install Python on your computer.
Visit the official Python website (https://www.python.org) and navigate to the Downloads section. Choose
the latest stable version of Python that is compatible with your operating system (Windows, macoS, or
Linux), Follow the installation instructions provided for your specific platform. During the installation
process, make sure to check the option to add Python to your system's PATH environment variable, as this
will allow you to run Python from the command line.
Once Pythonis installed, you can start writing and running Python code using a simple text editor and the
command line. However, using an IDE can greatly enhance your coding experience by providing features
such as syntax highlighting, autocompletion, debugging tools, and integrated version control.
Popular Python IDEs include:
+ PyCharm: Developed by JetBrains, PyCharm is a powerful IDE that offers a wide range of
features for Python development. It comes in two versions: community (free) and professional
(paid).
+ Visual Studio Code: Created by Microsoft, Visual Studio Code is a lightweight and extensible
code editor that supports multiple programming languages, including Python. It has a rich
ecosystem of extensions that can be installed to enhance its functionality.+ Jupyter Notebook: Jupyter Notebook is a web-based interactive development environment that
allows you to create and share documents containing live code, equations, visualizations, and
narrative text. It is particularly useful for data analysis and exploration.
+ Spyder: Spyder is an open-source IDE specifically designed for scientific computing and data
analysis with Python. It integrates well with popular scientific libraries such as NumPy, SciPy,
and Matplotlib.
To set up your chosen IDE, follow these general step:
+ Download and install the IDE from its official website.
+ Launch the IDE and create a new Python project or open an existing one.
+ Configure the IDE's settings according to your preferences, such as font size, color scheme, and
keyboard shortcuts.
+ Install any necessary plugins or extensions that can enhance your productivity or add func-
tionality specific to your project's requirements.
In addition to an IDE, you may also want to set up a virtual environment for your Python projects. A virtual
environments an isolated Python runtime that allows you to install packages and dependencies specificto
a project without affecting your system-wide Python installation. This is particularly useful when working
on multiple projects with different dependencies or collaborating with others.
To create a virtual environment, follow these steps:
+ Opena terminal or command prompt.
+ Navigate to your project's directory.+ Run the command python -m venv myeny, replacing "myenv" with your desired virtual envi-
ronment name.
+ Activate the virtual environment by running source myenv/bin/activate (Unix/Linux) or
myenv\Scripts\activate (Windows).
With your Python installation, IDE, and virtual environment set up, you're ready to start writing Python
code and exploring the vast ecosystem of libraries and frameworks available for Python development. Re-
member to keep your Python installation and packages up-to-date, and don't hesitate to experiment with
different IDEs and tools until you find the setup that best suits your needs and preferences.
Understanding Data Types and Variables
Data types and variables are fundamental concepts in programming that allow you to store and manipu-
late different kinds of information. To become a proficient programmer, it's essential to understand how
data types and variables work and how to use them effectively in your code.
In programming, a data type is a classification of data that determines the kind of value it can hold and the
operations that can be performed on it, Some common data types include:
+ Integers (int): Whole numbers, such as -5, 0, or 42.
+ Floating-point numbers (float or double): Numbers with decimal points, like 3.14 or -0.5.
+ Characters (char): Single letters, digits, or symbols, such as'a’,'7',or'$
+ Booleans (bool): Logical values that can only be true or false.
+ Strings (string): Sequences of characters, like "Hello, world!" or "123 Main St.".Each programming language has its own set of data types, but these are some of the most common ones
youll encounter,
Variables, on the other hand, are named storage locations in a program's memory that hold values of a
specific data type. You can think of variables as containers that store data for later use or manipulation. To
create a variable, you typically specify its data type and give it a name that follows certain naming conven-
tions, which may vary depending on the programming language you're using.
Here's an example of declaring and initializing variables in C++:
int age = 25;
double price
char grade ='A'
bool isStudent = true;
string name = "John Doe";
In this example, we declare variables of different data types (int, double, char, bool, and string) and assign
them initial values using the assignment operator (=).
995
When choosing variable names, it's important to use descriptive and meaningful names that reflect the
purpose or content of the variable. This makes your code more readable and easier to understand for your-
self and others who may work with your code in the future.
Throughout your program, you can access and manipulate the values stored in variables using various
operators and statements. For instance, you can perform arithmetic operations on numeric variables, con-
catenate strings, or use variables in conditional statements to control the flow of your program.
intx=5;
inty = 3;
int sum =x + y;// Addition
int difference = x- y; // Subtractionint product = x* y; // Multiplication
double quotient = (double)x / y; // Division (casting to double to preserve decimal places)
string firstName = “John";
string lastName = "Doe";
string fullName = firstName + "* + lastName; // String concatenation
int age = 18;
if (age >= 18) {
cout << "You are eligible to vote!" << endl;
}else{
cout << "You are not yet eligible to vote." << endl;
}
As you learn to program, you'll encounter more complex data types, such as arrays, structures, and objects,
which build upon the basic data types and allow you to organize and manipulate data in more sophisti-
cated ways.
Understanding data types and variables is crucial for writing effective and efficient code. By choosing the
appropriate data types for your variables and using them correctly, you can ensure that your program han-
dles data accurately and performs the desired operations. As you gain more experience in programming,
you'll become more comfortable working with different data types and variables, and you'll be able to cre-
ate more complex and powerful programs.
Operators and Expressions
Operators and expressions are fundamental concepts in programming that allow developers to perform
calculations, make comparisons, and manipulate data. Understanding how operators and expressions
work is crucial for writing efficient and effective code.Operators are symbols or keywords that perform specific operations on one or more operands. They can be
classified into several categories:
Arithmetic Operators:
+ Addition (+)
+ Subtraction (-)
+ Multiplication (*)
+ Division (/)
+ Modulus (%)
+ Increment (++)
+ Decrement (—-)
These operators are used to perform mathematical calculations on numeric operands. For example, 5 +
3 evaluates to 8,and 10%3 yields the remainder 1.
Comparison Operators:
+ Equal to(
+ Not equal to (!=)
+ Greater than (>)
+ Less than (<)
+ Greater than or equal to (>=)
+ Less than or equal to (<=)Comparison operators are used to compare two values and return a boolean result (true or false ). They are
commonly used in conditional statements and loops to make decisions based on the comparison outcome
Logical Operators:
Logical AND (&&)
Logical OR (|)
Logical NOT (!)
Logical operators are used to combine or negate boolean expressions. The logical AND operator ( &&&)
returns true ifboth operands are true, while the logical OR operator (|) returns true ifat least one oper-
and is true . The logical NOT operator (!) negates the boolean value ofits operand.
Assignment Operators:
simple assignment (=)
Addition assignment (+=)
Subtraction assignment (-=)
Multiplication assignment (*=
Division assignment
‘Modulus assignment (%=)
Assignment operators are used to assign values to variables. The simple assignment operator ( = ) assigns
the value on the right-hand side to the variable on the left-hand side. The compound assignment operators
perform the specified operation and then assign the result back to the variable.Expressions are combinations of operators, operands, and function calls that evaluate to a single value.
They can beas simple as a single literal value or as complex as a series of operations involving multiple op-
erators and operands.
Here are a few examples of expression:
+ 243*4: This expression uses the arithmetic operators + and *. The multiplication is per-
formed first due to operator precedence, resulting in 2 +12, which evaluates to 14.
+ > 5 &&y< 10: This expression uses the comparison operators > and < alongwith the logical
AND operator && . It evaluates to true if both conditions are satisfied.
+ (a+b)/(c-d): This expression uses parentheses to specify the order of operations. The addi-
tion and subtraction are performed first, and then the division is applied to the results.
When working with operators and expressions, it's important to consider operator precedence and asso-
ciativity. Operator precedence determines the order in which operators are evaluated, while associativity
determines the order in which operators with the same precedence are grouped.
Parentheses can be used to override the default precedence and explicitly specify the desired order of eval-
uation. It's good practice to use parentheses to improve code readability and avoid potential ambiguity.
Expressions can be used in various contexts, such as variable assignments, conditional statements, loop
conditions, and function arguments. They allow you to perform calculations, make decisions, and manip-
ulate data based on the values of variables and the results of operations.Understanding operators and expressions is essential for writing effective code. By combining different
operators and operands, you can create complex expressions that solve specific problems and control the
flow of your program.
As you progress in your programming journey, you'll encounter more advanced operators and expressions,
such as bitwise operators, ternary operators, and short-circuit evaluation. Mastering these concepts will
empower you to write more efficient and sophisticated code.
Remember to always consider the readability and maintainability of your expressions. Use appropriate
spacing, follow consistent formatting, and add comments when necessary to clarify the purpose and logic
behind your expressions.
Control Structures: Conditional Statements and Loops
Control structures are fundamental building blocks in Python programming, allowing developers to con-
trol the flow of their programs based on specific conditions and repetitive tasks. Conditional statements
and loops are two essential control structures that every Python programmer should master to create dy-
namic and efficient code.
Conditional statements, such as if, elif, and else, enable programmers to make decisions based on whether
a condition is true or false. The if statement is the most basic form of a conditional statement, executing a
block of code only if the specified condition is true. For example:
x=5
ifx> 0:
print("x is positive")
In this case, the code block will be executed because the condition x > is true.The elif statement, short for "else if," allows programmers to check for multiple conditions. It is used when
the first condition is false, and you want to check for another condition. Multiple elif statements can be
chained together to handle various scenarios:
x=0
ifx>0:
print("x is positive")
elifx<
print("x is negative")
else:
print("x is zero")
Here, the program will print "x is zero" because the first two conditions are false.
The else statement is used to specify a block of code that should be executed if all the preceding conditions
are false. It provides a catch-all case for situations not covered by the if and elif statements.
Loops, on the other hand, allow programmers to repeatedly execute a block of code until a specific condi-
tion is met. Python offers two primary types of loops: for loops and while loops.
For loops are used to iterate over a sequence (such as a list, tuple, or string) or other iterable objects. The
loop executes a block of code for each item in the sequence. For example:
fruits = ["apple", "banana’, “cherry"]
for fruit in fruits:
print(fruit)
This code will output each fruit name ona separate line.
While loops execute a block of code as long as a given condition is true. The loop continues to run until the
condition becomes false. It's crucial to ensure that the condition eventually becomes false to avoid an infi-
nite loop. For example:count = 0
while count < 5:
print(count)
count += 1
This loop will print the numbers 0 through 4 and then exit once count reaches 5.
Python also provides the break and continue statements to modify the behavior of loops. The break state-
ment allows you to exit a loop prematurely when a specific condition is met, while the continue statement
skips the rest of the current iteration and moves on to the next one.
‘Mastering conditional statements and loops is crucial for creating dynamic and efficient Python programs.
By understanding how to control the flow of your code based on conditions and repetitive tasks, you'll be
able to tackle more complex programming challenges and develop sophisticated applications.
‘As you progress in your Python journey, you'll encounter more advanced control structures and tech-
niques, such as nested loops, list comprehensions, and generator expressions. These tools will further en-
hance your ability to write concise and powerful code.
Remember, practice is key to becoming proficient in using control structures. Experiment with different
conditions, sequences, and loop types to solidify your understanding. Don't be afraid to make mistakes —
debugging and problem-solving are essential skills that will help you grow as a programmer.
By leveraging conditional statements and loops effectively, youll be well on your way to creating robust
and dynamic Python programs that can handle a wide range of scenarios and tasks.
Debugging Your Code: Error Handling Essentials
Debugging is an essential skill for every programmer, regardless of their level of experience. It involves
identifying, locating, and fixing errors or bugs in your code. Whether you're a beginner or an experienceddeveloper, you will inevitably encounter errors during the development process. Understanding how to
effectively debug your code can save you countless hours of frustration and ensure that your programs run
smoothly.
One of the most important aspects of debugging is error handling, Error handling refers to the process of
anticipating, detecting, and responding to errors that may occur during program execution. By implement-
ing proper error handling techniques, you can gracefully handle exceptions, provide meaningful error
messages, and prevent your program from crashing unexpectedly.
When an error occurs in your code, Python raises an exception. Exceptions are objects that represent errors
or exceptional conditions. They disrupt the normal flow of your program and, if not handled properly, can
cause your program to terminate abruptly. Python provides a robust exception handling mechanism that
allows you to catch and handle exceptions gracefully.
The try-except statements the primary tool for handling exceptions in Python. It allows you to enclose a
block of code that may raise an exception within a try block. If an exception occurs within the try block,
the program flow is immediately transferred to the corresponding except block. The except block speci-
fies the type of exception it can handle and contains the code to handle that exception.
Here's a basic example of using try-except for error handling:
# Code that may raise an exception
result = 10 / 0 # Division by zero
except ZeroDivisionError:
# Code to handle the ZeroDivisionError exception
print("Error: Division by zero!")In this example, the code inside the try block attempts to divide 10 by zero, which raises a ZeroDivision-
Exror exception. The program flow is then transferred to the except block, which handles the ZeroDivi-
sionError exception and prints an error message.
You can have multiple except blocks to handle different types of exceptions. It's important to be specific
about the exceptions you catch to avoid masking unintended errors. You can also use the else block to
specify code that should run if no exceptions occur, and the finally block to define cleanup code that al-
ways runs, regardless of whether an exception was raised or not.
Effective error handling goes beyond just using try-except statements. It also involves providing infor-
mative error messages and logging. When an exception occurs, it's crucial to give the user or developer
meaningful information about what went wrong. You can achieve this by including relevant details in your
error messages, such as the type of exception, the line of code where the error occurred, and any relevant
variable values.
Logging is another powerful technique for debugging and error handling. By strategically placing log state-
ments throughout your code, you can track the flow of execution, monitor variable values, and identify the
source of errors. Python's built-in logging module provides a flexible and configurable logging system that
allows you to log messages at different levels of severity, such as debug, info, warming, error, and critical.
In addition to error handling, debugging involves using appropriate tools and techniques to identify and
fix bugs in your code, Python provides an interactive debugger called pdb that allows you to step through
your code line by line, inspect variables, and identify the cause of errors. Integrated Development Envi-
ronments (IDEs) like PyCharm, Visual Studio Code, and Jupyter Notebook also offer debugging capabilities,
making it easier to detect and fix issues in your code.Debugging and error handling are iterative processes. As you encounter errors, you'll need to investigate
the cause, make necessary corrections, and test your code again. It's important to approach debugging
systematically and break down complex problems into smaller, manageable parts. Utilize print statements,
logging, and debugging tools to gather information about the state of your program at different points of
execution.
‘As you gain more experience with debugging, you'll develop a keen eye for identifying common pitfalls and
errors. Youll learn to anticipate potential issues and proactively handle them in your code. Debugging is
not just about fixing errors; it's also an opportunity to learn from mistakes, improve your problem-solving
skills, and write more robust and reliable code.CHAPTER 2
MASTERING PYTHON'S CORE DATA STRUCTURES
Exploring Strings and String Manipulation
Strings area fundamental data type in Python, representing sequences of characters. They are used to store
and manipulate text-based information, such as names, addresses, and messages. Understanding how to
work with strings is essential for any Python programmer, as they form the basis for many common pro-
gramming tasks, from user input validation to data processing and text analysis.
Creating and Accessing Strings:
To create a string in Python, simply enclose a sequence of characters in either single quotes (") or double
quotes (""). For example:
my_string = 'Hello, world!’
another_string = "Python is awesome!”
You can access individual characters within a string using square brackets and an index. In Python, string
indices start at 0, meaning the first character is at index 0, the second character is at index 1, and so on. You
can also use negative indices to access characters from the end of the string, with -1 representing the last
character, -2 representing the second-to-last character, and so on.
my_string = ‘Hello, world!
print(my_string[0}) # Output
print(my_string[-1]) # Outp
String Slicing:
Python allows you to extract substrings from a string using a technique called slicing. To slice a string, usesquare brackets with a start index, an end index, and an optional step value, separated by colons.
my_string = 'Hello, world!’
print(my_string[0:5]) # Output: ‘Hello’
print(my_string|7: itput: ‘world
print(my_string[::2]) # Output: Ho ol!"
String Methods:
Python provides a wide range of built-in methods for manipulating and processing strings. Some com-
monly used string methods include:
+ lower() and upper() : Convert a string to lowercase or uppercase, respectively.
+ strip(), Istrip(), and rstrip0 : Remove whitespace characters from the beginning and/or end of
astring.
+ split) and join(): Split a string into a list of substrings based on a delimiter, or join a list of
strings into a single string using a separator.
+ replace() : Replace occurrences of a substring with another substring.
+ startswith() and endswith() : Check if a string starts or ends with a specific substring.
+ isdigit(), isalpha(), and isalnum() : Check if a string contains only digits, alphabetic charac-
ters, or alphanumeric characters, respectively,
my_string =' Hello, World! *
print(my_string.lower()) # Output:' hello, world! '
print(my_string.strip()) # Output: ‘Hello, World!"
print(my_string.split(,')) # Output: [' Hello, World!
print(my_string.replace(‘World, ‘Python’)) # Output
Hello, Python! *String Formatting:
Python offers several ways to format strings, allowing you to insert variables, expressions, and other val-
ues into a string template. The most common methods for string formatting are:
+ f-strings (formatted string literals): Introduced in Python 3.6, f-strings allow you to embed
expressions inside string literals by prefixing the string with the letter 'f' and enclosing expres-
sions in curly braces {}.
name = ‘Alice’
age = 30
print(f*My name is {name} and I'm {age} years old.")
+ strformat() method: This method allows you to insert values into a string template using
placeholders (e.g,, {}) and the format() method.
name = ‘Bob!
age = 25
print("My name is {} and I'm {} years old.”.format(name, age))
+ % operator: This is the oldest method for string formatting in Python, using the % operator
and placeholders (e.g., %s for strings, %d for integers).
name = ‘Charlie!
age = 35
print(*My name is %s and I'm %d years old." % (name, age))
String Concatenation and Repetition:
You can concatenate (join) strings using the + operator and repeat strings using the * operator.
stri = Hello!
str2 = ‘world’
print(stri +','+ str2 +") # Output: ‘Hello, world!"print(str1 * 3) # Output: 'HelloHelloHello'
Immutability of Strin,
It's important to remember that strings in Python are immutable, meaning once a string is created, its
contents cannot be changed. When you perform operations that appear to modify a string, such as con-
catenation or slicing, you are actually creating a new string object.
Mastering string manipulation is crucial for becoming a proficient Python programmer. By understanding
how to create, access, and manipulate strings using various methods and techniques, you'll be able to
tackle a wide range of text-based programming challenges and build powerful applications that process
and analyze textual data.
Understanding Lists, Tuples, and Sets
Lists, tuples, and sets are essential data structures in programming that allow you to store and manipulate
collections of data efficiently. Each of these data structures has its own unique characteristics and use
cases, making them valuable tools in a programmer's toolkit.
Lists are one of the most versatile data structures available. They are ordered, mutable, and allow duplicate
elements. In Python, lists are created using square brackets |] and can contain elements of different data
types. Here's an example of a list in Python:
fruits = [*apple", "banana", "orange", "grape", “apple"]
One of the key advantages of lists is their ability to be modified. You can add, remove, or change elements
ina list after it has been created. Lists also support various methods and operations, such as appending el-
ements, inserting elements at specific positions, removing elements, and sorting the list.
fruits.append("kiwi") # Adds "kiwi" to the end of the listfruits.insert(1, "mango" # Inserts "mango" at index 1
fruits.remove("grape") # Removes the first occurrence of "grape"
fruits.sort() # Sorts the list in ascending order
Lists are ideal for situations where you need to maintain a collection of items that may need to be modified
or reordered, such as a list of tasks, a collection of student names, or a shopping list
Tuples, on the other hand, are ordered, immutable, and allow duplicate elements. They are created using
parentheses () and can also contain elements of different data types. Once a tuple is created, its elements
cannot be changed, added, or removed. Here's an example of a tuple in Python:
coordinates = (10, 20, 30)
Tuples are useful when you have a collection of related data that shouldn't be modified, such as a set of
coordinates, a person's name and age, or a date (year, month, day). Tuples are also faster than lists in terms
of processing time because they are immutable, which allows for certain optimizations.
Although you cannot modify the elements of a tuple, you can still access individual elements using index-
ing, just like with lists. You can also unpack a tuple into separate variables for easier access to individual
elements.
x, ¥,2= coordinates # Unpacks the tuple into separate variables
print(x) # Output: 10
print(coordinates(1}) # Accessing an element by index; Output: 20
Sets are another useful data structure that store unordered, unique elements. They are created using curly
braces {J or the set() function. Sets are mutable, meaning you can add or remove elements, but since they
are unordered, you cannot access elements by index. Here's an example of a set in Python:
numbers = {1, 2,3, 4, 5, 2, 3} # Creating a set with duplicate elements
print(numbers) # Output: {1, 2, 3, 4, 5} (duplicates are automatically removed)Sets are particularly usefull when you need to store a collection of unique elements or perform set oper-
ations like union, intersection, or difference. For example, you can easily find the common elements be-
tween two sets using the intersection() method or combine two sets using the union() method.
common_elements = set1.intersection(set2) # Finds common elements; Output: {3, 4}
all_elements = set1.union(set2) # Combines both sets; Output: {1, 2, 3, 4, 5, 6}
Sets are also useful for removing duplicates from a list or checking if an element exists in a collection, as
membership testing in sets is much faster than in lists.
When deciding which data structure to use, consider the specific requirements of your program. If you
need an ordered collection that can be modified, lists are a good choice. If you have a collection of related
data that shouldn't change, tuples are the way to go. And if you need to store a collection of unique ele-
ments or perform set operations, sets are the perfect fit.
‘As you become more comfortable with lists, tuples, and sets, you'll find numerous applications for them in
your programs, They are essential tools for organizing and manipulating data efficiently, making your code
more readable, and optimizing performance. By understanding the strengths and limitations of each data
structure, you can make informed decisions about which one to use in different scenarios, ultimately lead-
ing to more effective and efficient programs.
Unleashing the Power of Dictionaries
Dictionaries are one of the most versatile and powerful data structures in programming. They provide a
convenient way to store and retrieve data using key-value pairs, making them an essential tool in any pro-
grammer's toolkit.At its core, a dictionary is a collection of key-value pairs, where each key is unique and associated with
a corresponding value. The keys in a dictionary must be immutable objects, such as strings or numbers,
while the values can be of any data type, including other dictionaries or even functions.
One of the key advantages of dictionaries is their efficient lookup time. When you access a value in a
dictionary using its key, the retrieval process is incredibly fast, regardless of the size of the dictionary. This
is because dictionaries use a hash table implementation under the hood, which allows for constant-time
average case complexity for key lookups.
Creating a dictionary is straightforward, You can use curly braces {} to define a dictionary literal or the
dict() constructor function. Here's an example:
# Using curly braces
my_dict = {'apple': 3, banana’: 5, ‘orange: 2}
# Using the dict() constructor
my_dict = dict(apple=3, banana=5, orange=2)
In both cases, we create a dictionary called my_dict with three key-value pairs, where the keys are strings
representing fruit names, and the values are integers representing quantities.
Accessing values in a dictionary is done using square brackets [] with the corresponding key. For example,
toretrieve the value associated with the key ‘apple’, you would use:
quantity = my_dict['apple']
If the key exists in the dictionary, the associated value will be returned. If the key doesn't exist, a KeyEr-
ror will be raised. To avoid this, you can use the get() method, which allows you to specify a default value to
return if the key is not found:
quantity = my_dict.get(‘apple', 0)In this case, if the key ‘apple’ doesn’t exist, the get() method will return 0 instead of raising an error.
‘Modifying a dictionary is just as easy as accessing values. You can add new key-value pairs or update exist-
ing ones using the square bracket notation:
# Adding a new key-value pair
my_dict['grape'] = 7
# Updating an existing value
my_diet{’banana'] = 10
Dictionaries also provide methods to remove key-value pairs. The del keyword allows you to remove a
specific key-value pair, while the pop() method removes and returns the value associated with a given key.
If the key doesn't exist, you can specify a default value to return instead of raising an error.
# Removing a key-value pair using del
del my_dict{'orange']
# Removing and returning a value using pop()
value = my_dict.pop(’banana', None)
In addition to basic operations, dictionaries offer a range of powerful methods and functionalities. You can
iterate over the keys, values, or key-value pairs of a dictionary using the keys(), values(), and items() meth-
ods, respectively. These methods return view objects that provide a dynamic view of the dictionary's
contents.
# Iterating over keys
for key in my_dict.keys():
print(key)
# Iterating over values
for value in my_dict.values():
print(value)# Iterating over key-value pairs
for key, value in my_dict.items():
print(key, value)
Dictionaries also support various set operations, such as union, intersection, and difference, using the up-
date(), intersection() ,and difference() methods, respectively.
One common use case for dictionaries is to count the occurrences of items in a collection. By using a dictio-
nary, you can efficiently keep track of the count of each unique item, Here's an example:
# Counting occurrences of items ina list
my_list = ‘apple’, banana’, ‘apple’, orange’, ‘banana’, apple!)
item_count = {}
for item in my_list:
item_count[item] = item_count.get(item, 0) + 1
print(item_count)
In this example, we iterate over the items in my_list and use a dictionary item_count to store the count
ofeach item. The get() method is used to retrieve the current count of an item, defaulting to 0 ifthe item
doesn't exist in the dictionary yet. We then increment the count by 1 for each occurrence of the item.
Dictionaries can also be nested, allowing you to create hierarchical data structures. This is particularly
useful when working with complex data sets or when you need to represent relationships between differ-
ent entities.
# Nested dictionaries
78, 'Science': 95, 'English': 91},
‘Michael: {'Math': 92, 'Science': 88, ‘English’: 85}In this example, we have a dictionary student_grades where the keys are student names, and the values
are dictionaries representing their grades in different subjects. This nested structure allows us to easily ac-
cess and manipulate the grades for each student.
Dictionaries are widely used in various programming scenarios, such as caching, memoization, and build-
ing lookup tables. They provide a convenient and efficient way to organize and retrieve data based on
unique keys.
When working with dictionaries, it's important to choose appropriate keys that are unique and immutable.
Strings and numbers are commonly used as keys, but you can also use tuples or frozensets if needed.
It's also worth noting that the order of key-value pairs in a dictionary is not guaranteed to be preserved.
If you need to maintain a specific order, you can use the OrderedDict class from the collections module,
which preserves the insertion order of key-value pairs.
Functional Programming with Python
Functional programming is a powerful paradigm that treats computation as the evaluation of mathemat-
ical functions, avoiding changing state and mutable data. Python, although primarily an object-oriented
language, provides excellent support for functional programming concepts. By embracing functional pro-
gramming techniques, you can write cleaner, more concise, and easier-to-maintain code.
One of the core concepts in functional programming is the use of pure functions. Pure functions always
produce the same output fora given input, without any side effects. They do not modify any external state
or depend on mutable data, making them highly predictable and testable. In Python, you can define pure
functions using the def keyword, just like regular functions.Another fundamental aspect of functional programming is the use of higher-order functions. These are
functions that can take other functions as arguments or return functions as results. Python's built-in
functions like map(, filter(), and reduce()are classic examples of higher-order functions. They allow you to
apply a function to each element of a sequence, filter elements based on a predicate, or reduce a sequence
toa single value.
Lambda functions, also known as anonymous functions, are a concise way to define small, one-line func-
tions without explicitly using the def keyword. They are particularly useful when working with higher-
order functions. Lambda functions are defined using the lambda keyword, followed by the function pa-
rameters and a single expression that is evaluated and returned. For example:
square = lambda x: x **2
result = square(5)
print(result) # Output: 25
Immutability is another key principle in functional programming. Immutable data structures cannot be
modified once created, ensuring that data remains unchanged throughout the program's execution. In
Python, built-in data types like strings and tuples are immutable, while lists and dictionaries are mutable.
To embrace immutability, you can use tuples instead of lists when possible and create new objects instead
of modifying existing ones.
Recursion is a technique commonly used in functional programming, where a function calls itself to solve
a problem by breaking it down into smaller subproblems. Recursive functions are particularly useful for
handling recursive data structures like trees or when dealing with problems that have a naturally recursive
solution. However, it's important to ensure that the recursive function has a base case to avoid infinite
recursion.Python's functional programming capabilities are further enhanced by the functools module, which
provides a collection of higher-order functions and utilities. some notable functions include partial() for
creating new functions with some arguments pre-filled, reduce() for applying a function of two arguments
cumulatively to the items of a sequence, and Iru_cache() for memoizing expensive function calls.
Functional programming in Python is not limited to the built-in functions and modules. There are several
third-party libraries that provide additional functional programming tools and abstractions. One popular
library is PyToolz, which offers a wide range of utility functions for working with iterables, functions, and
dictionaries in a functional style, Another library worth exploring is Fn.py, which brings functional pro-
gramming concepts from languages like Haskell and Scala to Python.
When applying functional programming techniques in Python, it's important to find a balance between
readability and conciseness. While functional programming can lead to more elegant and expressive code,
overusing lambda functions or deeply nested function calls can make the code harder to understand. Aim
for a pragmatic approach, leveraging functional concepts where they improve code quality and maintain-
ability.
To further deepen your understanding of functional programming in Python, consider exploring topics
like function composition, lazy evaluation, and algebraic data types. These concepts can help you write
more modular, reusable, and testable code.
Functional programming is a valuable addition to your Python toolbox, enabling you to write more ex-
pressive, concise, and maintainable code. By mastering functional techniques like pure functions, higher-
order functions, and immutability, you'll be well-equipped to tackle complex problems and build robustapplications. Embrace the power of functional programming in Python, and watch your code become more
elegant and efficient.
Working with Files and File Handling
File handling is a fundamental concept in programming that allows you to read from and write to files
on your computer. In Python, working with files is a straightforward process that involves opening a file,
performing operations on it, and then closing the file. Whether you need to read data froma file, write data
toa file, or manipulate file contents, Python provides a set of built-in functions and methods to accomplish
these tasks efficiently.
To work with files in Python, you first need to open the file using the open() function. This function takes
two parameters: the file path (including the file name) and the mode in which you want to open the file.
The mode specifies the purpose of opening the file, such as reading, writing, or appending. Some common
modes include:
+ 'r': Read mode (default). Opens the file for reading.
+ 'w': Write mode. Opens the file for writing, truncating the file if it already exists.
+ 'a': Append mode. Opens the file for writing, appending to the end of the file if it exists.
+ 'x': Exclusive creation mode. Creates a new file, failing if the file already exists.
: Binary mode. Used for non-text files, such as images or binary data.
Text mode (default). Used for text files.
Here's an example of opening a file in read mode:
file = open(‘example.txt','r')‘Once you have opened a file, you can perform various operations on it depending on your requirements.
If you opened the file in read mode, you can use the read() method to read the contents of the file. This
method returns the entire contents of the file as a string. If you want to read the file line by line, you can use
the readline() method or iterate over the file object using a for loop.
# Reading the entire contents of a file
content = file.read()
print(content)
# Reading a file line by line
line = file-readline()
while line:
print(line)
line = file.readline()
# Iterating over the file object
for line in file:
print(line)
When you open a file in write mode, you can use the write() method to write data to the file. If the file
already exists, its previous contents will be truncated (deleted), and the new data will be written from the
beginning of the file. Ifthe file doesn't exist, a new file will be created.
file = open(‘example.txt’, w’)
file.write(‘Hello, World!\n')
file.write('This is a new line.)
If you want to append data to an existing file without overwriting its contents, you can open the file in
append mode using the 'a' flag, The write() method will then add the new data to the end of the file.
file = open(‘example.txt', a’)
file.write("\nThis is an appended line.’)It's important to close the file after you're done working with it to free up system resources and ensure that
any changes are properly saved. You can close a file using the close() method.
file.close()
To simplify file handling and ensure that files are always properly closed, Python provides the with state-
ment. The with statement automatically takes care of closing the file for you, even if an exception occurs
within the block.
with open(‘example.txt,, 'r’) as file:
content = file.read()
print(content)
In addition to reading and writing text files, Python also supports working with binary files, such as im-
ages or binary data, To read or write binary files, you need to open the file in binary mode using the'b’ flag
along with the appropriate mode.
with open(‘image.jpg', 'tb’) as file:
image_data = file.read()
with open(‘output.jpg', ‘wb') as file:
file.write(image_data)
Python also provides the os module, which offers a range of file and directory-related operations. With
the os module, you can perform tasks such as checking if a file exists, renaming or deleting files, creating
directories, and more.
import os
# Checking if file exists
if os.path.exists(‘example.txt’):
print(‘File exists’)
else:
print(‘File does not exist’)#Renaming a file
os.rename(‘example.txt', ‘new_example.txt')
# Deleting a file
os.remove(‘new_example.txt’)
# Creating a directory
os.mkdir(‘new_directory')
File handling is a crucial skill for any Python programmer, as it enables you to persist data, read from
external sources, and manipulate files on your computer. Whether you're working with text files, CSV files,
JSON files, or binary files, Python provides the necessary tools and functions to handle them efficiently.
When working with files, it's important to consider file permissions and ensure that you have the appropri-
ate access rights to read from or write to a file. Additionally, be cautious when handling user-provided file
paths or names to prevent security vulnerabilities, such as path traversal attacks.
Remember to always close files after you're done working with them, either manually using the
close() method or by leveraging the with statement, This practice helps prevent resource leaks and en-
sures that any changes are properly saved.
File handling is a valuable skill that you'll encounter in various real-world scenarios, such as reading
configuration files, logging data, processing user input, and more. By mastering file handling techniques in
Python, you'll be well-equipped to tackle a wide range of programming tasks and build robust applications
that can interact with the file system effectively.CHAPTER 3
LEVELING UP WITH FUNCTIONS AND MODULES
Defining and Calling Functions
Functions are a fundamental building block of Python programming, allowing you to encapsulate reusable
pieces of code that perform specific tasks. By defining functions, you can break down complex problems
into smaller, more manageable subproblems, making your code more modular, readable, and maintainable.
Functions also promote code reuse, as you can call the same function multiple times from different parts of
your program, saving you time and effort.
Defining Functions:
To define a function in Python, use the def keyword followed by the function name, a pair of parenthe-
ses (), anda colon :. The function body, which contains the code to be executed when the function is
called, is indented below the function definition.
def greet():
print("Hello, world!)
Function Parameters:
Functions can accept input values, called parameters or arguments, which allow you to pass data into the
function for processing. Parameters are specified within the parentheses of the function definition, sepa-
rated by commas.
def greet(name):
print(f"Hello, {name}}!")In this example, name is a parameter that accepts a value when the function is called. The function then
uses the value of name to personalize the greeting,
Default Parameter Values:
You can assign default values to function parameters, which will be used if no argument is provided when
the function is called. Default values are specified by assigning a value to the parameter in the function
definition.
def greet(name="world"):
print(f"Hello, {name}!")
Now if you call the function without an argument, it will use the default value *world’. If you provide an
argument, it will override the default value.
Returning Values:
Functions can also return values using the return statement. The returned value can be assigned to a vari-
able or used directly in an expression.
def add(a, b):
returna+b
result = add(3, 5)
print(result) # Output: 8
The add() function takes two parameters, a and b, and returns their sum. The returned value is then
assigned to the result variable and printed.
“args and ““kwargs:
Python allows you to define functions that accept a variable number of arguments using
the ‘args and “*kwargs syntax. ‘args is used to pass a variable number of non-keyword arguments to afunction, while “kwargs is used to passa variable number of keyword arguments.
def print_args(‘args):
for arg in args:
print(arg)
print_args(1, 2,3)
print_args(‘a, b,c’)
def print_kwargs(*kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_kwargs(nai
"Alice", age=30)
Calling Functions:
To call a function, simply use the function name followed by a pair of parentheses (). If the function accepts
parameters, provide the necessary arguments within the parentheses.
greet() # Output: Hello, world!
greet("Alice") # Output: Hello, Alice!
Function Scope and Global Variables:
Variables defined within a function have a local scope, meaning they are only accessible within the func-
tion body. Variables defined outside of any function have a global scope and can be accessed from anywhere
in the program, including inside functions.
To modify a global variable from within a function, you need to use the global keyword to indicate that
youare referring to the global variable, not creating a new local variable with the same name.
count = 0def increment():
global count
count +=1
increment()
print(count) # Output: 1
Recursive Functions:
python supports recursive functions, which are functions that call themselves. Recursive functions are
useful for solving problems that can be broken down into smaller subproblems of the same type. However,
it's important to define a base case that stops the recursion to avoid infinite loops.
def factorial(n):
oma
else:
return n* factorial(n- 1)
print(factorial(5)) # Output: 120
The factorial() function calculates the factorial ofa number n by recursively callingitself with n-1 until
the base case of n =
0 is reached.
Lambda Functions:
Python also supports lambda functions, which are small, anonymous functions that can be defined inline.
Lambda functions are defined using the lambda keyword followed by the function parameters, a colon :,
and the function expression.
add = lambda a, b:a+b
print(add(3, 5)) # Output: 8Lambda functions are often used in combination with higher-order functions, such as map(), filter(),
and reduce(), to perform operations on collections of data.
Mastering the use of functions is essential for writing clean, modular, and reusable Python code, By under-
standing how to define and call functions, work with function parameters and return values, and leverage
advanced concepts like ‘args , **kwargs , recursion, and lambda functions, you'll be able to tackle complex
problems and build powerful applications with ease.
Function Arguments and Return Values
Functions are a fundamental concept in programming that allow you to break down complex tasks into
smaller, more manageable pieces of code. Two essential aspects of functions are their arguments and re-
turn values, which enable you to pass data into a function and receive a result back from it.
Function arguments, also known as parameters, are values that are passed into a function when it is called.
These arguments allow you to provide input data to the function, which it can then use to perform its task.
Arguments are defined in the function declaration within parentheses, and they can have different data
types, such as integers, floating-point numbers, strings, or even more complex data structures like lists or
objects.
Here's an example of a function in Python that takes two integer arguments and returns their sum:
def add_numbers(a, b):
returna+b
In this example, the function add_numbers takes two arguments, a and b, which are used within the func-
tion body to calculate their sum. The return statement is used to send the result back to the caller.When calling a function, you provide the arguments in the same order as they are defined in the function
declaration. For instance, to call the add_numbers function with the arguments 5 and 3, you would write:
result = add_numbers(5, 3)
print(result) # Output: 8
Python also supports keyword arguments, which allow you to pass arguments to a function using their
parameter names. This can make your code more readable and less prone to errors, especially when dealing
with functions that have many arguments. Here's an example:
def greet(name, age):
print(f"Hello, {name}! You are {age} years old.")
greet(age=25, name="John") # Using keyword arguments
In this case, the order of the arguments doesn't matter because they are matched based on their names.
Functions can also have default argument values, which are used when the caller doesn't provide a value
for a specific argument. Default values are defined in the function declaration by assigning a value to the
parameter. Here's an example:
def greet(name, age=18):
print(f"Hello, {name}! You are {age} years old."
greet("John") # Output: Hello, John! You are 18 years old.
greet(“Alice", 25) # Output: Hello, Alice! You are 25 years old.
In this example, if the age argument is not provided, it defaults to 18.
Return values are the results that a function sends back to the caller after it has completed its task. A
function can return any data type, including numbers, strings, lists, or even more complex objects. The re-
turn statement is used to specify the value that the function should return.Here's an example of a function that takes a list of numbers and returns the sum of all the even numbers in
the list:
def sum_even_numbers(numbers):
total =0
for num in numbers:
ifnum % 2 == 0:
total += num
return total
my_list = [1, 2, 3, 4,5, 6, 7, 8,9, 10]
result = sum_even_numbers(my_list)
print(result) # Output: 30
In this example, the sum_even_numbers function iterates through the numbers list, checks each number
to see if it's even, and adds it to the total if it is. Finally, the function returns the total sum of all even
numbers.
It’s important to note that a function can have multiple return statements, but only one of them will be
executed, depending on the conditions within the function. Once a return statement is encountered, the
function immediately stops executing and returns the specified value.
def absolute_value(num):
ifnum >= 0:
return num
else:
return -num
Inthis example, the absolute_value function checks if the input number is greater than or equal to zero. Ifit
is, the function returns the number asis. If the number is negative, the function returns the negated value
of the number, effectively making it positive.Understanding function arguments and return values is crucial for writing modular, reusable, and
maintainable code. By breaking down complex tasks into smaller functions with well-defined inputs and
outputs, you can create code that is easier to understand, test, and debug. As you become more comfortable
with these concepts, you'll be able to write more advanced functions that tackle increasingly complex prob-
lems, making your programs more efficient and effective.
Scope and Namespaces in Python
In Python, the concept of scope and namespaces plays a crucial role in organizing and managing variables,
functions, and other objects within a program. Understanding how scope and namespaces work is essen-
tial for writing clean, maintainable, and bug-free code.
Anamespace is a container that holds a collection of names (identifiers) and their corresponding objects. It
provides a way to avoid naming conflicts and allows for the reuse of names in different contexts. In Python,
namespaces are implemented as dictionaries, where the keys are the names and the values are the objects
associated with those names.
Python has several types of namespaces:
Built-in Namespace:
+ Contains built-in functions, exceptions, and other objects that are available by default in
Python.
+ Examples include print(), len() int(), dict(), and Exception.
Global Namespace:
+ Contains names defined at the top level of a module or in the interactive interpreter.+ These names are accessible from anywhere within the module or the interactive session.
Local Namespace:
+ Contains names defined within a function or a class method.
+ Each function or method invocation creates a new local namespace.
+ Local names are only accessible within the function or method where they are defined.
Scope, on the other hand, refers to the region of a program where a particular names visible and accessible.
Python follows a set of rules called the LEGB rule to determine the scope of a nam
Local Scope:
+ Names defined within a function or a class method have local scope.
+ They are only accessible within the function or method where they are defined.
+ Local names are created when the function is called and destroyed when the function returns.
Enclosing Scope (Nonlocal Scope):
+ Applies to nested functions (functions defined inside another function).
+ Names defined in the enclosing function are accessible to the nested function.
+ Enclosing scope is searched if a name is not found in the local scope.
Global Scope:
+ Names defined at the top level of a module or in the interactive interpreter have global scope.
+ They are accessible from anywhere within the module or the interactive session.+ Global names are created when the module is imported or the interactive session starts.
Built-in Scope:
+ Names in the built-in namespace, such as built-in functions and exceptions, have the widest
scope.
+ They are accessible from anywhere in the program without the need for an import statement.
When a name is referenced in Python, the interpreter searches for it in the following order: local scope,
enclosing scope, global scope, and finally, the built-in scope. If the name is not found in any of these scopes,
aNameError is raised.
Here's an example that demonstrates the concept of scope:
X= 10 # Global variable
def outer_function():
y = 20 # Local variable in outer_function
def inner_function():
z= 30 # Local variable in inner_function
print(x) # Accesses global variable
print(y) # Accesses variable from enclosing scope
print(z) # Accesses local variable
inner_function()
print(y) # Accesses local variable
# print(z) # Raises NameError: name’z'is not defined
outer_function()
print(x) # Accesses global variable
#print(y) # Raises NameError: name'y' is not definedIn this example, we have a global variable x, a local variable y in outer_function() , and a local variable
z in inner_function() . The inner_function() can access the global variable x , the variable y from its en-
closing scope ( outer_function() ), and its own local variable z . However, outer_function() cannot access
the local variable z defined in inner_function(), and the global scope cannot access the local variables y
and z.
It's important to note that Python allows for the modification of global names from within a function
using the global keyword. Similarly, the nonlocal keyword can be used to modify names in the enclosing
scope from within a nested function. However, it's generally recommended to minimize the use of global
variables and instead pass necessary data as function arguments or return values to maintain code clarity
and avoid potential bugs.
Namespaces and scope also come into play when dealing with modules and packages in Python. Each
module has its own global namespace, and names defined in one module are not directly accessible from
another module unless explicitly imported. This allows for the creation of self-contained and reusable code
modules without naming conflicts.
When importing modules or packages, you can use various import statements to control the visibility and
accessibility of names:
import module_name:
+ Imports the module and creates a reference to it in the current namespace.
+ Access names using the module name prefix, e.g., module_name.function_name() .from module_name import name1, name2 :
+ Imports specific names from the module directly into the current namespace.
+ Access names directly without the module name prefix, e.g., function name().
from module_name import * :
+ Imports all names from the module into the current namespace.
+ Access names directly without the module name prefix.
+ Use with caution as it can lead to naming conflicts and reduced code readability.
It's generally recommended to use explicit imports ( import module_name or from module_name import
name, name? )to maintain code clarity and avoid potential naming conflicts.
In addition to modules, Python also supports the concept of packages, which are directories containing
multiple modules. Packages provide a way to organize related modules hierarchically and create reusable
code libraries. Each package has its own namespace, and modules within a package can be accessed using
the dot notation, eg., package_name.module_name.
Understanding scope and namespaces is crucial for writing modular, maintainable, and error-free Python
code. By properly managing names and their visibility, you can avoid naming conflicts, create reusable code
components, and ensure that your program behaves as expected.
Remember the LEGB rule (Local, Enclosing, Global, Built-in) when determining the scope of a name, and
use appropriate import statements to control the visibility and accessibility of names from external mod-
ules and packages.By mastering the concepts of scope and namespaces, you'll be well-equipped to write clean, organized, and
efficient Python code, and you'll be able to effectively collaborate with other developers on larger projects.
Creating and Importing Modules
Modules are a fundamental concept in Python programming, allowing you to organize your code into
reusable and maintainable units. A module is essentially a Python file containing definitions, statements,
and functions that can be imported and used in other Python scripts. By creating and importing modules,
you can break down complex projects into smaller, more manageable pieces, making your code more mod-
ular and easier to understand.
To create a module, simply create a new Python file with a .py extension and define the desired functions,
classes, or variables within it. For example, let's create a module called math_utils.py that contains a func-
tion to calculate the factorial of a number:
#math_utils.py
def factorial(n):
if :
else:
return n* factorial(n - 1)
Once you have defined your module, you can import it into another Python script using the import state-
ment. There are several ways to import modules in Python:
Importing the entire module:
import math_utils
result = math_utils.factorial(5)print(result) # Output: 120
In this case, you need to prefix the function name with the module name when calling it.
Importing specific functions or variables from a module:
import math_utils as mu
result = mu-factorial(5)
print(result) # Output: 120
Aliasing a module can be useful when dealing with long module names or to avoid naming conflicts.
When importing modules, Python looks for them in a specific order of locations, known as the module
search path, The search path includes the current directory, directories listed in the PYTHONPATH environ-
ment variable, and standard library directories. You can modify the search path by appending to the sys.
path list or by using the PYTHONPATH environment variable.
To organize modules further, you can create packages, which are directories containing multiple modules
and an
py file, The init py file can be empty or contain initialization code for the package. To import
modules from a package, you need to use the dot notation:
from package_name.module_name import function_name
When creating modules, it's important to follow Python's naming conventions and best practices. Module
names should be lowercase, with underscores used to separate words if necessary. Avoid using reserved
keywords or names that clash with built-in modules or functions.
Modules can also contain executable statements, which are run only once when the module is first im-
ported. These statements are typically used for module-level initialization or to provide a command-line
interface. To prevent the executable statements from running when the module is imported, you can use
the following idiom:if __name_=="_main_":
# Executable statements go here
This ensures that the executable statements are only run when the module is directly executed, not when
itis imported by another script.
In addition to creating your own modules, Python provides a vast collection of built-in modules and third-
party libraries that you can import and use in your projects. The Python Standard Library offers modules
for a wide range of tasks, such as file I/O, networking, data processing, and more. You can explore the offi-
cial Python documentation to learn more about the available modules and their functionalities.
When working with third-party libraries, you can use pip, the package installer for Python, to easily install
and manage dependencies. Simply run pip install package_name to install a package and its dependencies.
It's good practice to use virtual environments to isolate project dependencies and avoid conflicts between
different versions of packages.
Creating and importing modules is a powerful way to organize and reuse code in Python. By breaking down
your projects into modular components, you can improve code readability, maintainability, and collabora-
tion with other developers. Embrace the concept of modules, and watch your Python projects become more
structured and efficient.
Exploring Python's Standard Library
Python's standard library is a collection of modules and packages that come bundled with every Python
installation. It provides a wide range of functionalities and tools that can help you tackle various program-
ming tasks efficiently. From file handling to network communication, from data manipulation to web de-velopment, the standard library has you covered. By leveraging the power of the standard library, you can
save time and effort in writing code from scratch and focus on solving the problem at hand.
One of the most commonly used modules in the standard library is os. The os module provides a way to
interact with the operating system, allowing you to perform file and directory operations, access environ-
ment variables, and more. With the os module, you can easily navigate the file system, create or delete files
and directories, and retrieve information about files, such as their size or modification time.
import os
# Get the current working directory
current_dir = os.getcwd()
print(current_dir)
# List files and directories in a directory
os.listdir(‘path/to/directory’)
riesettloe
# Create a new directory
os.mkdir(‘new_directory')
#Renamea file
os.rename(‘old_file.txt’, ‘new_file.txt')
Another essential module in the standard library is datetime. The datetime module provides classes for
working with dates, times, and time intervals. It allows you to create, manipulate, and format date and
time objects, making it easy to handle time-related tasks in your programs. Whether you need to calculate
the difference between two dates, format a date in a specific way, or work with time zones, the datetime
module has you covered.
from datetime import datetime, timedelta# Get the current date and time
now = datetime.now()
print(now)
# Create a specific date
date = datetime(2023, 6, 15)
print(date)
# Calculate the difference between two dates
delta = datetime(2023, 6, 15) - datetime(2023, 1, 1)
print(delta)
# Format a date
formatted_date = now.strftime("%Y-%m-%d %H:%M:%S")
print(formatted_date)
The standard library also includes modules for working with data in various formats. For example, the j-
son module allows you to encode and decode JSON data, making it easy to work with JSON-based APIs
or store structured data, The csv module provides functionality for reading from and writing to CSV files,
which is commonly used for data exchange and analysis
import json
import csv
# Encoding a Python object to JSON
json_data = json.dumps(data)
print(json_data)# Decoding JSON to a Python object
decoded_data = json.loads(json_data)
print(decoded_data)
# Reading data from a CSV file
with open(‘data.csv’, 'r) as file:
csv_reader = csv.reader(file)
for row in csv_reader:
print(row)
# Writing data to a CSV file
data =[
[’Name’, ‘Age’, ‘City'],
[John Doe’, 30", ‘New York'],
[Jane Smith’, '25', 'London']
with open(‘output.csv', ‘w', newline=") as file:
csv_writer = csv.writer(file)
csv_writer.writerows(data)
Python's standard library also provides modules for network communication and web development. The
socket module allows you to create network sockets and communicate over TCP/IP networks, enabling you
to build client-server applications or network-based tools. The http server module provides a simple HTTP
server that you can use for local development or testing purposes.
import socket
# Creating a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((‘example.com', 80))
sock.send(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
response = sock.recv(4096)
print(response.decode())sock.close()
The standard library also includes modules for working with regular expressions ( re), handling com-
mand-line arguments ( argparse ), generating random numbers ( random ), performing mathematical op-
erations (math ), and much more. These modules provide a wide range of functionality that can simplify
your code and help you solve complex problems efficiently.
It's important to note that the standard library is not limited to the examples mentioned above. It encom-
passes a vast collection of modules and packages that cater to various domains and use cases. From cryp-
tography (hashlib ) to data compression ( zlib), from email handling ( email ) to XML processing ( xml ),
the standard library has something to offer for almost every programming task you may encounter.
To explore the full potential of Python's standard library, it's recommended to refer to the official Python
documentation. The documentation provides detailed information about each module, including its pur-
pose, available functions, and usage examples. By familiarizing yourself with the modules relevant to your
specific needs, you can leverage the power of the standard library and write more efficient and concise
code.
When using modules from the standard library, it's important to keep in mind the version of Python you
are working with. Some modules may have different versions or implementations across different Python
versions. Always refer to the documentation specific to your Python version to ensure compatibility and
avoid any unexpected behavior.
Python's standard library is a treasure trove of pre-built functionality that can significantly enhance your
productivity as a programmer. By leveraging the modules and packages available in the standard library,
you can focus on solving the core problems of your application while relying on well-tested and optimizedcode for common tasks. Exploring and mastering the standard library is an essential skill for any Python
developer, as it empowers you to write more efficient, robust, and maintainable code.
Managing Dependencies with Pip and Virtual Environments
Managing dependencies is a crucial aspect of Python development, especially when working on projects
that require external libraries or packages. As your projects grow in complexity, it becomes increasingly
important to ensure that your code runs consistently across different environments and that your depen-
dencies are properly managed to avoid conflicts and compatibility issues. This is where pip, the standard
package manager for Python, and virtual environments come into play.
Pip: The Python Package Installer
Pip isa command-line tool that allows you to easily install, upgrade, and remove Python packages and their
dependencies. It works by downloading packages from the Python Package Index (PyPI), a central reposi-
tory of publicly available Python packages.
To install a package using pip, simply run the following command in your terminal or command prompt:
pip install package_name
For example, to install the popular web framework Django, you would run:
pip install Django
You can also specify a specific version of a package by appending the version number to the package name:
pip install package_name==version
To upgrade an already installed package to its latest version, use the -upgrade or -U flag:
pip install --upgrade package_name
To remove a package, use the uninstall command:pip uninstall package_name
Pip alsoallows youtocreatea requirements.txt file, which listsall the packages and their versions required
by your project. This file can be used to easily recreate the same environment on another machine or share
your project's dependencies with others.
To create a requirementstxt file, run the following command in your project's directory:
pip freeze > requirements.txt
This will generate a file containing a list of all installed packages and their versions. To install the packages
listed in a requirements txt file, run:
pip install -r requirements.txt
Virtual Environments: Isolating Project Dependencies
While pip is a powerful tool for managing packages, it installs them globally by default, which can lead to
conflicts between different projects that require different versions of the same package. This is where vir-
tual environments come in handy.
A virtual environment is an isolated Python environment that allows you to install packages and depen-
dencies specific to a project without affecting your system-wide Python installation or other projects.
This ensures that each project has its own self-contained environment with its own set of packages and
versions.
To create a virtual environment, you can use the venv module, which is included in Python 3.3 and above.
Navigate to your project's directory in the terminal or command prompt and run the following command:
python -m venv myenv
This will create a new directory called myeny (youcan choose any name you like) that contains the virtual
environment files.To activate the virtual environment, run the following command:
+ On Windows:
myenv\Scripts\activate
+ OnmacOS and Linux:
source myenv/bin/activate
Once the virtual environment is activated, your terminal prompt will be prefixed with the name of the
virtual environment, indicating that you're now working within the isolated environment.
Now, any packages you install using pip will be installed within the virtual environment and will not affect
your global Python installation. To deactivate the virtual environment, simply run the deactivate com-
mand.
It's a good practice to create a new virtual environment for each project you work on to keep their depen-
dencies separate and avoid conflicts. You can also combine the use of virtual environments with require-
ments.txt files to easily recreate a project's environment on another machine.
Best Practices for Managing Dependencies
Here are some best practices to follow when managing dependencies in your Python projects:
+ Use virtual environments: Always create a new virtual environment for each project to keep
their dependencies isolated and avoid conflicts.
- Pin your dependencies: Specify the exact versions of the packages your project depends on in
the requirements.txt file. This ensures that your project consistently uses the same versions of
the packages, even if newer versions are released.+ Keep your dependencies up to date: Regularly check for updates to the packages your project
depends on and upgrade them when necessary. This helps ensure that you have the latest bug
fixes, security patches, and feature improvements.
+ Use a dependency management tool: For larger projects with more complex dependency re-
quirements, consider using a dependency management tool like Poetry or Pipenv. These tools
provide additional features like dependency resolution, lockfiles, and deterministic builds.
+ Document your dependencies: Include a list of your project's dependencies and their versions
in your project's documentation or README file. This helps other developers (including your
future self) understand what packages are required to run your project.
By following these best practices and leveraging the power of pip and virtual environments, you can
effectively manage your Python project's dependencies, ensure reproducibility, and avoid common pitfalls
associated with dependency conflicts and compatibility issues.CHAPTER 4
OBJECT-ORIENTED PROGRAMMING WITH PYTHON
Introduction to Object-Oriented Programming (OOP)
Object-Oriented Programming (OP) is a programming paradigm that organizes code into objects, which
are instances of classes that encapsulate data and behavior. OOP has become one of the most widely used
programming paradigms due to its ability to create modular, reusable, and maintainable code. By under-
standing and applying the principles of OOP, you can write more efficient and effective programs that are
easier to understand, modify, and extend.
At the core of OOP are classes and objects. A class is a blueprint or template that defines the structure and
behavior of objects. It specifies the data (attributes) and functions (methods) that the objects of the class
will have. An object, on the other hand, is an instance of a class. It represents a specific entity with its own
unique set of attribute values.
Here's a simple example of a class definition in Python:
class Dog
def _init_(self, name, age):
selfname = name
self.age = age
def bark(self):
print("Woofl")
def get_info(self):
print(f"My name is {self.name} and I am {self.age} years old.")In this example, we define a Dog class with two attributes (name and age) and two methods (bark and
get info). The _init__ method is a special method called a constructor, which is called when a new object
of the class is created. It initializes the object's attributes with the values passed as arguments.
To create an object of the Dog class, you simply call the class as if it were a function, passing in the required
arguments:
my_dog = Dog("Buddy", 3)
Now, my_dogis an object (or instance) of the Dog class with the name "Buddy" and age 3. You can access the
object's attributes and call its methods using dot notation:
print(my_dog.name) # Output: Buddy
my_dog.bark() # Output: Woof!
my_dog.get_info() # Output: My name is Buddy and I am 3 years old.
One of the key principles of OOP is encapsulation, which involves bundling data and methods that operate
on that data within a class. Encapsulation helps to protect the data from unauthorized access and mod-
ification, ensuring that the object's internal state remains consistent and valid. In many programming
languages, you can control access to an object's attributes and methods using access modifiers like public,
private, and protected.
Another important principle of OOP is inheritance, which allows you to create new classes based on
existing ones. The new class, called a subclass or derived class, inherits the attributes and methods of the
existing class, called the superclass or base class. Inheritance promotes code reuse and allows you to create
specialized versions of a class that add or override functionality as needed.
Here's an example of inheritance in Python:
class Animal:
def _init_(self, name):selfname = name
def speak(self):
pass
class Cat(Animal):
def speak(self):
print("Meow!")
class Dog(Animal):
def speak(self):
print("Woof!")
In this example, we define an Animal superclass with a constructor that initializes the name attribute and
an empty speak method. We then define two subclasses, Cat and Dog, which inherit from the Animal class.
Each subclass overrides the speak method with its own implementation.
my_cat = Cat("Whiskers")
my_dog = Dog("Buddy")
my_cat.speak() # Output: Meow!
my_dog.speak() # Output: Woof!
When we create objects of the Cat and Dog classes, they inherit the name attribute from the Animal super-
class, but they use their own implementations of the speak method.
Polymorphism is another key concept in OOP, which allows objects of different classes to be treated as
objects of a common superclass. This enables you to write more flexible and reusable code by designing
algorithms that work with objects of the superclass, without needing to know the specific subclass of each
object.Inthe previous example, we can create a function that accepts an Animal object and calls its speak method,
without needing to know whether the object is a Cat or a Dog:
def make_animal_speak(animal):
animal.speak()
make_animal_speak(my_cat) # Output: Meow!
make_animal_speak(my_dog) # Output: Woof!
This function works with any object that inherits from the Animal class and has a speak method, demon-
strating the power of polymorphism.
‘As you delve deeper into OOP, you'll encounter more advanced concepts like abstract classes, interfaces,
and composition, which build upon the fundamental principles of encapsulation, inheritance, and poly-
morphism. These concepts allow you to create even more robust, flexible, and maintainable code struc-
tures.
By mastering OOP, you'll be able to tackle complex programming challenges more effectively, write code
that is easier to understand and modify, and create reusable components that can be shared across multiple
projects. As you continue to learn and practice OOP, you'll find that it becomes an indispensable tool in your
programming toolkit, enabling you to write more efficient, scalable, and maintainable software.
Creating Classes and Instances
Classes and instances are fundamental concepts in object-oriented programming (OOP) that allow you to
create reusable and modular code. They provide a way to organize related data and behavior into a single
entity, making your code more structured and easier to maintain.A class is a blueprint or a template that defines the properties (attributes) and behaviors (methods) that
objects of that class will have. It serves as a blueprint for creating individual instances of the class. Think
of aclass as a cookie cutter that shapes the dough (data) into cookies (objects) with specific characteristics
To define a class in Python, you use the class keyword followed by the class name, By convention, class
names are written in CamelCase. Here's a simple example of a class definition:
class Dog:
def _init_(self, name, age):
selfname = name
self.age = age
def bark(self):
print("Woof!")
In this example, we define a lass called Dog. The _init_ method is a special method called the construc-
tor, which is automatically called when a new instance of the class is created. It initializes the attributes
of the instance. In this case, the Dog class has two attributes: name and age, which are passed as argu-
ments to the constructor.
The self parameter in the method definitions refers to the instance itself. It allows you to access and mod-
ify the instance's attributes and call its methods.
To create an instance of a class, you simply call the class as if it were a function, passing any required argu-
ments to the constructor. Here's an example of creating instances of the Dog cla:
dogi = Dog(“Buddy", 3)
dog? = Dog("Max", 5)
In this code, we create two instances of the Dog class: dog and dog? . Each instance has its own set of,
attributes (name and age ) with the values provided during instantiation.You can access the attributes of an instance using the dot notation. For example:
print(dog1.name) # Output: Buddy
print(dog2.age) # Output: 5
Instances can also call methods defined in the class. Methods are functions that are associated with a class
and operate on the instance's data. In the Dog class, we defined a method called bark(), which simply prints
"Woof!" To call a method on an instance, you use the dot notation followed by the method name and paren-
theses. For example:
dogi.bark() # Output: Woof!
dog2.bark() # Output: Woof!
Classes can also have class-level attributes and methods, which are shared among all instances of the class.
Class-level attributes are defined outside of any method and are accessed using the class name itself, Here's
an example:
class Dog
species = "Canis lupus familiaris"
def _init_(self, name, age):
self.name = name
self.age = age
def bark(self):
print("Woof!")
@classmethod
def info(cls):
print(f"Dogs belong to the species {cls.species}")
In this updated Dog class, we added a class-level attribute called species, which is shared by all instances of,
the class. We also added a class method called info(), which is decorated with the @classmethod decorator.Class methods take the class itself (cls) as the first parameter instead of the instance (self). They can be
called on the class itself or on instances of the class.
print(Dog.species) # Output: Canis lupus familiaris
Dog.info() # Output: Dogs belong to the species Canis lupus familiaris
dogi = Dog("Buddy", 3)
dog1.info() # Output: Dogs belong to the species Canis lupus familiaris
Classes can also inherit from other classes, allowing for code reuse and the creation of specialized classes
based on more general ones. This is known as inheritance. The class being inherited from is called the base
class or superclass, and the class that inherits from it is called the derived class or subclass.
Here's an example of inheritance:
class Animal:
def _init_(self, name):
selfname = name
def speak(self):
pass
return "Meow!"
class Dog(Animal):
In this example, we define a base class called Animal with a constructor that initializes the name attribute
and a speak() method that does nothing (using the pass keyword). We then define two derived classes,Cat and Dog, which inherit from the Animal class. Each derived class overrides the speak() method with its
own implementation.
animal1 = Animal("Generic Animal")
cat] = Cat("Whiskers")
dogi = Dog("Buddy")
print(animal1.name) # Output: Generic Animal
print(cati.name) # Output: Whiskers
print(dogi.name) # Output: Buddy
print(cat1.speak()) # Output: Meow!
print(dogi.speak()) # Output: Woof!
Inheritance allows derived classes to inherit attributes and methods from their base class while also adding
or modifying their own specific attributes and behaviors.
Classes can also have special methods, such as _str__ and _repr__, which define how instances of the
class are represented as strings. These methods are used for debugging, logging, and providing meaningful
string representations of objects.
class Dog:
def _init_(self, name, age):
selfname = name
self.age = age
def _str_(self):
return f"{self:name} is {self.age} years old”
dogi = Dog(“Buddy”, 3)
print(dog1) # Output: Buddy is 3 years oldIn this example, the __str__ method is defined to return a string representation of the Dog instance, in-
cluding its name and age.
Creating classes and instances allows you to organize your code into logical and reusable units. By encapsu-
lating related data and behavior into classes, you can create more modular and maintainable code, Classes
provide a way to define custom data types that can have their own attributes and methods, making your
code more expressive and easier to understand.
When designing classes, it's important to consider the principles of object-oriented programming, such
as encapsulation, inheritance, and polymorphism. Encapsulation means hiding the internal details of a
class and providing a public interface for interacting with its instances. Inheritance allows you to create
specialized classes based on more general ones, promoting code reuse and modularity. Polymorphism en-
ables objects of different classes to be treated as objects of a common base class, providing flexibility and
extensibility.
By mastering the concepts of classes and instances, you'll be able to create powerful and flexible Python
programs that are easier to understand, maintain, and extend. Object-oriented programming is a funda-
mental paradigm in modern software development, and understanding how to create and use classes is es-
sential for building robust and scalable applications.
Class Attributes and Methods
In object-oriented programming (OOP) with Python, classes serve as blueprints for creating objects that
encapsulate data and behavior. Class attributes and methods are fundamental concepts that allow you to
define and manipulate the state and functionality of objects created from a class.Class attributes are variables that belong to the class itself, rather than individual instances of the class.
They are shared among all instances of the class and can be accessed using the class name or any instance
of the class, Class attributes are defined outside of any methods and are typically used to store data that is
common to all instances.
Here's an example of a class with a class attribute:
In this example, wheels is a class attribute that is shared by all instances of the Car class. You can access it
using Car.wheels or any instance of the class, such as my_car.wheels.
Class attributes can be useful for defining constants, default values, or shared resources among instances.
However, it's important to note that modifying a class attribute affects all instances of the class, so use
them judiciously.
On the other hand, instance attributes are specific to each instance of a class and are defined within the
class's init method. They store data that varies from one instance to another. In the Car class example,
make and model are instance attributes that are unique to each car object.
Class methods are functions that are defined within a class and operate on the class itself, rather than
individual instances. They are decorated with the @classmethod decorator and take the class (cls) as the
first parameter instead of self. Class methods can access and modify class attributes and are often used for
creating alternative constructors or performing operations that involve the class asa whole.Here's an example of a class method:
def _init_(self, make, model):
self.make = make
self.model = model
@classmethod
def from_string(cls, car_string
make, model = car_string.split(
return cls(make, model)
In this example, the from_string class method takes a string in the format "make-model" and returns a new
instance of the Car class. It splits the string into make and model components and uses the cls parameter to
create and return a new Car object.
Class methods provide a way to define alternative constructors or factory methods that create instances of,
the class based on different input formats or criteria. They can also be used for utility functions that oper-
ate on the class itself.
Static methods are another type of method that can be defined within a class. They are similar to class
methods but do not receive the class or instance as a parameter. Static methods are decorated with the
@staticmethod decorator and are essentially standalone functions that belong to the class namespace.
They can be called on the class itself or any instance of the class.
Here's an example of a static method:
class Car:
wheels = 4