Python% Handwritten Notebook
Python% Handwritten Notebook
and Programming
What is Programming?
Programming is the process of giving instructions to a computer to
perform specific tasks. It involves writing code in a programming
language that the computer can understand and execute.
Why Python?
[Link] is a high-level, interpreted programming language
known for its simplicity and readability.
[Link] is widely used in:
[Link] Development (Django, Flask)
[Link] Science and Machine Learning (Pandas, NumPy,
TensorFlow)
[Link] and Scripting
[Link] Development (Pygame)
[Link] has a large co mmunity and extensive libraries,
making it beginner-friendly.
Installing Python
1. Download Python:
[Link] Python:
[Link] the installer and ensure you check the box to Add Python to
PATH
(important for running Python from the command line).
[Link] Installation:
python --version
Choosing an IDE
[Link] is an IDE?
[Link] Integrated Development Environment (IDE) is a software
application that provides tools for writing, testing, and
debugging code.
[Link] Python IDEs:
[Link] Code: Lightweight, customizable, and supports extensions
for Python. (We will use this one as our primary IDE)
[Link]: Powerful IDE with advanced features for
professional developers.
[Link] Notebook: Great for data science and interactive coding.
[Link]: Comes pre-installed with Python; good for beginners.
[Link] the run button at the top of your IDE or alternatively type
this in your VS Code integrated terminal:
python [Link]
[Link]:
Hello, World!
Key Takeaways:
1. print() is a built-in function used to display output.
[Link] code is executed line by line.
[Link]:
[Link]:
[Link]
s:
for single-line comments.
[Link] #
"""
[Link]
''' o for multi-line comments.
[Link] r
e:
# This is a single-line
comment '''
This is a
multi-line comment
'''
•Example:
name = "Alice"
age = 25
height = 5.6
Best Practices-
•Use descriptive names that reflect the purpose of the variable.
•Use lowercase letters for variable names.
•Separate words using underscores for readabilityfirst_name
(e.g., ,
total_amount ).
Data Types in Python
Python supports several built-in data types:
Typecasting in Python
What is Typecasting?
•Typecasting is the process of converting one data type to another.
•Python provides built-in functions for typecasting:
•
: Converts to integer.
int()
•
: Converts to float.
float()
•
: Converts to string.
str()
•
: Converts to boolean.
bool()
Examples:
# Convert string to
integer
num_str = "10"
num_int = int(num_str)
print(num_int) # Output:
•By
input() returns a string. You can convert it to other data
default, types as
needed.
•Example:
Comments
•Comments are used to explain code and are ignored by
the Python interpreter.
# This is a single-line
comment '''
This is a
multi-line
comment '''
Escape Sequences
•Escape sequences are used to include special characters in strings.
• \n : Newline
•
\t : Tab
• : Backslash
\\
•
\" : Double quote
•
\' : Single quote
•Example:
print("Hello\nWorld!")
print("This is a tab\tcharacter.")
Print Statement
•The print() function is used to display output.
•You can sepand endparameters to customize the output.
use
Operators in Python
Types of Operators
[Link] Operators:
[Link]:
print(10 + 5) # Output: 15
print(10 ** 2) # Output: 100
[Link] Operators:
2. Example:
1.
and , or , not .
2. Example:
1.
= , += , -= , *= , /= , %= , **= , //= .
2. Example:
x = 10
x += 5 # Equivalent to x = x
+ 5 print(x) # Output: 15
5. B Membership Operators:
1. in , not in .
[Link]:
6. Identity Operators:
1. is , is not .
[Link]:
x = 10
y
= 10
Print(x is not y) # Output: True
Summary
•Variables store data, and Python supports multiple data types.
•Typecasting allows you to convert between data types.
•Us input()to take user input print()to display output.
e
and
•Comments and escape sequences help make your code more
readable.
•Python provides a variety of operators for performing operations on
data.
Control Flow and Loops
Syntax:
if condition1:
# Code to execute if condition1 is
True elif condition2:
# Code to execute if condition2 is
True else:
# Code to execute if all conditions are False
Example:
age =
18
What is Match-Case?
•Match-case is a new feature introduced in Python 3.10 for pattern
matching.
•It simplifies complex conditional logic.
Syntax:
match value:
case pattern1:
# Code to execute if value matches
pattern1 case pattern2:
# Code to execute if value matches
pattern2 case _:
# Default case (if no patterns match)
Example:
status =
404
match
status:
case 200:
print("Success!")
case 404:
print("Not Found")
case _:
print("Unknown
Example:
for fruit in
fruits:
print(fruit)
Using range() :
•Th
range() function generates a sequence of numbers.
e
•Example:
for i in range(5):
print(i) # Output: 0, 1, 2, 3, 4
Syntax:
while condition:
# Code to execute while condition is True
Example:
count =
0
Infinite Loops:
•Be careful to avoid infinite loops by ensuring the condition eventually
becomes False .
while True:
print("This will run forever!")
Break
•Th break statement is used to exit a loop prematurely.
e
•Example:
for i in
range(10): if
i == 5:
break
print(i) # Output: 0, 1, 2, 3, 4
Continu
e
statement skips the rest of the code in the current
•The continue iteration
and moves to the next iteration.
•Example:
for i in
range(5): if i
== 2:
continue
print(i) # Output: 0, 1, 3, 4
Pass
•Th pass statement is a placeholder that does nothing. It is used
e when syntax
requires a statement but no action is needed.
•Example:
for i in
range(5): if i
== 3:
pass # Do nothing
print(i) # Output: 0, 1, 2, 3, 4
Summary
•Us
if , , for decision-making.
e and
elif else
•Use for pattern matching (Python 3.10+).
match-case
•Us
loops to iterate over sequences loops for repeated
e
for and while
execution based on a
condition.
, continue , and pass .
•Control loop execution with
break
Strings in Python
Introduction
Strings are one of the most fundamental data types in Python. A string is
a
sequence of characters enclosed within either single quotes
' ( ), double
quotes
'''
(
" ), or triple or “““).
quotes (
Creating Strings
You can create strings in Python using different types of quotes:
# Single-quoted
string
a = 'Hello, Python!'
# Double-quoted
string
b = "Hello, World!"
String Indexing
Each character in a string has an index:
text = "Python"
print(text[0]) # Output:
P print(text[1]) #
Output: y
print(text[-1]) # Output: n (last character)
String Slicing
You can extract parts of a string using slicing:
String Methods
Python provides several built-in methods to manipulate strings:
String Formatting
Python offers multiple ways to format strings:
name =
"John"
age = 25
# Using format()
print("My name is {} and I am {} years old.".format(name,
age))
# Using f-strings (Python 3.6+)
print(f"My name is {name} and I am {age} years
old.")
Multiline Strings
Triple quotes allow you to create multi-line strings:
message = '''
Hello,
This is a multi-line string example.
Goodbye!
'''
print(message)
Summary
•Strings are sequences of characters.
•Use single, double, or triple quotes to define strings.
•Indexing and slicing allow accessing parts of a string.
•String methods help modify and manipulate strings.
•f-strings provide an efficient way to format strings.
Introduction
In Python, strings are sequences of characters, and each character
has an index. You can access individual characters using indexing
and extract substrings using slicing.
String Indexing
Each character in a string has a unique index, starting from 0 for the first
character and -1 for the last character.
text = "Python"
print(text[0]) # Output:
P print(text[1]) #
Output: y
print(text[-1]) # Output: n (last
character) print(text[-2]) # Output: o
String Slicing
Slicing allows you to extract a portion of a string using the syntax
string[start:stop:step .
]
Step Parameter
Summary
•Indexing allows accessing individual characters.
•Positive indexing starts from 0, negative indexing starts from -1.
•Slicing helps extract portions of a string.
•The step parameter defines the interval for selection.
•Using [::-1] reverses a string.
Introduction
Python provides a variety of built-in string methods and functions to
manipulate and process strings efficiently.
Changing Case
Removing Whitespace
text =
"apple,banana,orange"
fruits = [Link](",")
print(fruits) # Output: ['apple', 'banana', 'orange']
new_text = " - ".join(fruits)
print(new_text) # Output: "apple - banana - orange"
text = "Python123"
print([Link]()) # Output:
False print([Link]()) #
Output: False
print([Link]()) # Output:
True print([Link]()) #
print(ord('A')) # Output:
65 print(chr(65)) # Output:
'A'
name = "Alice"
age = 30
print("My name is {} and I am {} years old.".format(name,
age)) print(f"My name is {name} and I am {age} years old.")
Summary
•Python provides various string methods for modification and analysis.
•Case conversion, trimming, finding, replacing, splitting, and
joining are commonly used.
•Functions
len() , ord()
, and chr()are useful for working with string
like
properties.
Introduction
String formatting is a powerful feature in Python that allows you to insert
variables and expressions into strings in a structured way. Python
provides multiple ways to
.format()
format strings, including the method and the modern
older
f-strings .
name = "Alice"
age = 30
print("My name is {} and I am {} years old.".format(name,
age))
name = "Alice"
age = 30
print(f"My name is {name} and I am {age} years old.")
Using Expressions in f-Strings
x = 10
y = 5
print(f"The sum of {x} and {y} is {x + y}")
Formatting Numbers
pi = 3.14159265
print(f"Pi rounded to 2 decimal places: {pi:.2f}")
text = "Python"
print(f"{text:>10}") # Right
align print(f"{text:<10}") #
Left align
print(f"{text:^10}") # Center align
Important Notes
•Escape Sequences: \n , \t , \' , \" , \\ to handle special
Use characters in and
strings.
•Raw Strings: User"string" to prevent escape sequence
interpretation.
.decode()
•String Encoding & Decoding:
.encode()and to work
Use with different text
encodings.
•String Immutability: Strings in Python are immutable,
meaning they cannot be changed after creation.
•Performance Considerations: ''.join(list_of_stringsis more
Using efficient than )
concatenation in loops.
Summary
• .format() allows inserting values into placeholders.
•f-strings provide an intuitive and readable way to format strings.
•f-strings support expressions, calculations, and formatting options.
Functions and Modules
Syntax:
def greet(name):
return f"Hello,
{name}!"
print(greet("Alice")) # Output: Hello,
Alice!
Key Points:
•Defined defkeyword.
using
•Function name should be meaningful.
•Use return to send a value back.
Types of Arguments:
[Link] Arguments
[Link] Arguments
def greet(name="Guest"):
return f"Hello,
{name}!"
print(greet()) # Output: Hello,
Guest!
[Link] Arguments
Syntax:
square = lambda x: x * x
print(square(4)) # Output: 16
Example:
numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x**2,
numbers)) print(squared) # Output:
[1, 4, 9, 16]
4. Recursion in Python
A function calling itself to solve a problem.
def factorial(n):
if n == 1:
return 1
return n *
factorial(n-1)
print(factorial(5)) # Output:
120
Important Notes:
•Must have a base case to avoid infinite recursion.
•Used in algorithms like Fibonacci, Tree Traversals.
Importing Modules
Python provides built-in and third-party modules.
import
math
print([Link](16)) # Output:
4.0
import mymodule
print([Link]("Alice")) # Output: Hello, Alice!
Example usage:
import
requests
response =
[Link]("[Link]
print(response.status_code)
x = 10 # Global
variable
def my_func():
x = 5 # Local
variable print(x)
# Output: 5
my_func()
print(x) # Output: 10 (global x remains
unchanged)
x = 10 # Global
variable
def modify_global():
global x
x = 5 # Modifies the global
x
modify_global()
print(x) # Output:
5
Summary
•Functions help in reusability and modularity.
•Functions can take arguments and return values.
•Lambda functions are short, inline functions.
•Recursion is a technique where a function calls itself.
•Modules help in organizing code and using external libraries.
•Scope and lifetime of variables decide their accessibility.
•Docstrings are used to document functions, classes, and modules.
Data Structures in Python
Python provides powerful built-in data structures to store and manipulate
collections of data efficiently.
Creating a List:
numbers = [1, 2, 3, 4, 5]
mixed = [10, "hello", 3.14]
my_list = [1, 2,
3]
my_list.append(4) # [1, 2, 3, 4]
my_list.insert(1, 99)# [1, 99, 2, 3, 4]
my_list.remove(2) # [1, 99, 3, 4]
my_list.pop() # Removes last element -> [1, 99,
3]
my_list.reverse() # [3, 99, 1]
my_list.sort() # [1, 3, 99]
Creating a Tuple:
print(my_tuple[1]) # Output: 20
Tuple Unpacking:
a, b, c = my_tuple
print(a, b, c) # Output: 10 20 30
my_tuple = (1, 2, 2, 3, 4)
print(my_tuple.count(2)) # Output:
2
print(my_tuple.index(3)) # Output:
3
Why Use Tuples?
•Faster than lists (since they are immutable)
•Used as dictionary keys (since they are hashable)
•Safe from unintended modifications
Creating a Set:
my_set = {1, 2, 3,
4}
my_set.add(5) # {1, 2, 3, 4, 5}
my_set.remove(2) # {1, 3, 4, 5}
my_set.discard(10) # No error if element not
found my_set.pop() # Removes random element
Set Operations:
a = {1, 2,
3}
b = {3, 4,
print([Link](b # {1, 2, 3, 4,
)) 5}
print([Link](b)) # {3}
print([Link](b)) # {1,
2}
Creating a Dictionary:
print(student["name"])# Output:
Alice student["age"] = 22 #
Updating value
student["city"] = "New York" # Adding new key-value pair
Dictionary Comprehensions:
That’s what OOP is all about. It’s a way of programming that focuses on
creating “objects.” An object is like a self-contained unit that bundles
together:
•Class Attributes: These are shared by all objects of the class. Like
species in our Dog class. All dogs belong to the same species.
They are
defined outside of any method, directly within the class.
•Instance Attributes: These are specific to each individualname
object.
and breed are instance attributes. Each dog has its own name
and breed.
They are usually defined within the
init method.
3. The Constructor: Setting Things Up ( init
)
The method is special. It’s called the constructor. It’s
automatically
init run
whenever you create a new object from a class.
class Dog:
def init (self, name, breed):# The constructor
[Link] = name # Setting the name
attribute [Link] = breed # Setting the
breed attribute
# When we do this:
my_dog = Dog("Fido", "Poodle") # The init method is
automati
# It's like we're saying:
# 1. Create a new Dog object.
# 2. Run the init method on this new
object: # - Set my_dog.name to "Fido"
# - Set my_dog.breed to "Poodle"
You can also set default values for parameters in the constructor, making
them optional when creating an object:
class Dog:
def init (self, name="Unknown",
breed="Mixed"): [Link] = name
[Link] = breed
def speak(self):
print("Generic animal sound")
# Create objects:
my_dog = Dog("Rover")
my_cat = Cat("Fluffy")
p1 = Point(1,
2) p2 =
Point(3, 4)
Other useful magic methods: (You don’t need to memorize them all, but
be aware they exist!)
• ( - ), mul ( * ), truediv ( / ), ( == ), ne
sub eq
( != ) ( < ), ( > ( len()
lt gt len
, ), ), getitem
setitem , , behavior – allowing
(for list/dictionary-like
delitem
you
to use [] with your objects).
•Validation: You can add checks within the setter to make sure the
attribute is set to a valid value. For example, you could prevent an
age from being
negative.
•Read-Only Attributes: You can create a getter without a setter,
making the attribute effectively read-only from outside the class.
This protects the
attribute from being changed accidentally.
•Side Effects: You can perform other actions when an attribute is
accessed or
modified. For instance, you could update a display or log a change
whenever a value is set.
•Maintainability and Flexibility: If you decide to change how an
attribute is stored internally (maybe you switch from storing
degrees Celsius to
Fahrenheit), you only need to update the getter and setter methods.
You don’t need to change every other part of your code that uses
the attribute. This makes your code much easier to maintain and
modify in the future.
class Person:
def init (self,
name, age): [Link] =
name
Python offers a more elegant and concise way to define getters and
setters using
the @property decorator. This is the preferred way to implement them in
Python modern
.
class Person:
def init (self,
name, age): [Link] =
name
self._age = age # Convention: _age for "private"
attribu
class MyClass:
def init (self):
self._internal_value = 0 # Convention: _ means
"private
def get_value(self):
return
self._internal_value
obj = MyClass()
# print(obj._internal_value) # This *works*, but it's
against co print(obj.get_value()) # This is the preferred
way
Decorators in Python
Introduction
Decorators in Python are a powerful and expressive feature that allows
you to modify or enhance functions and methods in a clean and
readable way. They provide a way to wrap additional functionality
around an existing function without permanently modifying it. This is
often referred to as metaprogramming, where one part of the program
tries to modify another part of the program at compile time.
Understanding Decorators
A decorator is simply a callable (usually a function) that takes another
function as an argument and returns a replacement function. The
replacement function typically extends or alters the behavior of the
original function.
def my_decorator(func):
def wrapper():
print("Something is happening before the function is
call func()
print("Something is happening after the function is
calle return wrapper
@my_decorator
def say_hello():
print("Hello!"
)
say_hello(
)
Output:
def repeat(n):
def decorator(func):
def wrapper(a):
for _ in
range(n):
func(a)
return wrapper
@repeat(3)
def greet(name):
print(f"Hello,
{name}!")
greet("world")
Output:
Hello,
world!
Hello,
world!
def uppercase(func):
def wrapper():
return
func().upper() return
wrapper
def
exclaim(func):
def wrapper():
return func() +
"!!!" return wrapper
@uppercase
@exclaim
def greet():
return
"hello"
print(greet()
)
Output:
HELLO!!!
Here,
greet is first decorated byexclaim , and then the result of that is
decorated
uppercase . It’s equivalent greet = .
by to uppercase(exclaim(greet))
Recap
Decorators are a key feature in Python that enable code reusability and
cleaner function modifications. They are commonly used for:
Frameworks like Flask and Django use decorators extensively for routing,
authentication, and defining middleware.
Introduction
In object-oriented programming, getters and setters are methods used to
control access to an object’s attributes (also known as properties or
instance variables).
They provide a way to encapsulate the internal representation of an
object, allowing you to validate data, enforce constraints, and perform
other operations when an attribute is accessed or modified. While
Python doesn’t have private
variables in the same way as languages like Java, the convention is to use
a leading
underscore _( ) to indicate that an attribute is intended for internal use.
class Person:
def init (self, name):
self._name = name # Convention: underscore (_)
denotes a
def get_name(self):
return
self._name
def set_name(self,
new_name):
self._name = new_name
p =
Person("Alice")
#
print(p.get_name( Alice
))
#
p.set_name("Bob") Bob
Using @property (Pythonic Approach)
class Person:
def init (self,
name): self._name =
name
@property
def name(self): #
Getter return
self._name
@[Link]
def name(self, new_name): #
Setter self._name = new_name
p = Person("Alice")
print([Link]) # Alice (calls the
getter)
[Link] = "Bob" # Calls the
setter
print([Link]) # Bob
Benefits of@property:
•Attribute-like access: You can instead and
use of
[Link] obj.get_name()
, making the code cleaner and more readable.
obj.set_name()
@[Link]
def name(self):
del
self._name
p =
Person("Alice")
print([Link] #
) Alice
del [Link]
# AttributeError: 'Person' object has no
print([Link] attri
Read-Only Properties
If you want an attribute to be read-only, define @property decorator
only the (the
getter) and omit the @[Link] method. Attempting to set the
attribute will
then raise anAttributeError.
class Circle:
def init (self,
radius): self._radius
= radius
@property
def radius(self):
return
self._radius
@propert
y
def area(self): # Read-only computed
property
return 3.1416 * self._radius *
c = Circle(5)
print([Link]) # 5
print([Link]) #
78.54
# [Link] = 10 # Raises AttributeError: can't set
attribute
# [Link] = 20 # Raises AttributeError: can't set
Recap
•Getters and Setters provide controlled access to an object’s
attributes, promoting encapsulation and data validation.
•The @property decorator offers a cleaner and more Pythonic way to
implement getters and setters, allowing attribute-like access.
•You can create read-only properties by defining only a getter (using
@property without a corresponding
@<attribute>.sette ).
r
•Usin @property , you can dynamically compute values area in the
g
Circle (like the example) while maintaining an
attribute-like syntax.
Introduction
In Python, methods within a class can be of three main types:
class Dog:
def init (self, name):
[Link] = name # Instance
attribute
def speak(self):
return f"{[Link]} says
Woof!"
dog = Dog("Buddy")
print([Link]()) # Buddy says
Woof!
class Animal:
species = "Mammal" # Class attribute
@classmethod
def set_species(cls, new_species):
[Link] = new_species # Modifies class
attribute
@classmethod
def
get_species(cls)
: return
print(Animal.get_species()) #
Mammal
Animal.set_species("Reptile")
print(Animal.get_species()) #
class Person:
def init (self,
name, age): [Link] =
name
@classmethod
def from_string(cls, data):
name, age = [Link]("-")
return cls(name, int(age)) # Creates a new Person
instan
p = Person.from_string("Alice-
30")
print([Link], [Link])# Alice 30
In this
from_string acts as a factory method, providing an
example, way
Person alternative objects from a string.
to create
Static Methods @staticmetho
( )
d
Static methods are marked with @staticmethod
the decorator. They are similar
to
regular functions, except they are defined within the scope of a class.
•They don’t
self or as parameters.
take
cls
•They are useful when a method is logically related to a class but
doesn’t need to access or modify the instance or class state.
•Often used for utility functions that are related to the class
class MathUtils:
@staticmethod
def add(a, b):
return a +
b
print([Link](3, 5))
#
Recap
•Instance methods are the most common type and operate on
individual
objects (self ).
•Class methods operate on the class
cls ) and are often used for
itself ( methods or modifying class- factory
level attributes.
•Static methods are utility functions within a class that don’t depend
on the instance or class state. They’re like regular functions that are
logically grouped with a class.
Introduction
Magic methods, also called dunder (double underscore) methods, are
special methods in Python that have double underscores at the
beginning and end of their names (e.g., init , str , add ).
These methods allow you to define how your objects interact with built-
in Python operators, functions, and language constructs. They provide a
way to implement operator overloading and customize the behavior of
your classes in a Pythonic way.
1. – Object Initialization
init
class Person:
def init (self,
name, age): [Link] =
name
p = Person("Alice", 30)
print([Link], [Link])# Alice
30
class Book:
def init (self, title,
pages): [Link] = title
[Link] = pages
These methods allow you to define how your objects behave with
standard arithmetic and comparison operators.
class Vector:
def init (self,
x, y): self.x = x
self.y = y
print(v5) # Vector(10,
15)
• eq
(==)
• ne (!=)
• lt (<)
• gt (>)
• le (<=)
• (>
ge
• =)
(/)
truediv
• (//)
floordiv
•
• mod (%
)
pow (**
)
Recap
Magic (dunder) methods are a powerful feature of Python that allows you
to:
Introduction
Exceptions are events that occur during the execution of a program
that disrupt the normal flow of instructions. Python provides a robust
mechanism for handling
exceptions usingtry-except blocks. This allows your program to gracefully
recover from errors or unexpected situations, preventing crashes and
providing informative error messages. You can also define your own
custom exceptions to represent specific error conditions in your
application.
•The try block contains the code that might raise an exception.
•The except block contains the code that will be executed if a specific
exception occurs within thetry block.
try:
x = 10 / 0 # This will raise a
ZeroDivisionError except ZeroDivisionError:
print("Cannot divide by zero!")
Output:
try:
num = int(input("Enter a number: "))
result = 10 / num
except ZeroDivisionError:
print("You can't divide by zero!")
except ValueError:
print("Invalid input! Please enter a
number.")
# Alternative using a tuple:
try:
num = int(input("Enter a number: "))
result = 10 / num
except (ZeroDivisionError, ValueError)
as e:
print(f"An error occurred: {e}")
Usin else and finally
g
: The else block is optional and is executed only if no exception
• else occurs
within the trythe
try block. It’s useful for code that should run only when
block
succeeds.
block is also optional and is always executed,
• finally : The finally
regardless of whether an exception occurred or not. It’s typically used
for cleanup operations, such as closing files or releasing resources.
try:
file = open("[Link]",
"r") content = [Link]()
except FileNotFoundError:
print("File not
found!") else:
print("File read successfully.")
print(f"File contents:\n{content}")
finally:
[Link]() # Ensures the file is closed no matter what
Raising Exceptionsraise
( )
You can manually raise exceptions
raise keyword. This is useful for
using the signaling error conditions in
your own code.
def check_age(age):
if age < 18:
raise ValueError("Age must be 18 or
older!") return "Access granted."
try:
print(check_age(20)) # Access granted.
print(check_age(16)) # Raises
ValueError except ValueError as e:
print(f"Error: {e}")
Custom Exceptions
Python allows you to define your own custom exception classes by
creating a new
class that inherits (directly or indirectly) from the built-in
Exception class (or one
of its subclasses). This makes your error handling more specific and
informative.
class InvalidAgeError(Exception):
"""Custom exception for invalid age."""
def init (self, message="Age must be 18 or
older!"): [Link] = message
super(). init ([Link])
def
verify_age(age):
if age < 18:
raise InvalidAgeError() # Raise your custom
exception return "Welcome!"
try:
print(verify_age(16))
except InvalidAgeError as
e: print(f"Error:
{e}")
Conclusio
n
blocks are essential for handling errors and preventing
• try-except
crashes.
•Multiple except program blocks or a tuple of exception types can
be used to handle
different kinds of errors.
•Th else block executes only if no exception occurs block.
e in the
try
•Th
e finally block always executes, making it suitable for
cleanup tasks. keyword allows you to manually
•Th
e raise trigger exceptions.
Introduction
, , are higher-order functions in Python (and many
map
filter and reduce other
programming languages) that operate on iterables (lists, tuples, etc.).
They provide a concise and functional way to perform common
operations on sequences of data without using explicit loops. While they
were more central to Python’s functional programming style in earlier
versions, list comprehensions and generator expressions often provide a
more readable alternative in modern Python.
Map
The map() function applies a given function to each item of an iterable
and
returns an iterator that yields the results.
Syntax:map(function, iterable,
...)
• function : The function to apply to each item.
• iterable : The iterable (e.g., list, tuple) whose items will be
processed.
• ... : map can take multiple iterables. The function must take
the same number of arguments
numbers = [1, 2, 3, 4,
5]
Filte
r
function constructs an iterator from elements of an iterable
The filter() for
True
which a function . In other words, it filters the iterable based on
returns condition. a
Syntax:filter(function,
iterable)
• : A function that or for each item. If is
function returns True False None
passed, it defaults to checking if the element is True (truthy value).
• iterable : The iterable to be filtered.
numbers = [1, 2, 3, 4, 5,
6]
Syntax:reduce(function, iterable[,
initializer])
• function : A function that takes two arguments.
• iterable : The iterable to be reduced.
• initializer (optional): If provided, it’s placed before the items of
the iterable in the calculation and serves as a default when the
iterable is empty.
4, 5]
Introduction
The walrus operator ( := ), introduced in Python 3.8, is an assignment
expression operator. It allows you to assign a value to a variable within
an expression. This can make your code more concise and, in some
cases, more efficient by avoiding repeated calculations or function calls.
The name “walrus operator” comes from the operator’s resemblance to
the eyes and tusks of a walrus.
Use Cases
[Link] Expressions: The most common use case is if
within
statements,while loops, and list comprehensions, where you need to
both
test a condition and use the value that was tested.
numbers = [1, 2, 3, 4,
5]
[Link] Files: You can read lines from a file and process them within
a loop.
# Without Walrus
with open("my_file.txt", "r") as
f: line = [Link]()
while line:
print([Link]())
line = [Link]()
# With Walrus
with open("my_file.txt", "r") as
f: while (line :=
[Link]()):
Considerations
•Readability: While the walrus operator can make code more
concise, it can also make it harder to read if overused. Use it
judiciously where it improves clarity.
•Scope: The variable assigned using
:= is scoped to the surrounding
block
if while
(e.g., statement, loop, or list comprehension).
the
•Precedence: The walrus operator has lower precedence than most
other operators. Parentheses are often needed to ensure the
expression is evaluated as intended.
Introductio
n
are special syntaxes in Python function definitions
*args and **kwargs that
allow you to pass a variable number of arguments to a function. They
are used when you don’t know in advance how many arguments a
function might need to accept.
def my_function(*args):
print(type(args)) # <class
'tuple'> for arg in args:
print(arg)
def my_function(**kwargs):
print(type(kwargs)) # <class
'dict'> for key, value in
[Link]():
and **kwargs
Combining *args
You can use both *args and in the same function definition. The
order **kwargs
is *args must come **kwargs . You can also include regular
important: before
positional and keyword parameters.
def my_function(a, b, *args, c=10,
**kwargs):
print(f"a: {a}")
print(f"b: {b}")
print(f"args: {args}")
print(f"c: {c}")
print(f"kwargs: {kwargs}")
my_function(1,
2)
#
Output:
# a: 1
# b: 2
# args:
() # c:
Use Cases
•Flexible Function *args an **kwargs make your functions more
Design: d
flexible, allowing them to handle a varying number of inputs
without needing to define a specific number of parameters.
•Decorator Implementation: Decorators often *args and to
**kwargs
use wrap functions that might have different
signatures.
•Function Composition: You can use **kwargs to pass
*args an
through multiple layers of function d arguments
calls.
•Inheritance: Subclasses can accept extra parameters to those
defined by parent classes.
[Link] a file: You need to open a file before you can read from it
or write to it. This creates a connection between your program and
the file.
[Link] operations: You can then read data from the file or
write data to it.
[Link] the file: It’s crucial to close the file when you’re finished
with it. This releases the connection and ensures that any changes
you’ve made are saved.
•‘r’ (Read mode): Opens the file for reading. This is the default
mode. If the file doesn’t exist, you’ll get an error.
•‘w’ (Write mode): Opens the file for writing. If the file exists, its
contents will be overwritten. If the file doesn’t exist, a new file will
be created.
•‘a’ (Append mode): Opens the file for appending. Data will be
added to the end of the file. If the file doesn’t exist, a new file will
be created.
try:
file = open("my_file.txt", "r") # Open in read mode
content = [Link]()# Read the entire file
content
print(content)
[Link]() # Close the
file except FileNotFoundError:
print("File not found.")
Writing to a file:
Appending to a file:
try:
with open("my_file.txt", "r") as
file: content = [Link]()
print(content)
except FileNotFoundError:
print("File not
found.")
with open("[Link]", "w") as file:
[Link]("Data written using
'with'.\n")
os module examples:
import os
# Remove a file or
directory #
[Link]("my_file.txt")
# [Link]("new_directory") # removes empty directory
# [Link]("path/to/new_directory") # removes non-empty
dire
# Rename a file or directory
# [Link]("old_name.txt", "new_name.txt")
# Check if a file or directory
exists
if [Link]("my_file.txt"):
print("File exists")
import
shutil
# Copy a file
# [Link]("my_file.txt",
"my_file_copy.txt")
# Move a file or directory
# [Link]("my_file.txt",
"new_directory/")
import
argparse
args =
parser.parse_args()
try:
with open([Link], "r") as
file: content = [Link]()
for _ in range([Link]):
print(content)
except FileNotFoundError:
print("File not found.")
Virtual Environments:
•Windows: my_env\Scripts\
activate
•macOS/Linux:source
my_env/bin/activate
Once activated, you’ll see the virtual environment’s name in your
terminal prompt
(e.g., (my_env) ).
Installing a package:
pip install requests # Installs the "requests"
library pip install numpy==1.20.0 # Installs a
specific version
pip list
Upgrading a package:
Uninstalling a package:
A [Link] file lists all the packages your project depends on.
This
t
makes it easy to recreate the environment on another machine.
deactivate
import
requests
if response.status_code == 200:
data = [Link]() # Parse the JSON
response print(data["name"]) # Access data
from the JSON
else:
import re
Multithreading
These techniques allow your programs to perform multiple tasks
concurrently, improving performance.
import
threading
import time
def worker(num):
print(f"Thread {num}: Starting")
[Link](2) # Simulate some
work print(f"Thread {num}:
Finishing")
threads = []
for i in range(3):
thread = [Link](target=worker,
args=(i,)) [Link](thread)
[Link]()