0% found this document useful (0 votes)
12 views

Python Foundations Comprehensive Study Notes

The document provides a comprehensive overview of Python, detailing its history, design philosophy, and key features such as simplicity, readability, and dynamic typing. It covers the setup of a Python development environment, variable types, data structures, and operators, emphasizing Python's versatility in various fields like web development and data science. Additionally, it explains variable scope, naming conventions, and the distinction between mutable and immutable types.

Uploaded by

veerasainath526
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views

Python Foundations Comprehensive Study Notes

The document provides a comprehensive overview of Python, detailing its history, design philosophy, and key features such as simplicity, readability, and dynamic typing. It covers the setup of a Python development environment, variable types, data structures, and operators, emphasizing Python's versatility in various fields like web development and data science. Additionally, it explains variable scope, naming conventions, and the distinction between mutable and immutable types.

Uploaded by

veerasainath526
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 40

Python Foundations: Comprehensive

Technical Notes
1. Python Introduction & Basics
1.1. Python Introduction
Python, a high-level, interpreted programming language, was conceived by Guido van Rossum
in the late 1980s. Its design philosophy emphasizes code readability and simplicity, allowing
developers to express complex concepts in fewer lines of code compared to languages like C++
or Java. The name "Python" was inspired by the British comedy group Monty Python's Flying
Circus, reflecting a touch of humor in its origins.
The evolution of Python can be marked by several key releases:
●​ Python 1.0 (1994): This version introduced core features like dynamic typing and built-in
data structures, setting the stage for Python's versatility and ease of use.
●​ Python 2.x Era: This period saw significant growth in Python's popularity and an
expansion of its capabilities. Features such as list comprehensions and improved Unicode
support were introduced, broadening its applicability in areas like web development and
scientific computing.
●​ Python 3.x Era: Representing a major revision, Python 3 aimed to modernize the
language by introducing enhancements, improving performance, and ensuring more
consistent syntax. A key aspect of this release was its break in backward compatibility
with Python 2, a deliberate decision to rectify fundamental design flaws and allow the
language to evolve unencumbered by legacy constraints. This transition, though initially
challenging for the community, was pivotal for Python's long-term viability and relevance
in a rapidly changing technological landscape.
Python's design incorporates several key features that contribute to its widespread adoption:
●​ Simplicity and Readability: Python's syntax is designed to be clean and closely
resemble the English language, making it relatively easy to learn and understand. This
allows developers to focus on problem-solving rather than grappling with complex
language rules.
●​ Interpreted Language: Python code is executed line by line by an interpreter, rather than
being compiled into machine code all at once. This facilitates easier debugging and offers
greater flexibility during development. The analogy of a live interpreter translating speech
in real-time effectively illustrates this process.
●​ Platform Independent: The interpreted nature of Python contributes to its platform
independence. Python programs can typically run on various operating systems (e.g.,
Windows, macOS, Linux) without requiring code modifications.
●​ Dynamically Typed: As will be discussed further, variable types are checked during
runtime, meaning explicit type declarations are not required.
●​ Extensive Standard Library and Third-Party Packages: Python boasts a vast
collection of modules and packages that provide pre-written code for a wide array of
tasks, significantly reducing development time. This is often likened to having a
well-equipped workshop with tools for almost any job.
●​ Object-Oriented, Imperative, and Functional Programming Paradigms: Python
supports multiple programming paradigms, allowing developers to choose the style that
best suits their problem. Its data model is inherently object-oriented.
These features have led to Python's application in diverse fields, including:
●​ Web Development (e.g., Django, Flask)
●​ Data Science and Machine Learning (e.g., NumPy, Pandas, Scikit-learn, TensorFlow)
●​ Scientific and Numeric Computing
●​ Automation and Scripting
●​ Software Development and GUI Applications
The official Python Language Reference describes the syntax and core semantics of the
language, covering lexical analysis (how code is tokenized), the data model (objects, values,
types), and the execution model (program structure, naming, exceptions).
Setting up a Python development environment typically involves installing a Python interpreter
and, crucially, using virtual environments to manage project-specific dependencies. Virtual
environments, created using tools like venv (standard library) or virtualenv (third-party), isolate
project dependencies, preventing conflicts between different projects that might require different
versions of the same package.
Steps for Environment Setup (General Overview):
1.​ Install Python: Download the appropriate Python installer for the operating system from
the official Python website or use package managers like Homebrew (macOS) or apt
(Debian/Ubuntu).
2.​ Create a Virtual Environment: Navigate to the project directory and use the command
python -m venv <environment_name> (e.g., python -m venv env).
3.​ Activate the Virtual Environment:
○​ On macOS/Linux: source <environment_name>/bin/activate.
○​ On Windows: .\<environment_name>\Scripts\activate.
4.​ Install Packages: Once activated, use pip install <package_name> to install necessary
libraries within the isolated environment.
5.​ Deactivate: Use the command deactivate to exit the virtual environment.

1.2. Python Variables and Data Types


In Python, a variable serves as a named reference to a value stored in memory. Variables are
created when a value is first assigned to them; no explicit declaration is required beforehand.
This is a key aspect of Python's dynamic typing system.
Dynamic Typing: Python is a dynamically typed language, meaning the type of a variable is
determined at runtime based on the value assigned to it. The same variable can hold values of
different types at different points in the program's execution. Example:
x = 10 # x is an integer​
print(type(x)) # Output: <class 'int'>​
x = "Python" # x is now a string​
print(type(x)) # Output: <class 'str'>​

Variable Naming Rules (PEP 8 Conventions): Valid variable names must adhere to specific
rules :
●​ Must begin with a letter (a-z, A-Z) or an underscore (_).
●​ Can contain letters, numbers (0-9), and underscores after the first character.
●​ Are case-sensitive (myVar and myvar are distinct variables).
●​ Cannot be a Python reserved keyword (e.g., if, for, while, def). PEP 8, the style guide for
Python code, recommends using snake_case (lowercase with words separated by
underscores) for variable names (e.g., user_name, total_amount).
Variable Scope (LEGB Rule): The scope of a variable determines its accessibility within the
program. Python follows the LEGB rule for name resolution :
●​ L (Local): Names assigned within a function (def or lambda). These are not visible
outside the function. Example:​
def my_func():​
message = "Hello from local scope" # Local variable​
print(message)​
my_func()​
# print(message) # This would cause a NameError​

●​ E (Enclosing function locals): Names in the local scope of any enclosing functions
(e.g., in nested functions). The nonlocal keyword can be used to modify such variables.
Example:​
def outer_function():​
text = "Outer variable" # Enclosing scope variable​
def inner_function():​
nonlocal text # Declare intent to modify 'text' from
enclosing scope​
text = "Inner variable modified outer"​
print(text)​
inner_function()​
print(text) # Shows the modification​
outer_function()​

●​ G (Global): Names assigned at the top level of a module file, or declared global in a
function using the global keyword. Example:​
count = 100 # Global variable​
def show_count():​
print(count) # Accesses global 'count'​
def update_count():​
global count # Declare intent to modify global 'count'​
count += 1​
show_count() # Output: 100​
update_count()​
show_count() # Output: 101​

●​ B (Built-in): Pre-assigned names in Python (e.g., len(), print(), True, None). These are
always available.
Python searches for a name in this order: Local -> Enclosing -> Global -> Built-in. The first
occurrence found is used.
Constants: While Python doesn't have true constants (whose values cannot be reassigned),
the convention is to use all uppercase letters for names intended to be constants (e.g., PI =
3.14159, MAX_CONNECTIONS = 100). This signals to other developers that the value should
not be changed.
Built-in Data Types: Python offers several fundamental built-in data types :
●​ Numeric Types:
○​ int (Integer): Whole numbers (positive, negative, or zero) of unlimited size.
Example: age = 25.
○​ float (Floating-Point Number): Numbers with a decimal point. Example: price =
19.99.
○​ complex (Complex Number): Numbers with a real and imaginary part, written as a
+ bj. Example: z = 2 + 3j.
●​ Sequence Types:
○​ str (String): Ordered, immutable sequence of Unicode characters. Enclosed in
single ('...'), double ("..."), or triple ('''...''' or """...""") quotes. Example: name =
"Python".
○​ list: Ordered, mutable sequence of items. Enclosed in square brackets ``. Example:
numbers = .
○​ tuple: Ordered, immutable sequence of items. Enclosed in parentheses ().
Example: coordinates = (10, 20).
○​ range: Immutable sequence of numbers, commonly used for looping. Example:
range(0, 5).
●​ Mapping Type:
○​ dict (Dictionary): Unordered (ordered in Python 3.7+) collection of key-value pairs.
Enclosed in curly braces {}. Keys must be immutable and unique. Example: person
= {"name": "Alice", "age": 30}.
●​ Set Types:
○​ set: Unordered collection of unique, immutable items. Mutable. Enclosed in curly
braces {}. Example: unique_numbers = {1, 2, 3}.
○​ frozenset: Unordered collection of unique, immutable items. Immutable. Created
using frozenset(). Example: frozen_colors = frozenset({"red", "green"}).
●​ Boolean Type:
○​ bool: Represents truth values, True or False. Example: is_active = True.
●​ None Type:
○​ NoneType: Has a single value, None, used to represent the absence of a value or
a null value. Example: result = None.
●​ Binary Types: bytes, bytearray, memoryview for handling binary data.
Type Checking and Conversion:
●​ type(): Returns the type of an object. Example: type(10) returns <class 'int'>.
●​ isinstance(): Checks if an object is an instance of a particular class or type. Example:
isinstance(10, int) returns True.
●​ Type Conversion (Casting): Python allows explicit conversion between compatible data
types using built-in functions like int(), float(), str(), list(), tuple(), set(), dict(), bool().
○​ Example: str_num = "123"; num = int(str_num) converts the string "123" to the
integer 123.
○​ Example: float_val = 3.9; int_val = int(float_val) results in int_val being 3
(truncation).
○​ Python also performs implicit type conversion (coercion) in certain situations, like
adding an integer and a float, where the result is a float to avoid data loss. For
instance, 1 + 2.0 results in 3.0.
Mutable and Immutable Types:
●​ Immutable: Objects whose state cannot be changed after creation. Examples: int, float,
str, tuple, frozenset. Operations that appear to modify them actually create new objects.​
x = 10​
print(id(x)) # Memory address of x​
x = x + 1 # A new integer object 11 is created, and x now
refers to it​
print(id(x)) # Different memory address​
my_tuple = (1, 2, 3)​
# my_tuple = 4 # This would raise a TypeError​

●​ Mutable: Objects whose state can be changed after creation. Examples: list, dict, set.​
my_list = ​
print(id(my_list)) # Memory address of my_list​
my_list.append(4) # Modifies the original list object in place​
print(id(my_list)) # Same memory address​
print(my_list) # Output: ​

The distinction between mutable and immutable types is fundamental in Python. When an
immutable object is "changed," a new object is created in memory, and the variable name is
rebound to this new object. For mutable objects, operations can modify the object in-place
without creating a new object. This has implications for how variables are shared and modified,
especially when passed to functions or when multiple variables reference the same mutable
object. For example, if list_a = and list_b = list_a, modifying list_a (e.g., list_a.append(3)) will
also change list_b because both names refer to the same list object in memory. This concept of
shared references is crucial.
Assigning Multiple Variables: Python allows assigning a single value to multiple variables or
multiple values to multiple variables in a single line.
●​ Single value to multiple variables: x = y = z = 10
●​ Multiple values to multiple variables (packing/unpacking): a, b, c = 1, 2, 3
Deleting Variables: The del keyword can be used to remove a variable from the current
namespace. Example: x = 10; del x; # print(x) would raise NameError

1.3. Operators in Python


Operators are special symbols or keywords that perform operations on values (operands).
Python supports a rich set of operators categorized as follows :
●​ Arithmetic Operators: Perform mathematical calculations.
○​ + (Addition): a + b
○​ - (Subtraction): a - b
○​ * (Multiplication): a * b
○​ / (Division): a / b (results in a float)
○​ // (Floor Division): a // b (results in an integer, discards remainder)
○​ % (Modulus): a % b (remainder of division)
○​ ** (Exponentiation): a ** b (a^b)
○​ Examples: 10 / 3 is 3.33...; 10 // 3 is 3; 10 % 3 is 1; 2 ** 3 is 8.
●​ Comparison (Relational) Operators: Compare two values and return a Boolean (True or
False).
○​ == (Equal to): a == b
○​ != (Not equal to): a!= b
○​ > (Greater than): a > b
○​ < (Less than): a < b
○​ >= (Greater than or equal to): a >= b
○​ <= (Less than or equal to): a <= b
○​ Example: 5 > 3 is True; 5 == 3 is False.
●​ Logical Operators: Combine Boolean expressions.
○​ and: a and b (True if both a and b are true)
○​ or: a or b (True if either a or b is true)
○​ not: not a (True if a is false, and vice-versa)
○​ Example: (5 > 3) and (2 < 4) is True. not (5 > 3) is False.
○​ Python's logical operators use short-circuit evaluation. For a and b, if a is false, b is
not evaluated. For a or b, if a is true, b is not evaluated.
●​ Assignment Operators: Assign values to variables.
○​ = (Simple Assignment): x = 5
○​ += (Add and assign): x += 2 (equivalent to x = x + 2)
○​ -= (Subtract and assign): x -= 2
○​ *= (Multiply and assign): x *= 2
○​ /= (Divide and assign): x /= 2
○​ //= (Floor divide and assign): x //= 2
○​ %= (Modulus and assign): x %= 2
○​ **= (Exponentiate and assign): x **= 2
○​ &=, |=, ^=, >>=, <<= (Bitwise assignment operators)
○​ Example: count = 0; count += 1 results in count being 1.
●​ Bitwise Operators: Perform operations on integers at the binary level.
○​ & (Bitwise AND)
○​ | (Bitwise OR)
○​ ^ (Bitwise XOR)
○​ ~ (Bitwise NOT/Complement)
○​ << (Bitwise Left Shift)
○​ >> (Bitwise Right Shift)
○​ Example: a = 5 # 0101; b = 3 # 0011; print(a & b) is 1 # 0001.
●​ Membership Operators: Test for membership in sequences (e.g., strings, lists, tuples).
○​ in: True if a value is found in the sequence.
○​ not in: True if a value is not found in the sequence.
○​ Example: my_list = ; print(2 in my_list) is True.
●​ Identity Operators: Compare the memory locations of two objects.
○​ is: True if both variables point to the same object in memory.
○​ is not: True if both variables point to different objects in memory.
○​ Example: x = ; y = x; z = ; print(x is y) is True. print(x is z) is False (because x and z
are different list objects, even if their content is identical). However, print(x == z) is
True (value equality).
The "Pythonic" use of operators extends beyond basic arithmetic. Python overloads operators
for various data types in an intuitive manner. For instance, the + operator is used for string
concatenation (e.g., "hello" + " " + "world") and list concatenation (e.g., +). Similarly, the *
operator can be used for string repetition (e.g., "Ha" * 3 results in "HaHaHa") and list repetition
(e.g., * 3 results in ``). This operator overloading contributes significantly to Python's readability
and expressiveness, allowing code like if item in my_list: which is much clearer than a manual
loop for checking membership.
A crucial distinction lies between the is identity operator and the == equality operator. The is
operator checks if two variables refer to the exact same object in memory. In contrast, the ==
operator checks if the values of the objects are equivalent. For example, if a = and b = , then a
== b is True because their values are the same. However, a is b is False because a and b are
two distinct list objects created independently in memory. If, subsequently, c = a is assigned,
then a is c becomes True because c now refers to the same object as a. This understanding is
particularly vital when working with mutable objects, as modifications made to an object through
one reference will be visible through any other reference pointing to that same object. This
behavior also influences how functions might modify arguments passed to them if those
arguments are mutable.
Operator Precedence and Associativity: When an expression contains multiple operators,
Python evaluates them based on precedence and associativity rules. Higher precedence
operators are evaluated before lower precedence ones. Associativity determines the order for
operators of the same precedence (typically left-to-right, except for exponentiation and
assignment which are right-to-left). Parentheses () can be used to override the default
precedence and explicitly control the order of evaluation.
A clear understanding of operator precedence is essential for writing expressions that behave
as intended, reducing the need for excessive parentheses and ensuring code predictability. For
instance, in the expression a + b * c, multiplication (*) has higher precedence than addition (+),
so it is evaluated as a + (b * c). Associativity resolves cases like a / b * c, which, due to
left-to-right associativity for / and *, is evaluated as (a / b) * c.
Table 1: Python Operator Precedence (Highest to Lowest)
Operator Description Associativity
() Parentheses (grouping) n/a
f(args...), x[index], Function call, Slicing, Left-to-right
x[index:index], x.attribute Subscription, Attribute
reference
await x Await expression n/a
** Exponentiation Right-to-left
+x, -x, ~x Positive, Negative, Bitwise NOT Right-to-left
*, /, //, % Multiplication, Division, Floor Left-to-right
Division, Modulus
+, - Addition, Subtraction Left-to-right
<<, >> Bitwise Left Shift, Bitwise Right Left-to-right
Shift
& Bitwise AND Left-to-right
^ Bitwise XOR Left-to-right
` ` Bitwise OR
in, not in, is, is not, <, <=, >, >=, Comparisons, including Left-to-right
!=, == membership tests and identity
tests
not x Logical NOT Right-to-left
and Logical AND Left-to-right
or Logical OR Left-to-right
if-else Conditional expression Right-to-left
lambda Lambda expression n/a
:= Assignment expression (Walrus n/a
Operator Description Associativity
operator)
=, +=, -=, *=, /=, //=, %=, **=, =, ^=, >>=, <<=` Assignment operators
&=, `
Source: Synthesized from. The official Python documentation is the definitive source.

2. Control Flow
Control flow statements dictate the order in which the statements in a program are executed.
Python provides conditional statements for decision-making and loops for repetitive execution of
code blocks.

2.1. Conditional Statements


Conditional statements allow a program to execute different blocks of code based on whether
specified conditions evaluate to true or false. This forms the basis of decision-making in
programming.
●​ if Statement: The if statement is the most basic conditional statement. Its associated
block of code is executed only if the provided expression (condition) evaluates to True.
Python uses indentation (typically four spaces) to define the scope of the code block
under the if statement. Syntax:​
if expression:​
# statement(s) to execute if expression is True​
Example:​
temperature = 35​
if temperature > 30:​
print("It's a hot day!") # This will be printed​

●​ if-else Statement: The if-else statement provides an alternative block of code (the else
block) to be executed if the if condition evaluates to False. Syntax:​
if expression:​
# statement(s) to execute if expression is True​
else:​
# statement(s) to execute if expression is False​
Example:​
num = 7​
if num % 2 == 0:​
print("The number is even.")​
else:​
print("The number is odd.") # This will be printed​

●​ if-elif-else Ladder: For situations involving multiple mutually exclusive conditions, the
if-elif-else ladder (where elif stands for "else if") is used. Python evaluates the conditions
sequentially. The code block associated with the first condition that evaluates to True is
executed, and the rest of the ladder is skipped. An optional else block at the end can
serve as a default case if none of the preceding if or elif conditions are met. Syntax:​
if condition1:​
# code block 1​
elif condition2:​
# code block 2​
elif condition3:​
# code block 3​
#... more elif blocks​
else:​
# default code block (optional)​
Example:​
score = 85​
if score >= 90:​
print("Grade: A")​
elif score >= 80:​
print("Grade: B") # This will be printed​
elif score >= 70:​
print("Grade: C")​
else:​
print("Grade: F")​
The order of elif conditions is crucial, particularly if the conditions are not strictly mutually
exclusive. For instance, in the score example, if the condition score >= 80 were checked
before score >= 90, a score of 95 would incorrectly result in "Grade: B". Therefore, more
specific conditions should generally precede more general ones if overlap is possible. The
else block plays a vital role in handling cases not explicitly covered by the if or elif
statements, acting as a catch-all and contributing to robust program design by preventing
unhandled scenarios. Python's truth value testing, where non-empty sequences and
non-zero numbers evaluate to True, can lead to concise conditions like if my_list:, which
checks if my_list is not empty. While Pythonic, the explicitness of direct boolean
comparisons might be preferred for clarity in some contexts.
●​ Nested if Statements: Conditional statements can be nested within one another to
create more complex decision-making logic. An if, if-else, or if-elif-else statement can be
part of the code block of another conditional statement. Syntax:​
if condition1:​
# statement(s) for condition1​
if condition2:​
# statement(s) for condition1 AND condition2​
else:​
# statement(s) for condition1 AND NOT condition2​
else:​
# statement(s) if condition1 is False​
Example:​
num = 10​
if num > 0:​
print("Number is positive.")​
if num % 2 == 0:​
print("Number is also even.") # This will be printed​
else:​
print("Number is also odd.")​
else:​
print("Number is not positive.")​

●​ Ternary Conditional Operator (Conditional Expressions): Python provides a concise


way to write simple if-else statements in a single line, primarily used for assignments. This
is known as the ternary conditional operator or a conditional expression. Syntax:
value_if_true if condition else value_if_false Example:​
age = 20​
status = "adult" if age >= 18 else "minor"​
print(status) # Output: adult​

# Chained ternary operator​
num = 0​
sign = "positive" if num > 0 else ("negative" if num < 0 else
"zero")​
print(sign) # Output: zero​

2.2. Loops in Python


Loops are used to execute a block of code repeatedly. Python offers two main types of loops: for
loops and while loops.
●​ for Loops: for loops in Python are used for iterating over a sequence (such as a list,
tuple, dictionary, set, or string) or other iterable objects. Unlike for loops in some other
languages that primarily use a counter, Python's for loop directly iterates over the items of
the sequence in the order they appear.The versatility of Python's for loop stems from its
ability to work with any iterable object. This is a fundamental concept in Python. Built-in
sequences like lists, tuples, and strings are iterables, as are dictionaries and sets. Even
custom objects can be made iterable by implementing Python's iterator protocol (which
involves the __iter__() and __next__() methods). When a for loop begins, it implicitly calls
the iter() function on the iterable to obtain an iterator object. Then, it repeatedly calls the
next() function on this iterator to fetch subsequent items until a StopIteration exception is
raised, signaling the end of the iteration. This underlying mechanism allows for loops to
operate consistently and elegantly across a diverse range of data types and
structures.Syntax:​
for variable in iterable:​
# statement(s) to execute for each item​
Iterating over Sequences:
○​ Strings:​
for char in "Python":​
print(char)​
# Output: P, y, t, h, o, n (each on a new line)​

○​ Lists:​
colors = ["red", "green", "blue"]​
for color in colors:​
print(color)​
# Output: red, green, blue (each on a new line)​
○​ Tuples:​
coordinates = (10, 20, 30)​
for coord in coordinates:​
print(coord)​
# Output: 10, 20, 30 (each on a new line)​

○​ Dictionaries: By default, iterating over a dictionary iterates over its keys.​


person = {"name": "Alice", "age": 30}​
for key in person:​
print(key, ":", person[key])​
# Output: name : Alice, age : 30 (order may vary in Python <
3.7)​

# To iterate over values:​
for value in person.values():​
print(value)​
# Output: Alice, 30​

# To iterate over key-value pairs (items):​
for key, value in person.items():​
print(f"{key} -> {value}")​
# Output: name -> Alice, age -> 30​

range() Function: The range() function generates an immutable sequence of


numbers and is commonly used to control for loop iterations a specific number of
times. It produces an iterable, not a full list, making it memory-efficient for large
ranges, especially in Python 3.
○​ range(stop): Generates numbers from 0 up to (but not including) stop.​
for i in range(3):​
print(i) # Output: 0, 1, 2​

○​ range(start, stop): Generates numbers from start up to (but not including) stop.​
for i in range(1, 4):​
print(i) # Output: 1, 2, 3​

○​ range(start, stop, step): Generates numbers from start up to stop, incrementing by


step.​
for i in range(0, 10, 3):​
print(i) # Output: 0, 3, 6, 9​

Nested for Loops: A for loop can be placed inside another for loop. The inner loop
completes all its iterations for each single iteration of the outer loop.for i in
range(2): # Outer loop iterates twice (i=0, i=1)​
for j in range(3): # Inner loop iterates thrice for each i
(j=0, j=1, j=2)​
print(f"i={i}, j={j}")​
# Output:​
# i=0, j=0​
# i=0, j=1​
# i=0, j=2​
# i=1, j=0​
# i=1, j=1​
# i=1, j=2​

●​ while Loops: while loops execute a block of code repeatedly as long as a given Boolean
condition remains True. The condition is checked before each iteration. Syntax:​
while condition:​
# statement(s) to execute as long as condition is True​
Counter-Controlled Example:​
count = 1​
while count <= 3:​
print(f"Count is: {count}")​
count += 1 # Important: update the condition variable​
# Output: Count is: 1, Count is: 2, Count is: 3​
If the variable controlling the condition is not updated correctly within the loop, it can lead
to an infinite loop.Sentinel-Controlled Example (Conceptual): This type of loop
continues until a special value (sentinel) is encountered.​
# Conceptual example, requires user input​
# entry = ""​
# while entry.lower()!= "exit":​
# entry = input("Enter command (or 'exit' to quit): ")​
# if entry.lower()!= "exit":​
# print(f"Processing: {entry}")​
Nested while Loops: Similar to for loops, while loops can be nested.​
outer_count = 1​
while outer_count <= 2:​
inner_count = 1​
while inner_count <= 2:​
print(f"Outer: {outer_count}, Inner: {inner_count}")​
inner_count += 1​
outer_count += 1​
# Output:​
# Outer: 1, Inner: 1​
# Outer: 1, Inner: 2​
# Outer: 2, Inner: 1​
# Outer: 2, Inner: 2​
Infinite Loops: A loop whose condition always evaluates to True (e.g., while True:) will
run indefinitely unless explicitly terminated by a break statement or an external
interruption.

2.3. Loop Control Statements


Loop control statements alter the normal flow of execution within loops.
●​ break Statement: The break statement immediately terminates the innermost enclosing
for or while loop. Program execution then resumes at the next statement following the
terminated loop. Syntax: break Example:​
for i in range(1, 11): # Numbers from 1 to 10​
if i == 6:​
break # Exit loop when i is 6​
print(i)​
# Output: 1, 2, 3, 4, 5​
print("Loop terminated.")​

●​ continue Statement: The continue statement skips the rest of the code inside the current
iteration of the innermost for or while loop and proceeds directly to the next iteration. For
for loops, it moves to the next item in the iterable. For while loops, it re-evaluates the
loop's condition. Syntax: continue Example:​
for i in range(1, 6): # Numbers from 1 to 5​
if i == 3:​
continue # Skip printing when i is 3​
print(i)​
# Output: 1, 2, 4, 5​
print("Loop finished.")​

●​ pass Statement: The pass statement is a null operation; nothing happens when it is
executed. It is used as a placeholder where a statement is syntactically required, but no
code needs to be executed. This is common in defining empty functions, classes, or
conditional blocks that will be implemented later. Syntax: pass Example:​
def my_empty_function():​
pass # Placeholder for future implementation​

for i in range(3):​
if i == 1:​
pass # Do nothing specific for i=1​
else:​
print(i)​
# Output: 0, 2​

●​ else Clauses on Loops: A unique feature in Python is the ability to have an else clause
associated with for and while loops. The else block is executed only if the loop terminates
normally—that is, it completes all its iterations (for a for loop) or its condition becomes
False (for a while loop), without being exited by a break statement.This behavior is distinct
from the else in an if statement. It is particularly useful in search scenarios: if the loop
finds the item and breaks, the else is skipped; if the loop completes without finding the
item (no break), the else block can execute code indicating the item was not found. This
often leads to more readable code than using a separate flag variable. The else clause on
a loop shares more in common with the else clause of a try...except block, which runs if
no exception occurs.Syntax (for for loop):​
for item in iterable:​
if condition_met_for_break:​
# process item​
break​
# process item​
else:​
# executed if the loop completed without a break​
Example (for...else): Searching for a prime number​
num_to_check = 7​
for i in range(2, num_to_check):​
if num_to_check % i == 0:​
print(f"{num_to_check} is not a prime number, it is
divisible by {i}.")​
break​
else: # Belongs to the for loop​
print(f"{num_to_check} is a prime number.")​
# Output: 7 is a prime number.​
If num_to_check was, for example, 6, the inner if would be true for i=2, "6 is not a prime..."
would print, break would execute, and the else block would be skipped.Syntax (for while
loop):​
while condition:​
if condition_met_for_break:​
# process​
break​
# process​
else:​
# executed if the loop condition became False (and no break
occurred)​
Example (while...else):​
attempts = 3​
while attempts > 0:​
password = input("Enter password: ")​
if password == "secret":​
print("Access granted.")​
break​
attempts -= 1​
print(f"Incorrect. {attempts} attempts remaining.")​
else: # Belongs to the while loop​
print("Access denied. No attempts left.")​
If the correct password is entered, "Access granted" prints, break executes, and the else
block is skipped. If all attempts fail, the loop condition attempts > 0 eventually becomes
false, the loop terminates normally, and "Access denied..." is printed.

3. String Manipulation
Strings are fundamental data types in Python, representing immutable sequences of Unicode
characters. Python provides a rich set of tools for creating, accessing, and manipulating strings.

3.1. Strings in Python


●​ Definition: A string is an ordered, immutable sequence of Unicode characters. Being
immutable means that once a string is created, its content cannot be directly altered;
operations that appear to modify a string actually create and return a new string.
●​ Creation: Strings can be created using:
○​ Single quotes: my_string = 'Hello, Python!'
○​ Double quotes: my_string = "Hello, Python!"
○​ Triple quotes ('''...''' or """..."""): These are used for multi-line strings or strings that
need to contain single or double quotes without extensive escaping. Line breaks
within triple-quoted strings are preserved in the string itself unless a line ends with a
backslash \.​
multi_line_str = """This is a string​
that spans multiple​
lines."""​
print(multi__line_str)​

●​ Indexing: Individual characters within a string can be accessed using zero-based


indexing. Negative indexing is also supported, where -1 refers to the last character, -2 to
the second-to-last, and so on.​
s = "Python"​
print(s) # Output: P​
print(s[-1]) # Output: n​
# print(s) # This would raise an IndexError​

●​ Slicing: A portion of a string (a substring) can be extracted using slicing. The syntax is
string[start:end:step].
○​ start: The starting index (inclusive). Defaults to 0 if omitted.
○​ end: The ending index (exclusive). Defaults to the length of the string if omitted.
○​ step: The amount to increment the index by. Defaults to 1 if omitted.
s = "Programming"​
print(s[0:7]) # Output: Program (characters from index 0 to 6)​
print(s[:7]) # Output: Program (from the beginning up to
index 6)​
print(s[8:]) # Output: ming (from index 8 to the end)​
print(s[::2]) # Output: Pormig (every second character)​
print(s[::-1]) # Output: gnimmargorP (reverses the string)​

●​ Immutability: Strings are immutable. This means that operations like concatenation or
character replacement do not modify the original string; instead, they create and return a
new string.​
s1 = "hello"​
s2 = s1 + " world" # s2 is "hello world", s1 is still "hello"​
# s1 = "H" # This would raise a TypeError​
s1 = "H" + s1[1:] # Creates a new string "Hello" and reassigns s1​
print(s1) # Output: Hello​
This immutability has important implications. It makes strings predictable and safe to use
as dictionary keys because their hash value will never change. However, for operations
involving many modifications (like building a long string by repeatedly appending
characters in a loop), creating numerous intermediate string objects can be inefficient. In
such cases, techniques like joining a list of strings ("".join(list_of_strings)) are preferred
because they are optimized to pre-calculate the required memory and build the final string
in a more efficient manner, often in a single pass after collecting all parts.
●​ Concatenation (+) and Repetition (*):
○​ The + operator concatenates (joins) strings: greeting = "Hello" + " " + "World"
results in "Hello World".
○​ The * operator repeats a string a specified number of times: laugh = "ha" * 3 results
in "hahaha".
●​ Raw Strings: A raw string literal is prefixed with r or R. In a raw string, backslashes (\) are
treated as literal characters rather than initiating escape sequences. This is particularly
useful for regular expression patterns and Windows file paths, where backslashes have
special meanings. Syntax: raw_path = r"C:\Users\new_folder\notes.txt" Without the r
prefix, \n would be interpreted as a newline character. Note: A raw string cannot end with
an odd number of backslashes, as the final backslash would attempt to escape the
closing quote.
●​ Unicode Character Representation: Python strings are inherently sequences of
Unicode code points, allowing them to represent characters from virtually all writing
systems.
○​ Direct Inclusion: Unicode characters can often be typed directly into string literals
if the source file encoding (typically UTF-8) supports them: text = "你好, мир, γειά".
○​ Escape Sequences: Unicode characters can be represented using escape
sequences:
■​ \uXXXX: For 16-bit Unicode code points (four hexadecimal digits). Example:
'\u20AC' represents the Euro sign (€).

😀
■​ \UXXXXXXXX: For 32-bit Unicode code points (eight hexadecimal digits).
Example: '\U0001F600' represents a grinning face emoji ( ).
○​ ord(char): This built-in function returns the Unicode code point (an integer) of a
given character. Example: ord('A') returns 65; ord('€') returns 8364.
○​ chr(code_point): This built-in function returns the character (a string of length 1)
corresponding to a given Unicode code point. Example: chr(65) returns 'A';
chr(8364) returns '€'.

3.2. String Methods


Python strings come with a rich set of built-in methods for performing common operations. Since
strings are immutable, these methods do not modify the original string but instead return a new
string with the result of the operation.
The provision of a comprehensive suite of string methods is a testament to Python's design
philosophy of "batteries included." These methods, typically implemented in C for CPython, are
highly optimized and offer significant advantages in terms of performance, readability, and
conciseness over manual implementations using loops or basic string operations. For example,
using my_string.find(substring) is more direct and efficient than manually iterating through
my_string to locate substring. Similarly, the various is...() methods (e.g., isdigit(), isalpha(),
isspace()) simplify input validation and data cleansing tasks, often obviating the need for more
complex regular expressions for straightforward checks. Familiarity with these methods is key to
writing effective and Pythonic code.
Table 2: Common String Methods
Method Syntax Purpose Example
len() (built-in function) len(string) Returns the number of len("hello") is 5.
characters in the string.
Method Syntax Purpose Example
upper() string.upper() Converts all characters "Hello".upper() is
to uppercase. "HELLO".
lower() string.lower() Converts all characters "Hello".lower() is
to lowercase. "hello".
capitalize() string.capitalize() Converts the first "hELLo".capitalize() is
character to uppercase "Hello".
and the rest to
lowercase.
title() string.title() Converts to title case "hello world".title() is
(first letter of each word "Hello World".
uppercase, others
lowercase).
swapcase() string.swapcase() Swaps case of all "PyThOn".swapcase()
characters (upper to is "pYtHoN".
lower, lower to upper).
strip() string.strip([chars]) Removes leading and " ab c ".strip() is "ab c".
trailing characters
(whitespace by default).
chars is a string of
characters to remove.
lstrip() string.lstrip([chars]) Removes leading " ab c ".lstrip() is "ab c
characters. ".
rstrip() string.rstrip([chars]) Removes trailing " ab c ".rstrip() is " ab
characters. c".
split() string.split(separator=N Splits the string into a "a,b,c".split(',') is ['a', 'b',
one, maxsplit=-1) list of substrings. If 'c'].
separator is None,
splits by whitespace.
rsplit() string.rsplit(separator= Splits string from the "a-b-c".rsplit('-', 1) is
None, maxsplit=-1) right. ['a-b', 'c'].
join() separator_string.join(ite Concatenates strings "-".join(["a", "b"]) is
rable) from iterable with "a-b".
separator_string
between them.
find() string.find(sub, start=0, Returns lowest index of "hello".find("l") is 2.
end=len(string)) sub if found; else -1.
rfind() string.rfind(sub, start=0, Returns highest index "hello ello".rfind("l") is
end=len(string)) of sub if found; else -1. 7.
index() string.index(sub, Like find(), but raises "hello".index("e") is 1.
start=0, ValueError if not found.
end=len(string))
rindex() string.rindex(sub, Like rfind(), but raises "hello ello".rindex("l") is
start=0, ValueError if not found. 7.
end=len(string))
replace() string.replace(old, new, Replaces occurrences "mississippi".replace("is
count=-1) of old with new. count s", "ox") is "moxoxippi".
Method Syntax Purpose Example
limits replacements.
startswith() string.startswith(prefix, Returns True if string "apple".startswith("app"
start=0, starts with prefix. ) is True.
end=len(string))
endswith() string.endswith(suffix, Returns True if string "apple".endswith("ple")
start=0, ends with suffix. is True.
end=len(string))
isalnum() string.isalnum() True if all chars are "Python3".isalnum() is
alphanumeric and >=1 True.
char.
isalpha() string.isalpha() True if all chars are "Python".isalpha() is
alphabetic and >=1 True.
char.
isdecimal() string.isdecimal() True if all chars are "123".isdecimal() is
decimal chars and >=1 True.
char.
isdigit() string.isdigit() True if all chars are "123".isdigit() is True.
digits and >=1 char.
islower() string.islower() True if all cased chars "python".islower() is
are lowercase and >=1 True.
cased char.
isnumeric() string.isnumeric() True if all chars are "123.45".isnumeric() is
numeric and >=1 char. False.
isspace() string.isspace() True if all chars are " ".isspace() is True.
whitespace and >=1
char.
istitle() string.istitle() True if string is "Hello World".istitle() is
titlecased and >=1 True.
char.
isupper() string.isupper() True if all cased chars "PYTHON".isupper() is
are uppercase and >=1 True.
cased char.
center() string.center(width, Centers string within "Hi".center(5, '*') is
fillchar=' ') width, padding with "**Hi*".
fillchar.
ljust() string.ljust(width, Left-justifies string "Hi".ljust(5, '*') is
fillchar=' ') within width. "Hi***".
rjust() string.rjust(width, Right-justifies string "Hi".rjust(5, '*') is
fillchar=' ') within width. "***Hi".
zfill() string.zfill(width) Pads string with leading "42".zfill(5) is "00042".
zeros to width.
partition() string.partition(sep) Splits string at first sep, "a-b-c".partition('-') is
returns (head, sep, tail). ('a', '-', 'b-c').
rpartition() string.rpartition(sep) Splits string at last sep. "a-b-c".rpartition('-') is
('a-b', '-', 'c').
Method Syntax Purpose Example
splitlines() string.splitlines(keepen Splits string at line "a\nb".splitlines() is ['a',
ds=False) breaks into a list. 'b'].
encode() string.encode(encoding Encodes string to "€".encode() is
='utf-8', errors='strict') bytes. b'\xe2\x82\xac'.
maketrans() / str.maketrans(x,y,z) / For t=str.maketrans("ae","1
translate() s.translate(table) character-by-character 2");"apple".translate(t)
translation/mapping. is "1ppl2".
Source: Synthesized from. Examples are illustrative.

3.3. String Formatting


String formatting is the process of creating strings by embedding expressions or variables within
a template string. Python has evolved its string formatting capabilities over time, offering several
methods.
This evolution from %-formatting to str.format() and then to f-strings reflects Python's
commitment to enhancing readability, conciseness, and developer convenience. Each new
method aimed to address the limitations of its predecessor. %-formatting, while functional, could
become verbose and error-prone with multiple arguments or complex types. The str.format()
method offered greater flexibility, named placeholders, and better extensibility through the
__format__() special method, but its syntax could still be somewhat lengthy. F-strings
(introduced in Python 3.6 via PEP 498) achieved a high degree of directness and clarity by
allowing expressions to be embedded directly within the string literal. This progression
demonstrates Python's pragmatic design, responding to developer needs for more intuitive and
efficient tools for common tasks. The performance advantages often seen with f-strings also
align with Python's practical nature.
●​ f-Strings (Formatted String Literals): Introduced in Python 3.6, f-strings are the modern
and generally recommended way to format strings. They are prefixed with f or F and allow
embedding Python expressions directly inside string literals by placing them within curly
braces {}.
○​ Syntax: f"text {expression} text"
○​ Advantages: Highly readable, concise, and typically the fastest formatting method.
Expressions inside the braces are evaluated at runtime.
○​ Formatting Specifiers: F-strings support a mini-language for format specifiers,
similar to str.format(). These are placed after a colon within the expression:
f"{variable:.2f}" (formats a float to two decimal places), f"{variable:10}" (pads to a
width of 10).
○​ Self-documenting expressions (=): For debugging, f"{variable=}" expands to
variable=value_of_variable.
○​ Example:​
name = "Alice"​
age = 30​
pi_val = 3.14159​
print(f"Name: {name}, Age: {age}, Pi: {pi_val:.2f}")​
# Output: Name: Alice, Age: 30, Pi: 3.14​
print(f"Next year, {name} will be {age + 1} years old.")​
# Output: Next year, Alice will be 31 years old.​
print(f"{name=}, {age=}")​
# Output: name='Alice', age=30​

●​ str.format() Method: This method was introduced in Python 2.6 and offers a powerful
and flexible way to format strings. It uses curly braces {} as placeholders in the string,
which are then filled by arguments passed to the format() method.
○​ Syntax: template_string.format(arg1, arg2,..., kwarg1=value1,...)
○​ Positional Arguments: Placeholders can be empty {}, or numbered {0}, {1} to refer
to arguments by their position.​
print("Hello, {}! You are {} years old.".format("Bob", 25))​
# Output: Hello, Bob! You are 25 years old.​
print("The {1} {0} jumps over the {2} {0}.".format("dog",
"quick brown fox", "lazy"))​
# Output: The quick brown fox dog jumps over the lazy dog.​

○​ Keyword Arguments: Placeholders can refer to keyword arguments by name


{name}.​
print("Coordinates: x={x_coord},
y={y_coord}".format(x_coord=10, y_coord=20))​
# Output: Coordinates: x=10, y=20​

○​ Formatting Specifiers: A colon : followed by a format specifier can be used within


the placeholders to control alignment, padding, precision, type, etc..​
print("Value: {:.2f}".format(123.4567)) # Output: Value:
123.46​
print("Item: '{:<10}'".format("apple")) # Output: Item:
'apple ' (left-aligned)​
print("Item: '{:*>10}'".format("apple"))# Output: Item:
'*****apple' (right-aligned, fill with *)​

●​ %-Formatting (C-style): This is the original string formatting method in Python, similar to
C's printf. It uses the % operator to substitute values into a string.
○​ Syntax: format_string % values
○​ Specifiers: Common specifiers include %s (string), %d (integer), %f (float).
Precision for floats can be specified, e.g., %.2f.
○​ Disadvantages: Can be less readable for multiple substitutions, prone to errors if
types or number of arguments don't match the specifiers.
○​ Example:​
name = "Charlie"​
score = 95.5​
print("Player: %s, Score: %.1f" % (name, score))​
# Output: Player: Charlie, Score: 95.5​

●​ string.Template Class: This method, part of the string module, is considered safer when
dealing with format strings supplied by users, as it is less prone to unintended execution
of arbitrary code. It uses $-based substitutions.
○​ Syntax:​
from string import Template​
t = Template("Hello, $name! Your ID is $user_id.")​
print(t.substitute(name="Dave", user_id="dave123"))​
# Output: Hello, Dave! Your ID is dave123.​
# Use safe_substitute to avoid KeyError if a placeholder is
missing​
print(t.safe_substitute(name="Eve"))​
# Output: Hello, Eve! Your ID is $user_id.​

○​ Advantages: More secure for user-generated templates because it doesn't


evaluate expressions.
Table 3: Comparison of Python String Formatting Methods
Feature %-Formatting str.format() Method f-Strings string.Template
(Formatted String Class
Literals)
Readability Fair for simple Good Excellent Good for simple
cases substitutions
Performance Generally slower Moderate Generally fastest Moderate
Syntax "%s" % var "{}".format(var) f"{var}" Template("$var").s
Conciseness ubstitute()
Python Version All Python 2.6+/3.0+ Python 3.6+ Python 2.4+
Security (User Low (potential Moderate Low (evaluates High (safer)
Input) vulnerabilities) expressions)
Expression Limited type Good (access Excellent (arbitrary None (simple
Evaluation conversions attributes, items) expressions) substitution)
Primary Use Legacy code Older Python Most modern User-supplied
Case versions, complex Python code templates
formats
Source: Synthesized from.
For contemporary Python development (Python 3.6 and newer), f-strings are generally the
preferred method due to their superior readability and performance for most common use
cases. str.format() remains a viable and powerful option, especially if backward compatibility
with older Python 3 versions is required or for some advanced formatting scenarios.
%-formatting is largely considered legacy, and string.Template is the choice for security when
format strings originate from untrusted external sources.

4. Data Structures: Lists, Tuples, Sets, Dictionaries


Python offers several built-in data structures that are integral to its versatility, each with distinct
characteristics and use cases. These include lists, tuples, dictionaries, and sets.

4.1. Lists in Python


●​ Definition: Lists are ordered, mutable (changeable) collections of items. They are one of
Python's most versatile and commonly used data structures.
●​ Characteristics:
○​ Ordered: Items in a list are stored in a specific sequence, and this order is
preserved. The position of each item is defined by its index.
○​ Mutable: Lists can be modified after their creation. Elements can be added,
removed, or their values changed.
○​ Heterogeneous: A single list can contain items of different data types, such as
integers, strings, floats, or even other lists (creating nested lists).
○​ Dynamic Sizing: Lists can automatically grow or shrink in size as items are added
or removed.
○​ Allow Duplicates: A list can contain multiple instances of the same value.
○​ Stores References: List elements are essentially references (pointers) to the
actual objects stored in memory. This means a list doesn't store the objects
themselves directly within its structure but rather their memory addresses.
●​ Creation:
○​ Using square brackets `` with comma-separated items:​
my_list =​
empty_list =​

○​ Using the list() constructor, which can take an iterable (like a tuple, string, or
another list) as an argument:​
list_from_tuple = list((1, 2, 3)) # Becomes ​
list_from_string = list("hello") # Becomes ['h', 'e', 'l',
'l', 'o']​
empty_list_constructor = list()​

○​ Creating a list with repeated elements using the * operator:​


repeated_list = * 5 # Becomes ​

●​ Indexing and Slicing: Accessing and manipulating list elements is done similarly to
strings:
○​ Indexing: Access an individual item using its zero-based index. Negative indexing
is also supported (-1 for the last item).​
fruits = ["apple", "banana", "cherry"]​
print(fruits) # Output: apple​
print(fruits[-1]) # Output: cherry​

○​ Slicing: Extract a sub-list using list[start:end:step]. The start index is inclusive, end
is exclusive.​
numbers = ​
print(numbers[1:4]) # Output: ​
print(numbers[:3]) # Output: ​
print(numbers[::2]) # Output: ​

○​ Updating Elements: Since lists are mutable, individual elements or slices can be
reassigned.​
colors = ["red", "green", "blue"]​
colors = "yellow" # Changes "green" to "yellow"​
print(colors) # Output: ['red', 'yellow', 'blue']​
colors[0:2] = ["orange", "purple"] # Replaces first two
elements​
print(colors) # Output: ['orange', 'purple', 'blue']​
●​ Common List Methods: Python lists have a variety of built-in methods for
manipulation.Table 4: Common List Methods
Method Syntax Purpose Example
append() list.append(x) Adds element x to the L=; L.append(3) results
end of the list. in L=.
extend() list.extend(iterable) Appends all items from L=; L.extend() results in
iterable to the end of L=.
the list.
insert() list.insert(i, x) Inserts element x at L=; L.insert(1,2) results
index i. in L=.
remove() list.remove(x) Removes the first L=; L.remove(2) results
occurrence of element in L=.
x. Raises ValueError if
x is not found.
pop() list.pop([i]) Removes and returns L=; v=L.pop(1) makes
the item at index i. If i is v=2, L=.
omitted, removes and
returns the last item.
clear() list.clear() Removes all items from L=; L.clear() results in
the list, making it L=.
empty.
index() list.index(x[, start[, Returns the zero-based L=['a','b','c']; L.index('b')
end]]) index of the first is 1.
occurrence of x. Raises
ValueError if x is not
found.
count() list.count(x) Returns the number of L=; L.count(2) is 2.
times x appears in the
list.
sort() list.sort(key=None, Sorts the items of the L=; L.sort() makes L=.
reverse=False) list in-place. key and
reverse allow
customization.
reverse() list.reverse() Reverses the elements L=; L.reverse() makes
of the list in-place. L=.
copy() list.copy() Returns a shallow copy L1=; L2=L1.copy(). L2
of the list. Equivalent to is a new list.
list[:].
The copy() method and slicing ([:]) create a shallow copy. This means a new list object is
created, but the elements within this new list are references to the same objects that the original
list's elements refer to. If a list contains other mutable objects (e.g., nested lists), modifying
these nested objects through one list (original or copy) will affect the other. This is because both
lists hold references to the same nested mutable objects. For example, if a = [, ] and b =
a.copy(), then a and b refer to the same inner list . If `a.append(2)` is executed, this inner list
becomes . Consequently, b will also appear as [, ] because b still points to that same modified
inner list. To create a fully independent copy of a nested structure, a deep copy using the
copy.deepcopy() function from the copy module is necessary. This distinction is critical for
avoiding unintended side effects when working with lists of mutable objects.

4.2. List Comprehensions


List comprehensions provide a concise and often more readable way to create lists based on
existing iterables. They are a hallmark of Pythonic code and can be more efficient than using
explicit for loops with append() calls.
●​ Syntax: new_list = [expression for item in iterable if condition]
○​ expression: An operation or value to be included in the new list for each item.
○​ item: A variable representing the current element from the iterable.
○​ iterable: The source sequence or collection (e.g., list, tuple, range, string).
○​ if condition (optional): A filter that includes the item in the processing only if
condition is true.
While often described as syntactic sugar for a for loop, list comprehensions can offer
performance benefits. The iteration and element-creation logic is often implemented at a lower
level (closer to C in CPython), which can reduce the overhead associated with Python bytecode
execution for append() calls within a standard for loop. This makes list comprehensions not just
a tool for conciseness but also for efficiency in many common list-building scenarios. However,
for highly complex expressions or multiple levels of nesting with intricate conditions, the
readability of a list comprehension might decrease, and a traditional for loop could be more
maintainable.
●​ Examples:
○​ Basic list creation (squares of numbers):​
squares = [x**2 for x in range(1, 6)] # range(1,6) is
1,2,3,4,5​
# squares will be ​

○​ With an if condition (even numbers from a list):​


numbers = ​
even_numbers = [num for num in numbers if num % 2 == 0]​
# even_numbers will be ​

○​ With nested for loops (creating pairs):​


combinations = [(x, y) for x in for y in ['a', 'b']]​
# combinations will be [(1, 'a'), (1, 'b'), (2, 'a'), (2,
'b')]​

○​ Flattening a list of lists (matrix to list):​


matrix = [, , ]​
flattened_list = [element for row in matrix for element in
row]​
# flattened_list will be ​

4.3. Tuples in Python


●​ Definition: Tuples are ordered, immutable collections of items. Once a tuple is created,
its contents cannot be altered.
●​ Characteristics:
○​ Ordered: Items are stored in a specific sequence, and this order is maintained.
○​ Immutable: Elements cannot be changed, added, or removed after the tuple is
created. This makes tuples hashable if all their elements are hashable.
○​ Heterogeneous: Can store items of different data types.
○​ Allow Duplicates: A tuple can contain multiple occurrences of the same value.
●​ Creation:
○​ Using parentheses () with comma-separated items:​
my_tuple = (1, "banana", 2.71)​

○​ Comma-separated values (parentheses are optional if the context is clear, but often
recommended for readability):​
another_tuple = 10, 20, "see" # Creates a tuple (10, 20,
"see")​

○​ Using the tuple() constructor, which can take an iterable as an argument:​


tuple_from_list = tuple([1, 'x', 5]) # Becomes (1, 'x', 5)​
tuple_from_string = tuple("hey") # Becomes ('h', 'e',
'y')​

○​ Empty tuple:​
empty_tup = ()​
empty_tup_constructor = tuple()​

○​ Single element tuple: A trailing comma is mandatory to distinguish it from an


expression in parentheses.​
singleton_tuple = (42,) # Correct way​
not_a_tuple = (42) # This is just the integer 42​

●​ Indexing and Slicing: Tuples support indexing and slicing in the same way as lists and
strings.​
data = ("alpha", "beta", "gamma", "delta")​
print(data) # Output: beta​
print(data[-2]) # Output: gamma​
print(data[0:2]) # Output: ('alpha', 'beta')​

●​ Packing and Unpacking:


○​ Packing: The process of creating a tuple by assigning a sequence of
comma-separated values to a single variable.​
packed_data = 100, "Widget", 15.75 # packed_data is (100,
"Widget", 15.75)​

○​ Unpacking: Assigning the elements of a tuple to multiple variables. The number of


variables on the left must match the number of elements in the tuple.​
point = (10, 20)​
x, y = point # x becomes 10, y becomes 20​
print(f"x={x}, y={y}")​
Python 3 also supports extended unpacking with an asterisk:​
numbers = (1, 2, 3, 4, 5)​
first, second, *remaining = numbers​
# first is 1, second is 2, remaining is (a list)​

●​ Common Tuple Methods & Functions: Due to their immutability, tuples have fewer
methods than lists.
○​ tuple.count(value): Returns the number of times value appears in the tuple.​
t = (1, 2, 2, 3, 2)​
print(t.count(2)) # Output: 3​

○​ tuple.index(value): Returns the index of the first occurrence of value. Raises


ValueError if not found.​
t = ('a', 'b', 'c', 'b')​
print(t.index('b')) # Output: 1​

○​ Built-in Functions: len(), max(), min(), sum() (for numeric tuples), sorted() (returns
a new sorted list), any(), all() work with tuples as they do with other iterables.
●​ Use Cases (When to prefer tuples over lists): Tuples are often preferred over lists in
specific scenarios due to their immutability and the semantic meaning they can convey :
1.​ Heterogeneous Fixed Collections: When representing a collection of items that
form a single, fixed record or entity where the order and number of items are
meaningful (e.g., geographic coordinates (latitude, longitude), RGB color values
(red, green, blue), or database records).
2.​ Data Integrity: Immutability ensures that the data within a tuple cannot be
accidentally modified after creation, which is crucial for maintaining data integrity in
certain parts of a program.
3.​ Dictionary Keys: Since tuples are immutable (if all their elements are immutable),
they are hashable and can be used as keys in dictionaries. Lists, being mutable,
cannot be used as dictionary keys.
4.​ Returning Multiple Values from Functions: Python functions can conveniently
return multiple values, which are implicitly packed into a tuple. The caller can then
easily unpack these values.​
def get_user_info():​
name = "Alice"​
age = 30​
return name, age # Returns ("Alice", 30)​

user_name, user_age = get_user_info()​

5.​ Performance: In some specific, micro-optimization scenarios, iterating over a tuple


can be slightly faster than iterating over a list because of its fixed nature. However,
this difference is often negligible in most practical applications, and readability or
mutability requirements usually dictate the choice.
The immutability of tuples makes them behave like "lightweight records." When a function
returns a tuple, or when a tuple is used to store related pieces of data, it signals that this
collection of items is intended to be treated as a whole and should not change. This contrasts
with lists, which are typically used for collections of similar items that might grow, shrink, or
change over time.

4.4. Dictionaries
●​ Definition: Dictionaries in Python are mutable, unordered (ordered in Python 3.7+)
collections that store data as key-value pairs. Each key must be unique and immutable,
while values can be of any data type and can be duplicated. They are highly optimized for
retrieving values when the key is known.
●​ Characteristics:
○​ Key-Value Pairs: Data is stored in key: value mappings.
○​ Mutable: Dictionaries can be changed after creation (items can be added, modified,
or removed).
○​ Ordered (Python 3.7+): As of Python 3.7, dictionaries remember the insertion
order of items. In earlier versions (Python 3.6 and below), dictionaries were
unordered.
○​ Unique Keys: Keys within a dictionary must be unique. If a duplicate key is
assigned, the new value will overwrite the previous value associated with that key.
○​ Immutable Keys: Keys must be of an immutable type (e.g., strings, numbers,
tuples containing only immutable elements). Lists or other dictionaries cannot be
used as keys.
○​ Heterogeneous: Values can be of any data type. Keys can also be of different
immutable types within the same dictionary.
○​ Dynamic: Dictionaries can grow or shrink in size.
○​ Hashing: Dictionaries are implemented using hash tables, which allows for efficient
(typically average O(1) time complexity) lookup, insertion, and deletion of items.
●​ Creation:
○​ Using curly braces {} with comma-separated key: value pairs:​
person = {"name": "Alice", "age": 30, "city": "New York"}​
empty_dict = {} # Creates an empty dictionary​

○​ Using the dict() constructor:


■​ With a sequence of key-value tuples:​
employee = dict()​

■​ With keyword arguments (keys must be valid identifiers):​


config = dict(host="localhost", port=8080)​

■​ To create an empty dictionary: empty_dict_constructor = dict().


●​ Accessing Elements: Values are accessed using their corresponding keys.
○​ Using square brackets ``: dictionary[key]​
print(person["name"]) # Output: Alice​
# print(person["country"]) # Would raise a KeyError if
"country" is not a key​

○​ Using the get() method: dictionary.get(key, default_value) This method is safer as it


returns None (or a specified default_value) if the key is not found, instead of raising
a KeyError.​
print(person.get("age")) # Output: 30​
print(person.get("occupation")) # Output: None​
print(person.get("occupation", "Unemployed")) # Output:
Unemployed​

●​ Modifying (Adding/Updating) Items:


○​ Adding a new key-value pair: Assign a value to a new key.​
person["occupation"] = "Engineer"​
# person is now {'name': 'Alice', 'age': 30, 'city': 'New
York', 'occupation': 'Engineer'}​

○​ Updating an existing value: Assign a new value to an existing key.​


person["age"] = 31​
# person is now {'name': 'Alice', 'age': 31,...}​

○​ Using the update() method: Merges another dictionary or an iterable of key-value


pairs into the dictionary. If keys overlap, values from the argument dictionary
overwrite existing values.​
contact_info = {"email": "[email protected]", "phone":
"123-456-7890"}​
person.update(contact_info)​
person.update(city="Boston", zipcode="02110") # Can also use
keyword arguments​
print(person)​

●​ Deleting Items:
○​ del dictionary[key]: Removes the item with the specified key. Raises KeyError if key
is not found.​
# Assuming person has "zipcode" key​
# del person["zipcode"]​

○​ dictionary.pop(key, default_value): Removes the item with the specified key and
returns its value. If key is not found, it returns default_value (if provided) or raises
KeyError.​
# age = person.pop("age")​
# print(f"Removed age: {age}")​

○​ dictionary.popitem(): Removes and returns the last inserted key-value pair as a


tuple (in Python 3.7+). In earlier versions, it removes an arbitrary item. Raises
KeyError if the dictionary is empty.​
# last_item = person.popitem()​
# print(f"Removed item: {last_item}")​

○​ dictionary.clear(): Removes all items from the dictionary, making it empty.​


# person.clear() # person becomes {}​

●​ Common Dictionary Methods: Dictionaries provide various methods for accessing keys,
values, items, and performing other operations.Table 5: Common Dictionary Methods
Method Syntax Purpose Example
keys() dictionary.keys() Returns a view object d={'a':1,'b':2};
that displays a list of all k=d.keys(); print(list(k))
the keys in the is ['a','b'].
dictionary.
values() dictionary.values() Returns a view object d={'a':1,'b':2};
that displays a list of all v=d.values();
the values in the print(list(v)) is ``.
dictionary.
items() dictionary.items() Returns a view object d={'a':1,'b':2};
that displays a list of a i=d.items(); print(list(i))
dictionary's key-value is [('a',1),('b',2)].
tuple pairs.
get() dictionary.get(key, Returns the value for d={'a':1}; d.get('a') is 1;
default=None) key if key is in the d.get('c',0) is 0.
dictionary, else default.
If default is not given, it
defaults to None.
pop() dictionary.pop(key, Removes key and d={'a':1,'b':2};
default) returns its value. If key v=d.pop('a'); v is 1, d is
is not found, default is {'b':2}.
returned if given,
otherwise KeyError is
raised.
popitem() dictionary.popitem() Removes and returns a d={'a':1,'b':2};
(key, value) pair as a item=d.popitem(); item
2-tuple. Pairs are could be ('b',2).
returned in LIFO order
(last-in, first-out) for
Python 3.7+.
clear() dictionary.clear() Removes all items from d={'a':1}; d.clear()
the dictionary. makes d={}.
copy() dictionary.copy() Returns a shallow copy d1={'a':1};
of the dictionary. d2=d1.copy(); d2 is a
new dict.
update() dictionary.update([other Updates the dictionary d={'a':1};
]) with the key/value pairs d.update({'b':2,'a':3}); d
from other, overwriting is {'a':3,'b':2}.
existing keys. other can
be another dict or an
iterable of pairs.
setdefault() dictionary.setdefault(ke If key is in the d={'a':1};
y, default=None) dictionary, return its d.setdefault('a',0) is 1;
value. If not, insert key d.setdefault('b',0) is 0, d
with a value of default is {'a':1,'b':0}.
and return default.
default defaults to
Method Syntax Purpose Example
None.
Source: Synthesized from. Examples are illustrative.
The view objects returned by keys(), values(), and items() are dynamic. This means that if the
dictionary changes, the view reflects these changes. This is a powerful feature, as it avoids the
need to create a new list of keys/values/items every time the dictionary is modified if one is
iterating over these views.
The choice of dictionary keys (immutable types) is directly linked to the hashing mechanism that
underpins dictionary performance. Hashing requires that an object's hash value remains
constant throughout its lifetime. Mutable objects, like lists, can change their content, which
would change their hash value, making them unsuitable as dictionary keys. This immutability
requirement for keys ensures the integrity and efficiency of the dictionary's internal hash table.

4.5. Dictionary Comprehensions


Similar to list comprehensions, Python supports dictionary comprehensions, providing a concise
and readable way to create dictionaries.
●​ Syntax: new_dict = {key_expression: value_expression for item in iterable if condition}
○​ key_expression: Expression that evaluates to the key for each item.
○​ value_expression: Expression that evaluates to the value for each item.
○​ item: Variable representing the current element from the iterable.
○​ iterable: The source sequence or collection.
○​ if condition (optional): A filter that includes the item in the processing only if
condition is true.
●​ Purpose: To create dictionaries in a compact and expressive manner, often transforming
or filtering data from an existing iterable into key-value pairs.
●​ Examples:
○​ Creating a dictionary of squares:​
numbers = ​
square_dict = {x: x**2 for x in numbers}​
# square_dict will be {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}​

○​ Creating a dictionary from two lists (keys and values):​


keys_list = ['a', 'b', 'c']​
values_list = ​
combined_dict = {k: v for k, v in zip(keys_list,
values_list)}​
# combined_dict will be {'a': 10, 'b': 20, 'c': 30}​

○​ With an if condition (cubes of even numbers):​


num_range = range(1, 8) # 1, 2, 3, 4, 5, 6, 7​
even_cubes_dict = {n: n**3 for n in num_range if n % 2 == 0}​
# even_cubes_dict will be {2: 8, 4: 64, 6: 216}​

○​ With if/else in value expression (supported in comprehensions):​


# Example from [span_590](start_span)[span_590](end_span)
(adapted for clarity)​
nodes =​
start_node = 'A'​
distances = {node: (0 if node == start_node else
float('inf')) for node in nodes}​
# distances will be {'A': 0, 'B': inf, 'C': inf}​

4.6. Sets in Python


●​ Definition: Sets are unordered collections of unique, immutable items. They are mutable
themselves (elements can be added or removed), but the elements they contain must be
of an immutable type. Sets are highly useful for membership testing, removing duplicates
from a sequence, and performing mathematical set operations like union, intersection,
difference, and symmetric difference.
●​ Characteristics:
○​ Unordered: Elements in a set do not have a defined order. They are not stored in a
sequence and cannot be accessed by index.
○​ Mutable (for set type): Elements can be added to or removed from a set after its
creation. frozenset is an immutable version.
○​ No Duplicates: Sets automatically enforce uniqueness; each element can only
appear once. Adding a duplicate element has no effect.
○​ Heterogeneous (elements): Elements within a set can be of different immutable
data types (e.g., numbers, strings, tuples).
○​ Elements must be hashable: Since sets are typically implemented using hash
tables for efficient membership testing, their elements must be hashable (i.e.,
immutable).
●​ Creation:
○​ Using curly braces {} with comma-separated items (for non-empty sets):​
my_set = {1, "hello", 3.14, "hello"} # "hello" appears once​
# my_set will be {1, 3.14, 'hello'} (order may vary)​

○​ Using the set() constructor, which can take an iterable as an argument. This is also
the only way to create an empty set:​
set_from_list = set() # Becomes {1, 2, 3, 4}​
empty_set = set() # Creates an empty set​
# another_empty_set = {} # This creates an EMPTY
DICTIONARY, not a set!​

○​ frozenset: Immutable sets are created using frozenset(iterable).​


frozen = frozenset()​

●​ Common Set Methods and Operations: Sets support a variety of methods for
modification and mathematical operations.Table 6: Common Set Methods and
Operations
Method/Operation Syntax Purpose Example
add() set.add(element) Adds element to the s={1,2}; s.add(3)
set. If already present, makes s={1,2,3}.
Method/Operation Syntax Purpose Example
no change.
remove() set.remove(element) Removes element from s={1,2,3}; s.remove(2)
the set. Raises makes s={1,3}.
KeyError if element is
not found.
discard() set.discard(element) Removes element from s={1,2,3}; s.discard(4)
the set if present. Does leaves s={1,2,3}.
not raise an error if not
found.
pop() set.pop() Removes and returns s={1,2,3}; x=s.pop(). x
an arbitrary element is one of 1,2,3.
from the set. Raises
KeyError if the set is
empty.
clear() set.clear() Removes all elements s={1,2}; s.clear() makes
from the set, making it s=set().
empty.
union() / ` ` set1.union(set2,...) / Returns a new set
set1 | set2 containing all unique
elements from all sets.
intersection() / & set1.intersection(set2,.. Returns a new set s1={1,2};s2={2,3};
.) / set1 & set2 containing only s1.intersection(s2) is
elements common to all {2}.
sets.
difference() / - set1.difference(set2,...) Returns a new set with s1={1,2};s2={2,3};
/ set1 - set2 elements from set1 that s1.difference(s2) is {1}.
are not in set2 (and
other sets).
symmetric_difference() set1.symmetric_differe Returns a new set with s1={1,2};s2={2,3};
/^ nce(set2) / set1 ^ set2 elements in exactly one s1.symmetric_differenc
of the sets (elements in e(s2) is {1,3}.
either set1 or set2, but
not both).
issubset() / <= set1.issubset(set2) / Returns True if set1 is s1={1,2};s2={1,2,3};
set1 <= set2 a subset of set2. s1.issubset(s2) is True.
issuperset() / >= set1.issuperset(set2) / Returns True if set1 is s1={1,2,3};s2={1,2};
set1 >= set2 a superset of set2. s1.issuperset(s2) is
True.
isdisjoint() set1.isdisjoint(set2) Returns True if set1 s1={1,2};s2={3,4};
and set2 have no s1.isdisjoint(s2) is True.
elements in common.
update() / ` =` set1.update(set2,...) / Adds elements from
set1 |= set2 set2 (and others) to
set1. (In-place union)
intersection_update() / set1.intersection_updat Updates set1 keeping s1={1,2,3};s2={2,4};
&= e(set2,...) / set1 &= only elements found in s1.intersection_update(
Method/Operation Syntax Purpose Example
set2 both set1 and set2. s2) makes s1={2}.
(In-place intersection)
difference_update() / -= set1.difference_update( Updates set1 removing s1={1,2,3};s2={2,4};
set2,...) / set1 -= set2 elements found in set2. s1.difference_update(s
(In-place difference) 2) makes s1={1,3}.
symmetric_difference_ set1.symmetric_differe Updates set1 with s1={1,2,3};s2={2,4};
update() / ^= nce_update(set2) / set1 elements in either set1 s1.symmetric_differenc
^= set2 or set2 but not both. e_update(s2) makes
(In-place symmetric s1={1,3,4}.
difference)
copy() set.copy() Returns a shallow copy s1={1,2}; s2=s1.copy().
of the set. s2 is a new set.
Source: Synthesized from. Examples are illustrative. The non-operator versions of methods like
union(), intersection(), etc., can accept any iterable as an argument, automatically converting it
to a set before the operation. Their operator counterparts (|, &, etc.) require both operands to be
sets.

4.7. Set Comprehensions


Similar to list and dictionary comprehensions, Python also offers set comprehensions for
creating sets in a concise and readable way. They automatically handle duplicate elements,
ensuring the resulting collection is a valid set.
●​ Syntax: new_set = {expression for item in iterable if condition}
○​ expression: An operation or value to be included in the new set for each item.
○​ item: A variable representing the current element from the iterable.
○​ iterable: The source sequence or collection.
○​ if condition (optional): A filter that includes the item in the processing only if
condition is true. Note the use of curly braces {}, which are also used for dictionary
comprehensions. The distinction is that set comprehensions have only one
expression before the for clause, while dictionary comprehensions have a key:
value pair.
●​ Purpose: To create sets in a compact way, often by transforming or filtering elements
from an existing iterable, with automatic duplicate removal.
●​ Examples:
○​ Creating a set of squares:​
numbers = ​
squared_set = {x**2 for x in numbers}​
# squared_set will be {1, 4, 9, 16, 25} (duplicates from
numbers**2 are removed)​

○​ With an if condition (unique even numbers from a list):​


data = ​
unique_evens = {num for num in data if num % 2 == 0}​
# unique_evens will be {2, 4, 6, 8}​

○​ Creating a set of first letters from a list of words:​


word_list = ['apple', 'banana', 'apricot', 'blueberry']​
first_letters = {word for word in word_list}​
# first_letters will be {'a', 'b'} (order may vary)​

5. Functions & Lambda


Functions are fundamental building blocks in Python, allowing for the organization of code into
reusable blocks. Lambda functions offer a concise way to create small, anonymous functions.

5.1. Defining Functions in Python


Functions in Python are defined using the def keyword, followed by the function name,
parentheses () which may enclose parameters, and a colon :. The indented block of code
following the colon is the function body.
●​ Syntax:​
def function_name(parameter1, parameter2,...):​
"""Optional docstring explaining the function."""​
# statement(s)​
return # Optional return value​

●​ Calling Functions: Once defined, a function is called by using its name followed by
parentheses, passing any required arguments inside the parentheses.​
def greet(name):​
print(f"Hello, {name}!")​

greet("Alice") # Output: Hello, Alice!​

●​ Parameters and Arguments:


○​ Parameters: Variables listed inside the parentheses in the function definition.
○​ Arguments: Actual values passed to the function when it is called.
●​ Types of Arguments:
○​ Positional Arguments: Arguments passed to a function in the order they are
defined in the parameter list. The function call must provide arguments for all
positional parameters that do not have default values.​
def describe_pet(animal_type, pet_name):​
print(f"I have a {animal_type} named {pet_name}.")​
describe_pet("dog", "Buddy")​

○​ Keyword Arguments: Arguments passed to a function by explicitly naming the


parameters. The order of keyword arguments does not matter.​
describe_pet(pet_name="Lucy", animal_type="cat")​

○​ Default Parameter Values: Parameters can have default values, making the
corresponding argument optional during the function call. Default values are
specified in the function definition using the assignment operator =.​
def power(base, exponent=2): # exponent has a default value
of 2​
return base ** exponent​
print(power(5)) # Output: 25 (5**2)​
print(power(5, 3)) # Output: 125 (5**3)​
Default parameter values are evaluated only once when the function is defined.
This is important to remember when using mutable default values (like lists or
dictionaries), as modifications to such defaults persist across calls.
○​ Variable-Length Arguments (*args): Allows a function to accept an arbitrary
number of positional arguments. These arguments are collected into a tuple within
the function.​
def sum_all(*numbers): # numbers will be a tuple​
total = 0​
for num in numbers:​
total += num​
return total​
print(sum_all(1, 2, 3)) # Output: 6​
print(sum_all(10, 20, 30, 40)) # Output: 100​

○​ Variable-Length Keyword Arguments (**kwargs): Allows a function to accept an


arbitrary number of keyword arguments. These arguments are collected into a
dictionary within the function.​
def print_info(**details): # details will be a dictionary​
for key, value in details.items():​
print(f"{key}: {value}")​
print_info(name="John", age=30, city="New York")​
# Output:​
# name: John​
# age: 30​
# city: New York​

The order of parameters in a function definition generally follows: standard positional


parameters, *args, keyword-only parameters (parameters after *args or a bare *), and
then **kwargs.
●​ return Statement: The return statement exits a function and optionally passes a value (or
values) back to the caller. If no expression is given with return, or if the function execution
completes without encountering a return statement, the function implicitly returns None.
○​ Returning a Single Value:​
def multiply(a, b):​
return a * b​
result = multiply(4, 5) # result is 20​

○​ Returning Multiple Values: Python functions can return multiple values by listing
them after return, separated by commas. These values are automatically packed
into a tuple.​
def get_coordinates():​
return 10, 20 # Returns the tuple (10, 20)​
x, y = get_coordinates() # Unpacking the tuple​
print(f"x={x}, y={y}") # Output: x=10, y=20​
○​ Returning None:​
def log_message(message):​
print(message) # No explicit return​
output = log_message("Processing...") # output will be None​
print(output) # Output: None​

●​ Docstrings (Documentation Strings): A string literal placed as the first statement in a


function's body serves as its docstring. It's used to document what the function does, its
parameters, and what it returns. Docstrings are enclosed in triple quotes ("""...""" or '''...''')
and can be accessed via the function's __doc__ attribute or the help() function.
○​ Single-line docstring:​
def add(a, b):​
"""Return the sum of a and b."""​
return a + b​

○​ Multi-line docstring:​
def complex_function(param1, param2):​
"""​
A brief summary of what the function does.​

More detailed explanation can follow here, describing​
the algorithm, side effects, etc.​

Args:​
param1 (int): Description of the first parameter.​
param2 (str): Description of the second parameter.​

Returns:​
bool: Description of the return value.​
"""​
# function body​
return True​
Well-written docstrings are crucial for code maintainability and collaboration.
●​ Variable Scope (LEGB Rule): As previously discussed (Section 1.2), variables defined
inside a function have local scope by default. The global keyword can be used to modify
global variables from within a function, and the nonlocal keyword can be used in nested
functions to modify variables in an enclosing function's scope.

5.2. Lambda Functions


Lambda functions, also known as anonymous functions, provide a concise way to create small,
single-expression functions without a formal name using the def keyword. They are often used
for short-lived operations where defining a full function would be overly verbose.
●​ Syntax: lambda arguments: expression
○​ lambda: Keyword indicating a lambda function.
○​ arguments: Comma-separated list of input parameters (can be zero or more).
○​ expression: A single expression that is evaluated and its result is returned. Lambda
functions implicitly return the result of this expression; no explicit return statement is
used.
●​ Characteristics and Limitations:
○​ Anonymous: They don't have a name unless assigned to a variable (though this is
often discouraged if a regular function would be clearer).
○​ Single Expression: The body of a lambda function is limited to a single
expression. It cannot contain multiple statements, loops, or complex conditional
blocks (though a conditional expression is allowed).
○​ Concise: Ideal for simple operations.
●​ Use Cases: Lambda functions are most commonly used as arguments to higher-order
functions (functions that take other functions as arguments) or for short, throwaway
functions.
○​ With map(): To apply a simple transformation to each item in an iterable.​
numbers = ​
squared_numbers​

Works cited

1. Introduction to Python: History, Features, and Uses -,


https://syntaxscenarios.com/python/introduction-to-python-history-features-and-uses/ 2. The
Python Language Reference — Python 3.13.3 documentation,
https://docs.python.org/3/reference/index.html 3. Setting Up Virtual Environments in Python |
Information Technology and Computing Support,
https://it.engineering.oregonstate.edu/setting-virtual-environments-python 4. Setting up a Python
development environment | Google Cloud, https://cloud.google.com/python/docs/setup 5.
Variables in Python – Dr. Balvinder Taneja, https://drbtaneja.com/variables-in-python/ 6. Python
Variables | GeeksforGeeks, https://www.geeksforgeeks.org/python-variables/ 7. Python Scope &
the LEGB Rule: Resolving Names in Your Code ...,
https://realpython.com/python-scope-legb-rule/ 8. Python Global Variables,
https://www.pythoncentral.io/python-global-variables/ 9. Python Data Types with Examples and
Output - BlogsHub, https://blogshub.co.in/python-data-types-with-examples/ 10. Data Types in
Python (With Examples) - AlmaBetter,
https://www.almabetter.com/bytes/tutorials/python/data-types-in-python 11. Python Data Types |
DigitalOcean, https://www.digitalocean.com/community/tutorials/python-data-types 12. Data
Types — Python 3.13.3 documentation, https://docs.python.org/3/library/datatypes.html 13.
Python Data Type Conversion: A Guide With Examples | DataCamp,
https://www.datacamp.com/tutorial/python-data-type-conversion 14. Type Casting in Python
(Implicit and Explicit) with Examples ..., https://www.geeksforgeeks.org/type-casting-in-python/
15. Arithmetic Comparison Logical Assignment Bitwise Membership Identity operators,
https://www.youtube.com/watch?v=llgueg3Dyio 16. 19IT103 – COMPUTATIONAL THINKING
AND PYTHON PROGRAMMING - SNS Courseware,
https://ce.snscourseware.org/files/1678958911.pdf 17. operator — Standard operators as
functions — Python 3.13.3 ..., https://docs.python.org/3/library/operator.html 18. Operators —
Python Reference (The Right Way) 0.1 documentation,
https://python-reference.readthedocs.io/en/latest/docs/operators/ 19. Python String |
GeeksforGeeks, https://www.geeksforgeeks.org/python-string/ 20. 8.5. Precedence of Operators
— Foundations of Python Programming - Runestone Academy,
https://runestone.academy/ns/books/published/fopp/Conditionals/PrecedenceofOperators.html
21. Operator Precedence in Python: Understanding the Hierarchy of Operations - upGrad,
https://www.upgrad.com/tutorials/software-engineering/python-tutorial/operator-precedence-in-p
ython/ 22. Python Control Flow Statements and Loops | PDF - Scribd,
https://www.scribd.com/document/710214964/Python-Control-Flow-Statements-and-Loops 23.
4. More Control Flow Tools — Python 3.13.3 documentation,
https://docs.python.org/3/tutorial/controlflow.html#arbitrary-argument-lists 24. 8. Compound
statements — Python 3.13.3 documentation,
https://docs.python.org/3/reference/compound_stmts.html#function-definitions 25. Decision
Making Statements: If, If..else, Nested If..else and if-elif-else Ladder - ScholarHat,
https://www.scholarhat.com/tutorial/python/decision-making-statements-if-else-nested-if-else 26.
Python – if , if..else, Nested if, if-elif statements | GeeksforGeeks,
https://www.geeksforgeeks.org/python3-if-if-else-nested-if-if-elif-statements/ 27. control-flow.md
- wilfredinni/python-cheatsheet - GitHub,
https://github.com/wilfredinni/python-cheatsheet/blob/master/docs/cheatsheet/control-flow.md
28. Loops in Python – For, While and Nested Loops | GeeksforGeeks,
https://www.geeksforgeeks.org/loops-in-python/ 29. Python While Loop | GeeksforGeeks,
https://www.geeksforgeeks.org/python-while-loop/ 30. Python for Loops: The Pythonic Way –
Real Python, https://realpython.com/python-for-loop/ 31. For-Loops and While-Loops - Python
Like You Mean It,
https://www.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/ForLoops.html 32. Iterators
in Python | GeeksforGeeks, https://www.geeksforgeeks.org/iterators-in-python/ 33. Iterators and
Iterables in Python: Run Efficient Iterations – Real Python,
https://realpython.com/python-iterators-iterables/ 34. Python Iterators (With Examples) -
Programiz, https://www.programiz.com/python-programming/iterator 35. Loops in Python -
Codeingexpo, https://codeingexpo.great-site.net/loops-in-python/ 36. How To Use Python
Continue, Break and Pass Statements when ...,
https://www.digitalocean.com/community/tutorials/how-to-use-break-continue-and-pass-stateme
nts-when-working-with-loops-in-python-3 37. Loops and Control Statements (continue, break
and pass) in Python ...,
https://www.geeksforgeeks.org/loops-and-control-statements-continue-break-and-pass-in-pytho
n/ 38. Python Strings (With Examples) - Programiz,
https://www.programiz.com/python-programming/string 39. Built-in Types — Python 3.13.3
documentation, https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset 40. 3. An
Informal Introduction to Python — Python 3.13.3 documentation,
https://docs.python.org/3/tutorial/introduction.html 41. String Slicing in Python | GeeksforGeeks,
https://www.geeksforgeeks.org/string-slicing-in-python/ 42. Python Raw Strings |
GeeksforGeeks, https://www.geeksforgeeks.org/python-raw-strings/ 43. Escape Sequences and
Raw Strings in Python - GeeksforGeeks | Videos,
https://www.geeksforgeeks.org/videos/escape-sequences-and-raw-strings-in-python/ 44. How to
represent Unicode characters in Python strings | LabEx,
https://labex.io/tutorials/python-how-to-represent-unicode-characters-in-python-strings-398239
45. Python – Convert String to unicode characters - GeeksforGeeks,
https://www.geeksforgeeks.org/python-convert-string-to-unicode-characters/ 46. Built-in
Functions — Python 3.13.3 documentation, https://docs.python.org/3/library/functions.html 47.
Python String Methods | GeeksforGeeks, https://www.geeksforgeeks.org/python-string-methods/
48. How to use String Formatters in Python - GeeksforGeeks,
http://222.178.203.72:19005/whst/63/=vvvzfddjrenqfddjrznqf//how-to-use-string-formatters-in-pyt
hon/?ref=lbp 49. Python Print Formatting: Mastering Print Manipulation | Python Central,
https://www.pythoncentral.io/python-print-formatting-mastering-print-manipulation/ 50. How To
Use String Formatters in Python 3 | DigitalOcean,
https://www.digitalocean.com/community/tutorials/how-to-use-string-formatters-in-python-3 51.
Guide To String Formatting In Python | Simplilearn - Simplilearn.com,
https://www.simplilearn.com/tutorials/python-tutorial/string-formatting-in-python 52. PEP 498 –
Literal String Interpolation - Python Enhancement Proposals, https://peps.python.org/pep-0498/
53. 7. Input and Output — Python 3.13.3 documentation,
https://docs.python.org/3/tutorial/inputoutput.html 54. Python List methods | GeeksforGeeks,
https://www.geeksforgeeks.org/list-methods-python/ 55. Python Lists | GeeksforGeeks,
https://www.geeksforgeeks.org/python-lists/ 56. 5. Data Structures — Python 3.13.3
documentation, https://docs.python.org/3/tutorial/datastructures.html 57. List Comprehension in
Python | GeeksforGeeks, https://www.geeksforgeeks.org/python-list-comprehension/ 58.
Comprehensions in Python | GeeksforGeeks,
https://www.geeksforgeeks.org/comprehensions-in-python/ 59. Python Tuples | GeeksforGeeks,
https://www.geeksforgeeks.org/python-tuples/ 60. Tuple Operations in Python | GeeksforGeeks,
https://www.geeksforgeeks.org/tuples-in-python/ 61. tuple — Python Reference (The Right Way)
0.1 documentation - Read the Docs,
https://python-reference.readthedocs.io/en/latest/docs/tuple/ 62.
docs/docs/guides/development/python/python-tuples/index.md at develop - GitHub,
https://github.com/linode/docs/blob/develop/docs/guides/development/python/python-tuples/inde
x.md 63. Dictionaries in Python | GeeksforGeeks,
https://www.geeksforgeeks.org/python-dictionary/ 64. MicroPython - dictionary Data Type,
https://www.fredscave.com/23-micropython-data-types-dictionary.html 65. Data Structures (Part
II): Dictionaries - Python Like You Mean It,
https://www.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/DataStructures_II_Dictiona
ries.html 66. Dictionaries — Python Beginners documentation - Read the Docs,
https://python-adv-web-apps.readthedocs.io/en/latest/dicts.html 67. Python Dictionary
Comprehension | GeeksforGeeks,
https://www.geeksforgeeks.org/python-dictionary-comprehension/ 68. Learn Algorithm Design
by Building a Shortest Path Algorithm - Step 30 - Python,
https://forum.freecodecamp.org/t/learn-algorithm-design-by-building-a-shortest-path-algorithm-st
ep-30/675455 69. Python Sets: Create, Add, Remove with Example - WsCube Tech,
https://www.wscubetech.com/resources/python/sets 70. Sets in Python – Real Python,
https://realpython.com/python-sets/ 71. set — Python Reference (The Right Way) 0.1
documentation - Read the Docs, https://python-reference.readthedocs.io/en/latest/docs/sets/ 72.
Set Objects — Python 3.13.3 documentation, https://docs.python.org/3/c-api/set.html 73. ast —
Abstract Syntax Trees — Python 3.13.3 documentation,
https://docs.python.org/3/library/ast.html 74. List comprehension vs set comprehension - python
- Stack Overflow,
https://stackoverflow.com/questions/34059048/list-comprehension-vs-set-comprehension 75.
Python def Keyword - GeeksforGeeks,
http://222.178.203.72:19005/whst/63/=vvvzfddjrenqfddjrznqf//python-def-keyword/?itm_source=
auth&itm_medium=contributions&itm_campaign=improvements 76. Python Functions: How to
Call & Write Functions - DataCamp, https://www.datacamp.com/tutorial/functions-python-tutorial
77. Python def Keyword | GeeksforGeeks, https://www.geeksforgeeks.org/python-def-keyword/
78. Search — Python 3.10.17 documentation,
https://docs.python.org/3.10/search.html?q=argument 79. args and **kwargs in Python -
GeeksforGeeks, http://www.lamed-oti.com/school/rs/python/function_args_kwargs.pdf 80. 1.
*args and **kwargs — Python Tips 0.1 documentation,
https://book.pythontips.com/en/latest/args_and_kwargs.html 81. Python Return Statement -
Syntax, Usage, and Examples - Mimo, https://mimo.org/glossary/python/return 82. Python return
statement | GeeksforGeeks, https://www.geeksforgeeks.org/python-return-statement/ 83.
Python Docstrings Tutorial : Examples & Format for Pydoc, Numpy ...,
https://www.datacamp.com/tutorial/docstrings-python 84. Python Lambda Functions: A
Beginner's Guide | DataCamp, https://www.datacamp.com/tutorial/python-lambda-functions 85.
Using Lambda Functions with map, filter, and sorted in Python - w3resource,
https://www.w3resource.com/python-interview/how-do-you-use-lambda-functions-as-arguments-
to-higher-order-functions-like-map-filter-and-sorted.php 86. lambda — Python Reference (The
Right Way) 0.1 documentation,
https://python-reference.readthedocs.io/en/latest/docs/operators/lambda.html 87. 19. Lambdas
— Python Tips 0.1 documentation, https://book.pythontips.com/en/latest/lambdas.html

You might also like