Python
An introduction to Python programming.
Python is a high-level, interpreted programming language known for its simplicity and readability. It is widely used for web development, data analysis, artificial intelligence, scientific computing, and more.
Installing Python using a Version Manager
To install Python using a version manager, follow these steps based on your operating system.
Windows (using pyenv-win)
-
Open PowerShell as Administrator.
-
Install pyenv-win:
Invoke-WebRequest -UseBasicParsing https://pyenv.run | Invoke-Expression
-
Restart your terminal and install Python:
pyenv install 3.x.x # Replace with the desired version pyenv global 3.x.x
macOS/Linux (using pyenv)
-
Open Terminal.
-
Install pyenv dependencies:
curl https://pyenv.run | bash
-
Follow the prompts to add pyenv to your
.bashrc
or.zshrc
:export PATH="$HOME/.pyenv/bin:$PATH" eval "$(pyenv init --path)" eval "$(pyenv init -)"
-
Restart your terminal, then install Python:
pyenv install 3.x.x # Replace with the desired version pyenv global 3.x.x
Verifying Installation
Check the installed Python version:
python --version
Managing Python Versions and Virtual Environments (macOS/Linux) using pyenv
Once pyenv
is installed, follow these steps to install Python versions and manage virtual environments on Unix-based systems.
Installing Different Python Versions
-
List all available Python versions:
pyenv install --list
-
Install a specific Python version:
pyenv install 3.x.x # Replace with the desired version
-
Set the global default Python version:
pyenv global 3.x.x # Replace with the installed version you want as default
-
Set the Python version for the current shell session only:
pyenv shell 3.x.x # Replace with the version you want to use
Creating and Managing Virtual Environments
-
Create a new virtual environment:
pyenv virtualenv 3.x.x myenv # Replace with the desired Python version and environment name
-
Activate the virtual environment:
pyenv activate myenv
-
Deactivate the virtual environment:
pyenv deactivate
-
List all available environments:
pyenv virtualenvs
Installing Dependencies in a Virtual Environment
-
Activate the desired environment:
pyenv activate myenv
-
Install packages:
pip install package_name
-
To install all dependencies from a
requirements.txt
file:pip install -r requirements.txt
Saving and Recreating the Virtual Environment
-
To save the state of the virtual environment and create a
requirements.txt
file:pip freeze > requirements.txt
-
To recreate the environment on another system or after a fresh setup:
pip install -r requirements.txt
Managing Python Versions and Environments with pyenv and Poetry (macOS/Linux)
Use pyenv
to switch Python versions and Poetry
to manage dependencies and virtual environments. Here's how to use them together, including how to handle situations where the Python version is not installed.
Setting Python Version with pyenv
-
List all available Python versions:
pyenv install --list
-
Install the desired Python version:
pyenv install 3.x.x # Replace with the desired version
-
Set the global or local Python version for the project:
Global:
pyenv global 3.x.x
Local (for the current project folder):
pyenv local 3.x.x
Using Poetry to Manage the Environment
-
After switching to the desired Python version using
pyenv
, create a new Poetry project:poetry new myproject cd myproject
-
Configure Poetry to use the specific Python version set by
pyenv
:poetry env use 3.x.x
If the Python version is not installed, you can manually install it using
pyenv
or your system's package manager:pyenv install 3.x.x
-
To check which Python version is being used:
poetry env info
Installing Dependencies with Poetry
-
Install a package:
poetry add package_name
-
Install dependencies listed in
pyproject.toml
:poetry install
Saving and Recreating the Environment
-
To export the environment's dependencies to
poetry.lock
(happens automatically when adding packages):poetry lock
-
To recreate the environment in another system or after a fresh setup:
poetry install
Switching Between Python Versions
-
Use
pyenv
to switch Python versions:pyenv shell 3.x.x # Replace with the desired version
-
Update the Poetry environment to use the new Python version:
poetry env use 3.x.x
-
If the specified Python version in
pyproject.toml
is not installed, Poetry will give an error. You can fix this by installing the missing version withpyenv
:pyenv install 3.x.x
Hello World in Python
The classic first program in any language is printing "Hello, World!" to the screen.
print("Hello, World!")
This will output:
Hello, World!
Getting User Input
In Python, you can use the input()
function to get input from the user.
name = input("Enter your name: ")
print(f"Hello, {name}!")
This will prompt the user for their name and then greet them.
Enter your name: John
Hello, John!
Printing Output
Python's print()
function can display multiple variables and values.
age = 30
print("I am", age, "years old.")
This will output:
I am 30 years old.
You can also format the output with f-strings (introduced in Python 3.6+):
age = 30
print(f"I am {age} years old.")
Operators in Python
Python supports a variety of operators for performing different operations like arithmetic, comparison, logical, and bitwise operations.
1. Arithmetic Operators
Arithmetic operators are used for basic mathematical operations.
Operator | Description | Example |
---|---|---|
+ | Addition | 3 + 2 → 5 |
- | Subtraction | 5 - 3 → 2 |
* | Multiplication | 4 * 2 → 8 |
/ | Division | 8 / 2 → 4.0 |
% | Modulus (Remainder) | 5 % 2 → 1 |
** | Exponentiation | 2 ** 3 → 8 |
// | Floor Division | 5 // 2 → 2 |
2. Comparison Operators
Comparison operators are used to compare two values and return a boolean result (True
or False
).
Operator | Description | Example |
---|---|---|
== | Equal to | 3 == 3 → True |
!= | Not equal to | 3 != 2 → True |
> | Greater than | 5 > 3 → True |
< | Less than | 2 < 5 → True |
>= | Greater than or equal to | 3 >= 3 → True |
<= | Less than or equal to | 2 <= 5 → True |
3. Logical Operators
Logical operators are used to combine conditional statements.
Operator | Description | Example |
---|---|---|
and | Returns True if both statements are True | True and False → False |
or | Returns True if one of the statements is True | True or False → True |
not | Reverses the result, returns False if the result is True | not True → False |
4. Assignment Operators
Assignment operators are used to assign values to variables.
Operator | Description | Example |
---|---|---|
= | Assign value | x = 5 |
+= | Add and assign | x += 3 → x = x + 3 |
-= | Subtract and assign | x -= 3 → x = x - 3 |
*= | Multiply and assign | x *= 3 → x = x * 3 |
/= | Divide and assign | x /= 3 → x = x / 3 |
5. Bitwise Operators
Bitwise operators operate on the binary representations of integers.
Operator | Description | Example |
---|---|---|
& | AND | 5 & 3 → 1 |
` | ` | OR |
^ | XOR | 5 ^ 3 → 6 |
~ | NOT | ~5 → -6 |
<< | Left shift | 2 << 1 → 4 |
>> | Right shift | 4 >> 1 → 2 |
6. Membership and Identity Operators
Membership:
in
: Checks if a value is present in a sequence (e.g., list, tuple).not in
: Checks if a value is not present in a sequence.
Example:
x = [1, 2, 3]
print(2 in x) # Output: True
print(4 not in x) # Output: True
Identity:
is
: Checks if two variables refer to the same object.is not
: Checks if two variables do not refer to the same object.
Example:
a = [1, 2, 3]
b = a
print(a is b) # Output: True
Summary of Python Operators:
- Arithmetic operators: Perform basic math operations like addition and subtraction.
- Comparison operators: Compare two values and return boolean results.
- Logical operators: Combine conditional statements.
- Bitwise operators: Perform operations on binary representations of integers.
- Assignment operators: Assign values to variables and modify them.
- Membership and Identity operators: Check membership in sequences and identity of objects.
Data Types in Python
Python has several built-in data types. Here are the most common ones:
int
)
1. Integers (Whole numbers, positive or negative.
x = 10
print(type(x)) # Output: <class 'int'>
float
)
2. Floating Point Numbers (Numbers with decimal points.
x = 3.14
print(type(x)) # Output: <class 'float'>
str
)
3. Strings (A sequence of characters, enclosed in single or double quotes.
x = "Hello, World!"
print(type(x)) # Output: <class 'str'>
bool
)
4. Booleans (Logical values, either True
or False
.
x = True
print(type(x)) # Output: <class 'bool'>
list
)
5. Lists (Ordered, mutable collections of values.
x = [1, 2, 3, 4]
print(type(x)) # Output: <class 'list'>
dict
)
6. Dictionaries (Key-value pairs, unordered and mutable.
x = {"name": "John", "age": 30}
print(type(x)) # Output: <class 'dict'>
tuple
)
7. Tuples (Ordered, immutable collections of values.
x = (1, 2, 3)
print(type(x)) # Output: <class 'tuple'>
set
)
8. Sets (Unordered collections of unique values.
x = {1, 2, 3}
print(type(x)) # Output: <class 'set'>
Summary:
- Python provides several built-in data types such as integers, floats, strings, booleans, lists, dictionaries, tuples, and sets.
- You can use the
input()
function to get user input, and theprint()
function to output information to the console.
Additional Data Types in Python
Beyond the basic types like integers, floats, strings, and lists, Python provides other useful data types that may be less commonly discussed but are equally important.
None
)
1. NoneType (Represents the absence of a value or a null value.
x = None
print(type(x)) # Output: <class 'NoneType'>
None
is often used as a placeholder or default value in Python functions and objects.
bytes
)
2. Bytes (Immutable sequences of bytes, often used for binary data or encoding.
x = b"hello"
print(type(x)) # Output: <class 'bytes'>
Bytes are useful when working with binary data, such as files or network transmissions.
bytearray
)
3. Bytearray (Similar to bytes
, but mutable (you can change elements).
x = bytearray(b"hello")
x[0] = 72 # Change the first element
print(x) # Output: bytearray(b'Hello')
Use bytearray
when you need to modify a sequence of bytes.
memoryview
)
4. Memoryview (A way to access the memory of an object without copying it, typically used with bytes
and bytearray
.
x = memoryview(b"hello")
print(x) # Output: <memory at 0x...>
memoryview
can be useful for performance optimization when working with large binary data.
range
)
5. Range (Represents a sequence of numbers, commonly used in loops.
x = range(10)
print(type(x)) # Output: <class 'range'>
range
is useful when you want to iterate over a sequence of numbers without creating a list in memory.
complex
)
6. Complex Numbers (Numbers with a real and imaginary part.
x = 3 + 5j
print(type(x)) # Output: <class 'complex'>
Complex numbers are useful in mathematical and scientific computations where imaginary numbers are required.
frozenset
)
7. Frozenset (An immutable version of a set. Once created, elements cannot be added or removed.
x = frozenset([1, 2, 3])
print(type(x)) # Output: <class 'frozenset'>
frozenset
is useful when you need an immutable set, typically as keys in dictionaries or elements in other sets.
Summary of Additional Data Types:
NoneType
: Represents the absence of a value.bytes
andbytearray
: For handling binary data, withbytearray
being mutable.memoryview
: Efficient memory access without copying.range
: A sequence of numbers, often used in loops.complex
: For working with complex numbers.frozenset
: Immutable sets.
Useful Built-in Functions in Python
Python provides a variety of built-in functions that make development easier and more efficient. Here are some of the most commonly used ones:
callable()
1. Checks if an object can be called like a function. Functions, methods, and objects with a __call__()
method return True
.
def my_function():
return "Hello"
print(callable(my_function)) # Output: True
enumerate()
2. Adds a counter to an iterable (like a list or tuple) and returns it as an enumerate object, which you can loop over to get both the index and the value.
fruits = ['apple', 'banana', 'cherry']
for index, fruit in enumerate(fruits):
print(index, fruit)
Output:
0 apple
1 banana
2 cherry
zip()
3. Combines two or more iterables (such as lists or tuples) into pairs or tuples, returning them as a zip
object.
names = ['Alice', 'Bob', 'Charlie']
scores = [85, 90, 95]
combined = zip(names, scores)
for name, score in combined:
print(f"{name}: {score}")
Output:
Alice: 85
Bob: 90
Charlie: 95
map()
4. Applies a given function to each item of an iterable (like a list) and returns a map object.
numbers = [1, 2, 3, 4]
doubled = map(lambda x: x * 2, numbers)
print(list(doubled)) # Output: [2, 4, 6, 8]
filter()
5. Filters elements from an iterable based on a function that returns True
or False
.
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers)) # Output: [2, 4, 6]
sorted()
6. Returns a sorted list of the elements in an iterable.
numbers = [3, 1, 4, 1, 5, 9]
print(sorted(numbers)) # Output: [1, 1, 3, 4, 5, 9]
You can also sort based on a custom key:
words = ['banana', 'apple', 'cherry']
print(sorted(words, key=len)) # Output: ['apple', 'banana', 'cherry']
any()
7. Returns True
if any element in the iterable is True
. It stops at the first True
it finds.
numbers = [0, 1, 2, 3]
print(any(numbers)) # Output: True
all()
8. Returns True
if all elements in the iterable are True
. If any element is False
, it returns False
.
numbers = [1, 2, 3, 4]
print(all(numbers)) # Output: True
max()
and min()
9. Returns the largest and smallest items in an iterable, respectively.
numbers = [10, 20, 30, 40]
print(max(numbers)) # Output: 40
print(min(numbers)) # Output: 10
sum()
10. Returns the sum of all elements in an iterable.
numbers = [10, 20, 30]
print(sum(numbers)) # Output: 60
len()
11. Returns the length (number of items) of an object like a list, string, tuple, etc.
items = [1, 2, 3, 4, 5]
print(len(items)) # Output: 5
type()
12. Returns the type of an object.
x = 42
print(type(x)) # Output: <class 'int'>
isinstance()
13. Checks if an object is an instance or subclass of a class or a tuple of classes.
x = 42
print(isinstance(x, int)) # Output: True
Summary of Useful Built-in Functions:
callable()
: Check if an object can be called.enumerate()
: Add a counter to an iterable.zip()
: Combine iterables into tuples.map()
: Apply a function to each item in an iterable.filter()
: Filter items from an iterable based on a function.sorted()
: Return a sorted list.any()
: Check if any item in an iterable isTrue
.all()
: Check if all items in an iterable areTrue
.max()
andmin()
: Find the largest and smallest elements.sum()
: Sum up elements in an iterable.len()
: Find the length of an object.type()
: Get the type of an object.isinstance()
: Check if an object is an instance of a class.
Special Note: Iterables and Sequence Types in Python
What is an Iterable?
An iterable is any Python object capable of returning its elements one at a time. Iterables can be used in a for
loop or with functions like map()
, filter()
, and zip()
. Common iterable types in Python include:
- Lists (
list
) - Tuples (
tuple
) - Strings (
str
) - Dictionaries (
dict
) - Sets (
set
) - Range objects (
range
) - Files (open file objects)
Example of an Iterable:
numbers = [1, 2, 3, 4]
for number in numbers:
print(number)
In the above example, the list numbers
is iterable because you can loop through its elements.
Sequence Types
A sequence is a special type of iterable that has a specific order to its elements and supports element access using integer indices. Sequences include:
- Lists (
list
) - Tuples (
tuple
) - Strings (
str
) - Range objects (
range
) - Byte Array(
bytearray
)
Properties of Sequence Types:
- Indexing: Access elements using indices (e.g.,
my_list[0]
). - Slicing: Retrieve a slice (sublist) from the sequence using slicing (e.g.,
my_list[1:3]
). - Reversibility: Sequence types can often be reversed using methods like
reversed()
.
Example of a Sequence (List):
fruits = ['apple', 'banana', 'cherry']
print(fruits[0]) # Output: apple
# Slicing example
print(fruits[1:]) # Output: ['banana', 'cherry']
Iterators vs. Iterables
- An iterable is any object capable of returning an iterator (e.g., a list).
- An iterator is an object that represents a stream of data, produced one element at a time by calling
next()
on it.
Example of Creating an Iterator from an Iterable:
numbers = [1, 2, 3]
iterator = iter(numbers)
print(next(iterator)) # Output: 1
print(next(iterator)) # Output: 2
print(next(iterator)) # Output: 3
Mutable vs. Immutable Sequence Types
- Mutable sequences: These sequences can be modified after their creation (e.g., lists, bytearrays).
- Immutable sequences: These sequences cannot be modified (e.g., strings, tuples, bytes).
Mutable Sequence Example:
my_list = [1, 2, 3]
my_list[0] = 10
print(my_list) # Output: [10, 2, 3]
Immutable Sequence Example:
my_tuple = (1, 2, 3)
# Attempting to modify it will raise an error
# my_tuple[0] = 10 # Raises TypeError
Common Functions that Work on Iterables:
Many Python built-in functions operate on iterables and sequences:
len()
: Returns the length of an iterable.max()
/min()
: Returns the maximum or minimum value in a sequence.sorted()
: Returns a sorted sequence from an iterable.sum()
: Returns the sum of all elements in a sequence.
Summary:
- Iterable: Any object that can return its elements one at a time (used in loops).
- Sequence: A type of iterable that has order and supports indexing and slicing.
- Iterator: An object that represents a stream of data from an iterable, produced one element at a time using
next()
. - Mutable vs Immutable Sequences: Lists are mutable, while tuples and strings are immutable.
Strings in Python
A string in Python is a sequence of characters enclosed within single ('
), double ("
), or triple quotes ('''
, """
). Strings are immutable, meaning once created, their content cannot be changed.
Example:
my_string = "Hello, World!"
print(my_string)
String Indexing and Slicing
Strings are indexed starting from 0, and you can access individual characters or substrings using slicing.
my_string = "Python"
# Accessing individual characters
print(my_string[0]) # Output: P
print(my_string[-1]) # Output: n
# Slicing the string
print(my_string[1:4]) # Output: yth
Common String Operations
Here are some common string operations:
-
Concatenation: Use the
+
operator to concatenate strings.str1 = "Hello" str2 = "World" result = str1 + " " + str2 print(result) # Output: Hello World
-
Repetition: Use the
*
operator to repeat a string multiple times.print("Hello" * 3) # Output: HelloHelloHello
-
Length: Use
len()
to get the length of a string.my_string = "Hello" print(len(my_string)) # Output: 5
-
Membership: Use
in
to check if a substring is present in a string.print("Py" in "Python") # Output: True
String Formatting
String formatting allows you to insert variables or expressions inside strings. There are several ways to format strings in Python:
format()
:
1. Using name = "John"
age = 30
print("My name is {} and I am {} years old.".format(name, age))
# Output: My name is John and I am 30 years old.
2. Using f-strings (Python 3.6+):
F-strings are a modern, concise way to format strings.
name = "John"
age = 30
print(f"My name is {name} and I am {age} years old.")
# Output: My name is John and I am 30 years old.
3. Using Percent Formatting (Old-style):
This is an older way of formatting strings, using %
symbols.
name = "John"
age = 30
print("My name is %s and I am %d years old." % (name, age))
# Output: My name is John and I am 30 years old.
String Methods
Python provides several built-in methods for string manipulation. Here are some commonly used ones:
-
upper()
: Converts a string to uppercase.print("hello".upper()) # Output: HELLO
-
lower()
: Converts a string to lowercase.print("HELLO".lower()) # Output: hello
-
strip()
: Removes whitespace from the beginning and end of a string.my_string = " hello " print(my_string.strip()) # Output: hello
-
replace()
: Replaces all occurrences of a substring with another.print("Hello, World!".replace("World", "Python")) # Output: Hello, Python!
-
split()
: Splits a string into a list of substrings based on a delimiter.my_string = "apple,banana,cherry" print(my_string.split(",")) # Output: ['apple', 'banana', 'cherry']
-
join()
: Joins a list of strings into a single string with a specified separator.fruits = ['apple', 'banana', 'cherry'] print(", ".join(fruits)) # Output: apple, banana, cherry
-
startswith()
andendswith()
: Check if a string starts or ends with a specified substring.print("Python".startswith("Py")) # Output: True print("Python".endswith("on")) # Output: True
-
find()
: Returns the index of the first occurrence of a substring, or-1
if not found.print("Hello, World!".find("World")) # Output: 7
Multiline Strings
You can create multiline strings using triple quotes ('''
or """
).
my_string = """This is a
multiline string"""
print(my_string)
Escape Characters
Escape characters allow you to include special characters in strings, such as newlines or tabs.
\n
: Newline\t
: Tab\\
: Backslash
print("Hello\nWorld") # Output: Hello (newline) World
print("Name:\tJohn") # Output: Name: John
Summary:
- Strings are immutable sequences of characters in Python.
- You can use a variety of methods for string manipulation like
upper()
,lower()
,replace()
,split()
, andjoin()
. - String formatting can be done with
format()
, f-strings, or percent formatting. - Strings can be indexed, sliced, and concatenated.
Flow Control Statements in Python
Flow control statements allow you to change the execution order of statements based on conditions, loops, or exceptions. Here are the main flow control statements in Python.
if
, elif
, and else
1. The if
statement is used to execute a block of code only if a condition is True
. You can also use elif
for additional conditions, and else
to handle the case where none of the conditions are true.
Syntax:
if condition:
# Code block for True condition
elif another_condition:
# Code block for another True condition
else:
# Code block if all conditions are False
Example:
age = 18
if age < 18:
print("You are a minor.")
elif age == 18:
print("You just became an adult!")
else:
print("You are an adult.")
for
Loop
2. The for
loop is used to iterate over an iterable (e.g., list, tuple, string) and execute a block of code for each item in the iterable.
Syntax:
for variable in iterable:
# Code block
Example:
fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
print(fruit)
Output:
apple
banana
cherry
while
Loop
3. The while
loop runs as long as a condition is True
. Be careful with while
loops to avoid infinite loops.
Syntax:
while condition:
# Code block
Example:
count = 0
while count < 5:
print(count)
count += 1
Output:
0
1
2
3
4
break
Statement
4. The break
statement is used to exit a loop prematurely when a certain condition is met.
Example:
for i in range(10):
if i == 5:
break # Exit the loop
print(i)
Output:
0
1
2
3
4
count = 0
while True:
print(count)
count += 1
if count == 5:
break # Exits the while loop when count reaches 5
continue
Statement
5. The continue
statement skips the current iteration of a loop and moves to the next one.
Example:
for i in range(5):
if i == 2:
continue # Skip the rest of this loop when i == 2
print(i)
Output:
0
1
3
4
pass
Statement
6. The pass
statement is a placeholder that does nothing. It's used where a statement is syntactically required but you don't want to perform any action.
Example:
for i in range(5):
if i == 3:
pass # Do nothing for this iteration
print(i)
Output:
0
1
2
3
4
try
, except
, finally
(Exception Handling)
7. Python uses try
and except
blocks to handle exceptions (errors). You can use the finally
block to execute code regardless of whether an exception occurs or not.
Syntax:
try:
# Code that may raise an exception
except SomeError:
# Code to handle the exception
finally:
# Code that will always run
Example:
try:
number = int(input("Enter a number: "))
except ValueError:
print("That's not a valid number.")
finally:
print("This will always run.")
else
in Loops
8. The else
block in a loop runs only when the loop completes normally, without encountering a break
statement.
Example:
for i in range(5):
if i == 2:
break
else:
print("Loop completed without break")
Output:
(No output, as the loop breaks at i == 2)
match
Statement (Python 3.10+)
9. The match
statement is similar to a switch-case statement found in other languages. It checks the value of a variable against multiple patterns.
Syntax:
match variable:
case pattern_1:
# Code block
case pattern_2:
# Code block
case _:
# Default code block, will match any wildcard that does not match previous cases
Example:
def check_day(day):
match day:
case "Monday":
print("Start of the workweek.")
case "Friday":
print("Almost weekend!")
case _:
print("It's a regular day.")
check_day("Friday")
Output:
Almost weekend!
Summary of Flow Control Statements:
if
,elif
,else
: Conditional branching.for
loop: Iterates over sequences.while
loop: Loops based on a condition.break
: Exits the loop.continue
: Skips the current iteration.pass
: Does nothing (used as a placeholder).try
,except
,finally
: Handles exceptions.match
: Pattern matching (Python 3.10+).
range()
in Python
Examples of The range()
function generates a sequence of numbers. It's commonly used in loops. The syntax is:
range(start, stop, step)
start
: The starting number (optional, defaults to 0).stop
: The stopping number (required, the range stops just before this number).step
: The increment (optional, defaults to 1).
Example 1: Basic Range
If you provide just the stop
value, range()
generates numbers from 0 up to (but not including) stop
.
for i in range(5):
print(i)
Output:
0
1
2
3
4
Example 2: Range with Start and Stop
You can specify both the start
and stop
values.
for i in range(2, 6):
print(i)
Output:
2
3
4
5
Example 3: Range with a Step
The step
parameter lets you control the interval between numbers.
for i in range(0, 10, 2):
print(i)
Output:
0
2
4
6
8
Example 4: Range with Negative Step
You can use a negative step
to generate numbers in reverse order.
for i in range(10, 0, -2):
print(i)
Output:
10
8
6
4
2
list()
Example 5: Using Range with You can convert a range
object to a list using list()
.
numbers = list(range(5))
print(numbers) # Output: [0, 1, 2, 3, 4]
range()
Example 6: Looping Over Indices with You can use range()
to loop through the indices of a list.
fruits = ['apple', 'banana', 'cherry']
for i in range(len(fruits)):
print(f"Index {i}: {fruits[i]}")
Output:
Index 0: apple
Index 1: banana
Index 2: cherry
Example 7: Using Range in Reverse
You can use range()
to generate numbers in reverse order by specifying a negative step
.
for i in range(5, 0, -1):
print(i)
Output:
5
4
3
2
1
range()
:
Summary of range(stop)
: Generates numbers from 0 tostop - 1
.range(start, stop)
: Generates numbers fromstart
tostop - 1
.range(start, stop, step)
: Generates numbers fromstart
tostop - 1
, incrementing bystep
.- Negative
step
: Generates numbers in reverse order.
Exceptions in Python
Exceptions in Python are errors that occur during the execution of a program. When an exception occurs, Python stops executing the code and raises an error message unless the exception is caught and handled. Python provides various ways to handle exceptions to prevent the program from crashing.
try
, except
, and finally
1. The try
block lets you test a block of code for errors, while the except
block lets you handle the exception. The finally
block lets you execute code, regardless of whether an exception was raised or not.
Example:
try:
# Code that may raise an exception
number = int(input("Enter a number: "))
except ValueError:
# Code to handle the exception
print("That's not a valid number.")
finally:
# Code that will always run
print("End of the program.")
2. Catching Specific Exceptions
You can catch specific exceptions by specifying the exception type in the except
block.
Example:
try:
result = 10 / 0
except ZeroDivisionError:
print("You cannot divide by zero!")
Output:
You cannot divide by zero!
3. Catching Multiple Exceptions
You can catch multiple exceptions by listing them as a tuple in a single except
block, or by using multiple except
blocks.
Example 1: Catch Multiple Exceptions in One Block
try:
x = int("hello")
except (ValueError, TypeError):
print("A value or type error occurred.")
except
Blocks
Example 2: Use Multiple try:
x = int("hello")
except ValueError:
print("A ValueError occurred.")
except TypeError:
print("A TypeError occurred.")
else
with try
4. Using The else
block executes if the try
block does not raise any exceptions.
Example:
try:
number = int(input("Enter a valid number: "))
except ValueError:
print("Invalid number entered.")
else:
print(f"Your number is {number}.")
finally
Block
5. The The finally
block always executes, regardless of whether an exception was raised or not. It's useful for cleanup actions, like closing files or releasing resources.
Example:
try:
file = open("somefile.txt", "r")
except FileNotFoundError:
print("File not found.")
finally:
print("Cleaning up...")
# Code to close the file or clean resources
6. Raising Exceptions
You can manually raise exceptions in Python using the raise
keyword.
Example:
x = -5
if x < 0:
raise ValueError("x cannot be negative")
This will raise a ValueError
with the message "x cannot be negative"
.
7. Creating Custom Exceptions
You can define your own exception classes by inheriting from Python's built-in Exception
class.
Example:
class CustomError(Exception):
pass
def check_positive_number(number):
if number < 0:
raise CustomError("This is a custom error: Negative number not allowed!")
try:
check_positive_number(-10)
except CustomError as e:
print(e)
Output:
This is a custom error: Negative number not allowed!
### Catching Any Exception in Python
You can catch any exception in Python using a general `except` block with `Exception`:
```python
try:
# Code that may raise any exception
risky_operation()
except Exception as e:
print(f"An error occurred: {e}")
Best Practices
- Use specific exceptions where possible for better error handling.
Exception
catches most exceptions, including built-in ones likeValueError
,TypeError
, etc.- Use
finally
to always execute cleanup code, regardless of whether an exception occurs.
### Common Built-in Exceptions in Python
- **`ValueError`**: Raised when a built-in operation or function receives an argument of the correct type but an inappropriate value.
- **`TypeError`**: Raised when an operation or function is applied to an object of inappropriate type.
- **`KeyError`**: Raised when a dictionary key is not found.
- **`IndexError`**: Raised when a sequence index is out of range.
- **`FileNotFoundError`**: Raised when trying to open a file that does not exist.
- **`ZeroDivisionError`**: Raised when dividing by zero.
- **`AttributeError`**: Raised when an attribute reference or assignment fails.
- **`IOError`**: Raised when an input/output operation fails.
### Summary:
- **`try` and `except`**: Handle exceptions and prevent program crashes.
- **`else`**: Runs if no exceptions are raised in the `try` block.
- **`finally`**: Always runs, typically for cleanup.
- **`raise`**: Used to manually raise exceptions.
- **Custom Exceptions**: You can define custom exceptions by subclassing `Exception`.
Lists in Python
A list in Python is an ordered, mutable collection of items. Lists can store elements of any data type (integers, strings, other lists, etc.), and they allow for indexing, slicing, and various built-in methods for modification.
1. Creating Lists
You can create a list by enclosing items in square brackets []
.
Example:
my_list = [1, 2, 3, "apple", [5, 6]]
print(my_list) # Output: [1, 2, 3, 'apple', [5, 6]]
2. Accessing List Elements
Elements in a list can be accessed using their index. The index starts at 0
for the first element.
Example:
my_list = ["apple", "banana", "cherry"]
print(my_list[0]) # Output: apple
print(my_list[-1]) # Output: cherry (negative indexing accesses elements from the end)
3. Slicing Lists
You can access a subset of list elements by using slicing.
Example:
my_list = [1, 2, 3, 4, 5]
print(my_list[1:4]) # Output: [2, 3, 4]
print(my_list[:3]) # Output: [1, 2, 3]
print(my_list[::2]) # Output: [1, 3, 5] (access every second element)
4. Modifying Lists
Since lists are mutable, you can modify their contents by accessing elements directly, adding, removing, or changing elements.
Modifying Individual Elements:
my_list = [1, 2, 3]
my_list[1] = 20
print(my_list) # Output: [1, 20, 3]
Adding Elements:
append()
: Adds an element to the end of the list.insert()
: Inserts an element at a specified position.extend()
: Extends the list by adding all elements from another iterable.
my_list = [1, 2, 3]
my_list.append(4)
my_list.insert(1, "banana")
my_list.extend([5, 6])
print(my_list) # Output: [1, 'banana', 2, 3, 4, 5, 6]
Removing Elements:
pop()
: Removes and returns the element at the given index (or the last element by default).remove()
: Removes the first occurrence of a specific value.clear()
: Removes all elements from the list.
my_list = [1, 2, 3, 4]
my_list.pop(2) # Removes and returns the element at index 2
my_list.remove(1) # Removes the first occurrence of the value 1
my_list.clear() # Empties the entire list
print(my_list) # Output: []
5. List Methods
Python provides a variety of built-in methods for working with lists.
Common List Methods:
append()
: Adds an element to the end of the list.extend()
: Extends the list with elements from another iterable.insert()
: Inserts an element at the specified position.remove()
: Removes the first occurrence of the element.pop()
: Removes and returns the element at the given index.clear()
: Removes all elements from the list.index()
: Returns the index of the first matching element.count()
: Returns the number of occurrences of a specified element.sort()
: Sorts the list in ascending order.reverse()
: Reverses the elements of the list.copy()
: Returns a shallow copy of the list.
Example of Sorting and Reversing:
my_list = [3, 1, 4, 1, 5, 9]
my_list.sort() # Sorts the list in place
print(my_list) # Output: [1, 1, 3, 4, 5, 9]
my_list.reverse() # Reverses the list in place
print(my_list) # Output: [9, 5, 4, 3, 1, 1]
6. List Comprehensions
List comprehensions provide a concise way to create lists. They are often used for creating lists based on existing lists, with optional filtering and transformations.
Example:
# Create a list of squares
squares = [x ** 2 for x in range(6)]
print(squares) # Output: [0, 1, 4, 9, 16, 25]
# Create a list with a condition
even_squares = [x ** 2 for x in range(6) if x % 2 == 0]
print(even_squares) # Output: [0, 4, 16]
7. Nested Lists
Lists can contain other lists, creating nested lists. You can access nested elements using multiple indices.
Example:
nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(nested_list[1][1]) # Output: 5
8. List Iteration
You can iterate over a list using a for
loop.
Example:
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
9. Copying Lists
To make a copy of a list, you can use the copy()
method or slicing.
Example:
my_list = [1, 2, 3]
new_list = my_list.copy() # Shallow copy
new_list2 = my_list[:] # Another way to copy
print(new_list) # Output: [1, 2, 3]
10. Checking Membership
Use the in
keyword to check if an item is in the list.
Example:
fruits = ["apple", "banana", "cherry"]
print("apple" in fruits) # Output: True
print("orange" in fruits) # Output: False
11. Advanced List Operations
List Concatenation:
You can concatenate two lists using the +
operator.
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2
print(combined) # Output: [1, 2, 3, 4, 5, 6]
Repetition:
You can repeat a list multiple times using the *
operator.
my_list = [1, 2, 3]
print(my_list * 3) # Output: [1, 2, 3, 1, 2, 3, 1, 2, 3]
Summary of Lists in Python:
- Lists are mutable, ordered collections that can hold elements of different types.
- You can add, modify, and remove elements using various built-in methods.
- Lists support indexing, slicing, and iteration.
- List comprehensions provide a concise way to create and filter lists.
- Nested lists allow for multi-dimensional data structures.
Tuples in Python
A tuple in Python is an ordered, immutable collection of items. Once created, the elements of a tuple cannot be changed. Tuples are often used when you want to store a collection of items that should not be modified.
1. Creating Tuples
Tuples are created by placing values inside parentheses ()
and separating them with commas. A tuple can hold elements of different data types.
Example:
my_tuple = (1, 2, 3, "apple", [5, 6])
print(my_tuple) # Output: (1, 2, 3, 'apple', [5, 6])
Note: Parentheses are optional when defining a tuple. For example,
my_tuple = 1, 2, 3
also creates a tuple.
2. Accessing Tuple Elements
Elements in a tuple can be accessed using their index. The index starts at 0
for the first element.
Example:
my_tuple = ("apple", "banana", "cherry")
print(my_tuple[0]) # Output: apple
print(my_tuple[-1]) # Output: cherry (negative indexing accesses elements from the end)
3. Tuple Immutability
Tuples are immutable, meaning once a tuple is created, you cannot modify its elements. However, if a tuple contains a mutable element like a list, that element can still be modified.
Example:
my_tuple = (1, 2, 3)
# my_tuple[0] = 10 # Raises TypeError: 'tuple' object does not support item assignment
# Modifying a mutable element inside a tuple
my_tuple = (1, [2, 3])
my_tuple[1][0] = 99
print(my_tuple) # Output: (1, [99, 3])
4. Tuple Slicing
You can access a subset of tuple elements using slicing.
Example:
my_tuple = (1, 2, 3, 4, 5)
print(my_tuple[1:4]) # Output: (2, 3, 4)
print(my_tuple[:3]) # Output: (1, 2, 3)
print(my_tuple[::2]) # Output: (1, 3, 5) (access every second element)
5. Tuple Methods
Since tuples are immutable, they have fewer built-in methods compared to lists. However, two commonly used methods are:
count()
: Returns the number of times a specified value occurs in a tuple.index()
: Returns the index of the first occurrence of a specified value.
Example:
my_tuple = (1, 2, 3, 2, 2, 4)
print(my_tuple.count(2)) # Output: 3 (2 appears 3 times)
print(my_tuple.index(3)) # Output: 2 (the first occurrence of 3 is at index 2)
6. Tuple Packing and Unpacking
Packing is the process of creating a tuple by assigning multiple values to a single variable. Unpacking is the reverse process, where the values in a tuple are assigned to multiple variables.
Example:
# Packing
my_tuple = 1, 2, 3
# Unpacking
a, b, c = my_tuple
print(a) # Output: 1
print(b) # Output: 2
print(c) # Output: 3
You can also use the *
operator to unpack the remaining elements.
Example of Extended Unpacking:
my_tuple = (1, 2, 3, 4, 5)
a, b, *rest = my_tuple
print(a) # Output: 1
print(b) # Output: 2
print(rest) # Output: [3, 4, 5]
7. Nested Tuples
Tuples can contain other tuples, creating nested tuples. You can access nested elements using multiple indices.
Example:
nested_tuple = ((1, 2), (3, 4), (5, 6))
print(nested_tuple[1][1]) # Output: 4
8. Tuple Iteration
You can iterate over a tuple using a for
loop.
Example:
fruits = ("apple", "banana", "cherry")
for fruit in fruits:
print(fruit)
9. Concatenating Tuples
You can concatenate tuples using the +
operator.
Example:
tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
combined_tuple = tuple1 + tuple2
print(combined_tuple) # Output: (1, 2, 3, 4, 5, 6)
10. Repetition of Tuples
You can repeat the elements of a tuple multiple times using the *
operator.
Example:
my_tuple = (1, 2)
print(my_tuple * 3) # Output: (1, 2, 1, 2, 1, 2)
11. Checking Membership in Tuples
Use the in
keyword to check if an item exists in a tuple.
Example:
fruits = ("apple", "banana", "cherry")
print("apple" in fruits) # Output: True
print("orange" in fruits) # Output: False
12. Copying Tuples
Since tuples are immutable, you don't need to create deep copies like you would with lists. You can simply assign a tuple to a new variable.
Example:
tuple1 = (1, 2, 3)
tuple2 = tuple1 # Shallow copy
print(tuple2) # Output: (1, 2, 3)
13. Tuple as Dictionary Keys
Tuples can be used as keys in dictionaries because they are immutable, unlike lists.
Example:
my_dict = {(1, 2): "point1", (3, 4): "point2"}
print(my_dict[(1, 2)]) # Output: point1
14. Tuple vs. List: When to Use Each
- Use tuples when you want to store a collection of items that should not change, such as coordinates or database records.
- Use lists when you need a mutable collection, and you may need to add, remove, or modify elements over time.
Summary of Tuples in Python:
- Tuples are immutable, ordered collections of elements.
- They support indexing, slicing, and can contain multiple data types, including other tuples.
- You can use tuple packing and unpacking to assign or retrieve values easily.
- Tuples can be used as keys in dictionaries because they are immutable.
- While fewer methods are available than lists, tuples are useful for storing data that should not be changed.
Dictionaries in Python
A dictionary in Python is an unordered, mutable collection that stores data in key-value pairs. Each key in a dictionary must be unique and immutable (such as strings, numbers, or tuples), while the values can be of any data type.
1. Creating Dictionaries
Dictionaries are created using curly braces {}
or the dict()
constructor, with key-value pairs separated by colons.
Example:
my_dict = {"name": "John", "age": 30, "city": "New York"}
print(my_dict) # Output: {'name': 'John', 'age': 30, 'city': 'New York'}
# Using the dict() constructor
my_dict2 = dict(name="Alice", age=25, city="London")
print(my_dict2) # Output: {'name': 'Alice', 'age': 25, 'city': 'London'}
2. Accessing Dictionary Values
You can access the values in a dictionary by using their corresponding keys.
Example:
my_dict = {"name": "John", "age": 30, "city": "New York"}
print(my_dict["name"]) # Output: John
print(my_dict["age"]) # Output: 30
3. Adding and Modifying Key-Value Pairs
You can add or modify key-value pairs in a dictionary by assigning a value to a key.
Example:
my_dict = {"name": "John", "age": 30}
my_dict["age"] = 31 # Modifying an existing key
my_dict["city"] = "Boston" # Adding a new key-value pair
print(my_dict) # Output: {'name': 'John', 'age': 31, 'city': 'Boston'}
4. Removing Key-Value Pairs
You can remove items from a dictionary using several methods:
pop()
: Removes the item with the specified key and returns its value.del
: Deletes the item with the specified key.popitem()
: Removes and returns the last inserted key-value pair.clear()
: Removes all items from the dictionary.
Example:
my_dict = {"name": "John", "age": 30, "city": "New York"}
# Using pop()
age = my_dict.pop("age")
print(age) # Output: 30
print(my_dict) # Output: {'name': 'John', 'city': 'New York'}
# Using del
del my_dict["city"]
print(my_dict) # Output: {'name': 'John'}
# Using clear
my_dict.clear()
print(my_dict) # Output: {}
5. Dictionary Methods
Python provides several built-in methods to work with dictionaries.
Common Dictionary Methods:
keys()
: Returns a view object of all the keys in the dictionary.values()
: Returns a view object of all the values in the dictionary.items()
: Returns a view object of key-value pairs as tuples.get()
: Returns the value of a key, orNone
if the key doesn't exist.update()
: Updates the dictionary with the elements from another dictionary or iterable.
Example:
my_dict = {"name": "John", "age": 30}
# Get all keys
print(my_dict.keys()) # Output: dict_keys(['name', 'age'])
# Get all values
print(my_dict.values()) # Output: dict_values(['John', 30])
# Get key-value pairs
print(my_dict.items()) # Output: dict_items([('name', 'John'), ('age', 30)])
# Using get() method
print(my_dict.get("name")) # Output: John
print(my_dict.get("city")) # Output: None (doesn't raise an error if key doesn't exist)
# Using update()
my_dict.update({"city": "New York", "age": 31})
print(my_dict) # Output: {'name': 'John', 'age': 31, 'city': 'New York'}
6. Iterating Over Dictionaries
You can iterate through a dictionary to access its keys, values, or key-value pairs.
Example:
my_dict = {"name": "John", "age": 30, "city": "New York"}
# Iterating over keys
for key in my_dict:
print(key, my_dict[key])
# Iterating over values
for value in my_dict.values():
print(value)
# Iterating over key-value pairs
for key, value in my_dict.items():
print(key, value)
7. Dictionary Comprehensions
Similar to list comprehensions, Python supports dictionary comprehensions, allowing you to create dictionaries in a concise manner.
Example:
# Create a dictionary of squares
squares = {x: x ** 2 for x in range(1, 6)}
print(squares) # Output: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Create a dictionary with a condition
even_squares = {x: x ** 2 for x in range(1, 6) if x % 2 == 0}
print(even_squares) # Output: {2: 4, 4: 16}
8. Nested Dictionaries
Dictionaries can contain other dictionaries, creating nested dictionaries. You can access elements in a nested dictionary using multiple keys.
Example:
nested_dict = {
"person1": {"name": "John", "age": 30},
"person2": {"name": "Alice", "age": 25}
}
print(nested_dict["person1"]["name"]) # Output: John
9. Copying Dictionaries
To create a copy of a dictionary, you can use the copy()
method or the dict()
constructor.
Example:
original = {"name": "John", "age": 30}
copy1 = original.copy() # Shallow copy
copy2 = dict(original) # Another way to copy
print(copy1) # Output: {'name': 'John', 'age': 30}
10. Checking for Keys
You can use the in
keyword to check if a key exists in a dictionary.
Example:
my_dict = {"name": "John", "age": 30}
print("name" in my_dict) # Output: True
print("city" in my_dict) # Output: False
11. Merging Dictionaries
You can merge two dictionaries using the update()
method or the new |=
operator (Python 3.9+).
Example:
dict1 = {"name": "John", "age": 30}
dict2 = {"city": "New York", "age": 31}
# Using update() to merge dict2 into dict1
dict1.update(dict2)
print(dict1) # Output: {'name': 'John', 'age': 31, 'city': 'New York'}
# Using | operator (Python 3.9+)
dict1 | dict2
print(dict1) # Output: {'name': 'John', 'age': 31, 'city': 'New York'}
12. Tuples as Dictionary Keys
Since tuples are immutable, they can be used as keys in dictionaries. This is useful for representing complex keys.
Example:
coordinates = {(0, 0): "origin", (1, 2): "point1", (3, 4): "point2"}
print(coordinates[(1, 2)]) # Output: point1
Summary of Dictionaries in Python:
- Dictionaries store key-value pairs and are mutable, allowing you to modify, add, or remove elements.
- Common methods include
keys()
,values()
,items()
,get()
, andupdate()
. - You can iterate over dictionaries and use dictionary comprehensions for concise creation.
- Nested dictionaries allow for complex data structures, and tuples can be used as keys because they are immutable.
Sets in Python
A set in Python is an unordered collection of unique, immutable elements. Sets are commonly used when you want to store multiple items without duplicates. Since sets are unordered, they do not support indexing or slicing like lists or tuples.
1. Creating Sets
You can create a set by placing elements inside curly braces {}
or by using the set()
constructor.
Example:
my_set = {1, 2, 3, 4}
print(my_set) # Output: {1, 2, 3, 4}
# Using set() constructor
my_set2 = set([1, 2, 2, 3, 4]) # Duplicates are removed
print(my_set2) # Output: {1, 2, 3, 4}
Note: To create an empty set, you must use
set()
, not{}
, as{}
creates an empty dictionary.
2. Adding and Removing Elements in Sets
Sets are mutable, meaning you can add or remove elements after creating them.
Adding Elements:
add()
: Adds a single element to the set.update()
: Adds multiple elements (can accept lists, tuples, or other sets).
my_set = {1, 2, 3}
my_set.add(4) # Adds 4 to the set
my_set.update([5, 6]) # Adds multiple elements
print(my_set) # Output: {1, 2, 3, 4, 5, 6}
Removing Elements:
remove()
: Removes a specific element (raisesKeyError
if the element is not found).discard()
: Removes a specific element (does not raise an error if the element is not found).pop()
: Removes and returns an arbitrary element from the set.clear()
: Removes all elements from the set.
my_set = {1, 2, 3, 4}
my_set.remove(3) # Removes 3 from the set
my_set.discard(5) # No error if 5 is not in the set
removed_item = my_set.pop() # Removes an arbitrary element
my_set.clear() # Empties the set
print(my_set) # Output: set()
3. Set Operations
Python provides several set operations like union, intersection, difference, and symmetric difference, which are useful for combining or comparing sets.
|
or union()
):
Union (Returns a set that contains all the elements from both sets.
set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1 | set2) # Output: {1, 2, 3, 4, 5}
print(set1.union(set2)) # Output: {1, 2, 3, 4, 5}
&
or intersection()
):
Intersection (Returns a set that contains only the elements present in both sets.
set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1 & set2) # Output: {3}
print(set1.intersection(set2)) # Output: {3}
-
or difference()
):
Difference (Returns a set that contains elements that are in the first set but not in the second.
set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1 - set2) # Output: {1, 2}
print(set1.difference(set2)) # Output: {1, 2}
^
or symmetric_difference()
):
Symmetric Difference (Returns a set that contains elements that are in either set, but not in both.
set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1 ^ set2) # Output: {1, 2, 4, 5}
print(set1.symmetric_difference(set2)) # Output: {1, 2, 4, 5}
4. Set Comparisons
You can compare sets using comparison operators like <=
, >=
, and ==
.
Example:
set1 = {1, 2, 3}
set2 = {1, 2, 3, 4}
print(set1 <= set2) # Output: True (set1 is a subset of set2)
print(set2 >= set1) # Output: True (set2 is a superset of set1)
print(set1 == set2) # Output: False (sets are not equal)
5. Set Methods
Some useful set methods include:
add()
: Adds a single element to the set.update()
: Adds multiple elements to the set.remove()
: Removes a specific element from the set.discard()
: Removes a specific element without raising an error.clear()
: Removes all elements from the set.pop()
: Removes and returns an arbitrary element from the set.union()
: Returns the union of two sets.intersection()
: Returns the intersection of two sets.difference()
: Returns the difference of two sets.symmetric_difference()
: Returns the symmetric difference of two sets.
6. Frozen Sets
A frozenset is an immutable version of a set. Once a frozenset is created, its elements cannot be changed, added, or removed.
Example:
my_set = frozenset([1, 2, 3, 4])
print(my_set) # Output: frozenset({1, 2, 3, 4})
# my_set.add(5) # Raises AttributeError: 'frozenset' object has no attribute 'add'
Frozen sets are useful when you need to use sets as keys in dictionaries, or in other contexts where immutability is required.
7. Iterating Over a Set
You can iterate over the elements of a set using a for
loop.
Example:
my_set = {1, 2, 3, 4, 5}
for item in my_set:
print(item)
8. Checking Membership in Sets
Use the in
keyword to check if an element exists in a set.
Example:
my_set = {1, 2, 3, 4}
print(2 in my_set) # Output: True
print(5 in my_set) # Output: False
9. Set Comprehensions
Similar to list comprehensions, you can create sets using set comprehensions.
Example:
# Create a set of squares
squares = {x ** 2 for x in range(1, 6)}
print(squares) # Output: {1, 4, 9, 16, 25}
# Create a set with a condition
even_squares = {x ** 2 for x in range(1, 6) if x % 2 == 0}
print(even_squares) # Output: {4, 16}
10. Converting Between Lists, Tuples, and Sets
You can easily convert between lists, tuples, and sets using the list()
, tuple()
, and set()
constructors.
Example:
# Convert list to set
my_list = [1, 2, 3, 4]
my_set = set(my_list)
print(my_set) # Output: {1, 2, 3, 4}
# Convert set to list
my_set = {1, 2, 3, 4}
my_list = list(my_set)
print(my_list) # Output: [1, 2, 3, 4]
11. Performance Considerations
Sets are optimized for membership checks, making operations like checking if an item exists (in
keyword) faster than in lists or tuples. This makes sets ideal for tasks where you need to eliminate duplicates or perform frequent membership checks.
Summary of Sets in Python:
- Sets are unordered collections of unique elements, optimized for fast membership checks.
- You can perform mathematical operations like union, intersection, difference, and symmetric difference on sets.
- Frozen sets are immutable versions of sets and can be used in situations where sets need to be immutable, like as dictionary keys.
- Set comprehensions provide a concise way to create sets from iterables.
Functions in Python
A function in Python is a block of reusable code that performs a specific task. Functions help in organizing code, improving readability, and enabling reusability. Functions are defined using the def
keyword.
1. Defining Functions
To define a function, use the def
keyword followed by the function name and parentheses ()
that may contain parameters.
Syntax:
def function_name(parameters):
# Code block
return result
Example:
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # Output: Hello, Alice!
2. Calling Functions
Once defined, a function can be called by using its name followed by parentheses ()
.
Example:
def add(a, b):
return a + b
result = add(5, 3)
print(result) # Output: 8
3. Function Arguments
Functions can take arguments to make them more flexible. There are several types of function arguments in Python:
3.1 Positional Arguments
Positional arguments are passed to functions in the order in which they are defined.
def multiply(a, b):
return a * b
result = multiply(4, 5)
print(result) # Output: 20
3.2 Keyword Arguments
Keyword arguments allow you to pass arguments by specifying their names.
def greet(name, message):
print(f"{message}, {name}!")
greet(name="Alice", message="Good morning") # Output: Good morning, Alice!
3.3 Default Arguments
You can provide default values for arguments. If the argument is not provided, the default value is used.
def greet(name, message="Hello"):
print(f"{message}, {name}!")
greet("Bob") # Output: Hello, Bob!
greet("Alice", "Hi") # Output: Hi, Alice!
3.4 Variable-Length Arguments
Python allows you to define functions with a variable number of arguments using *args
for positional arguments and **kwargs
for keyword arguments.
*args
: Allows the function to accept any number of positional arguments.**kwargs
: Allows the function to accept any number of keyword arguments.
# Using *args for positional arguments
def sum_all(*args):
return sum(args)
print(sum_all(1, 2, 3)) # Output: 6
# Using **kwargs for keyword arguments
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25) # Output: name: Alice, age: 25
4. Return Values
A function can return a value using the return
statement. If no return
statement is used, the function returns None
.
Example:
def square(number):
return number * number
result = square(4)
print(result) # Output: 16
5. Lambda Functions (Anonymous Functions)
Lambda functions are small, anonymous functions that can take any number of arguments but can only have one expression. They are often used for short, simple functions.
Syntax:
lambda arguments: expression
Example:
# A lambda function to add two numbers
add = lambda x, y: x + y
print(add(5, 3)) # Output: 8
# Using lambda with map() function
numbers = [1, 2, 3, 4]
squares = list(map(lambda x: x ** 2, numbers))
print(squares) # Output: [1, 4, 9, 16]
6. Docstrings
A docstring is a string literal that appears right after the function definition. It is used to document the function. You can access the docstring using the __doc__
attribute.
Example:
def multiply(a, b):
"""This function multiplies two numbers."""
return a * b
print(multiply.__doc__) # Output: This function multiplies two numbers.
7. Nested Functions
You can define functions inside other functions. These are called nested functions or inner functions.
Example:
def outer_function(text):
def inner_function():
print(text)
inner_function()
outer_function("Hello from the outer function!") # Output: Hello from the outer function!
8. Closures
A closure is a function object that remembers values in enclosing scopes, even if those scopes are no longer present. Closures are created when a nested function references a value from its outer function.
Example:
def outer_function(text):
def inner_function():
print(text)
return inner_function
my_func = outer_function("Hello!")
my_func() # Output: Hello!
9. Decorators
A decorator is a function that takes another function as an argument and extends or modifies its behavior without explicitly modifying the function itself. Decorators are often used for logging, enforcing access control, or measuring execution time.
Example:
def decorator_function(original_function):
def wrapper_function():
print("Wrapper executed this before {}".format(original_function.__name__))
original_function()
return wrapper_function
@decorator_function
def say_hello():
print("Hello!")
say_hello()
Output:
Wrapper executed this before say_hello
Hello!
10. Recursion
A function can call itself, known as recursion. It is commonly used for problems that can be broken down into smaller, similar problems (like factorial or Fibonacci calculations).
Example:
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n - 1)
print(factorial(5)) # Output: 120
11. Higher-Order Functions
A higher-order function is a function that takes another function as an argument or returns a function as a result. Common higher-order functions include map()
, filter()
, and reduce()
.
Example:
# Using map() as a higher-order function
numbers = [1, 2, 3, 4]
squares = list(map(lambda x: x ** 2, numbers))
print(squares) # Output: [1, 4, 9, 16]
# Using filter() as a higher-order function
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # Output: [2, 4]
*args
and **kwargs
in Detail
12. Example:
*args
allows a function to take a variable number of positional arguments:
def sum_all(*args):
return sum(args)
print(sum_all(1, 2, 3)) # Output: 6
**kwargs
allows a function to take a variable number of keyword arguments:
def print_details(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_details(name="Alice", age=25) # Output: name: Alice, age: 25
Summary of Functions in Python:
- Functions allow code reuse and help in organizing your programs.
- Functions can take positional arguments, keyword arguments, default arguments, and variable-length arguments.
- Functions can return values using the
return
statement, and can also have docstrings for documentation. - Lambda functions are anonymous, one-line functions, and decorators extend or modify the behavior of existing functions.
- Python supports recursion and higher-order functions like
map()
,filter()
, andreduce()
.
Modules in Python
A module in Python is a file containing Python code (functions, classes, variables) that can be imported and used in other Python programs. Modules help in organizing code into reusable blocks, improving the modularity and maintainability of large projects.
1. Creating a Module
A Python module is simply a .py
file containing Python code. You can create your own module by writing functions, classes, or variables in a Python file.
Example:
Create a file called mymodule.py
:
# mymodule.py
def greet(name):
return f"Hello, {name}!"
PI = 3.14159
Now, this mymodule.py
can be imported into another Python script.
2. Importing Modules
To use a module in your program, you can import it using the import
statement. You can import an entire module, specific functions, or use an alias.
Example: Importing the Entire Module
# main.py
import mymodule
print(mymodule.greet("Alice")) # Output: Hello, Alice!
print(mymodule.PI) # Output: 3.14159
Example: Importing Specific Functions or Variables
You can import only specific parts of a module using the from
keyword.
# main.py
from mymodule import greet, PI
print(greet("Bob")) # Output: Hello, Bob!
print(PI) # Output: 3.14159
Example: Using Aliases for Modules
You can use the as
keyword to give a module or function an alias for easier access.
# main.py
import mymodule as mm
print(mm.greet("Charlie")) # Output: Hello, Charlie!
print(mm.PI) # Output: 3.14159
3. Built-in Modules
Python comes with a large collection of built-in modules, like math
, os
, random
, sys
, and more. You can use these modules by importing them in your code.
math
Module
Example: Using the import math
print(math.sqrt(16)) # Output: 4.0
print(math.pi) # Output: 3.141592653589793
random
Module
Example: Using the import random
print(random.randint(1, 10)) # Output: A random integer between 1 and 10
dir()
Function
4. The The dir()
function can be used to list all the names (functions, variables, etc.) defined in a module. This is useful for exploring the contents of both built-in and custom modules.
Example:
import math
print(dir(math))
# Output: List of all functions and variables in the math module
__name__
Variable
5. The Every Python module has a special built-in variable called __name__
. When a module is run directly, __name__
is set to "__main__"
, but when it is imported, __name__
is set to the module's name. This is commonly used to control the execution of code only when a module is run directly.
Example:
In mymodule.py
:
# mymodule.py
def greet(name):
return f"Hello, {name}!"
if __name__ == "__main__":
print("This is executed when the module is run directly.")
If you run mymodule.py
directly, the print statement will execute. If you import mymodule
in another file, the print statement won't execute.
$ python mymodule.py
This is executed when the module is run directly.
6. Packages
A package is a collection of modules grouped in a directory. It allows for better organization of large projects. A package contains an __init__.py
file, which can be empty or used to initialize the package.
Example: Package Structure
mypackage/ __init__.py module1.py module2.py
In module1.py
:
def func1():
print("Function 1 from module 1")
In module2.py
:
def func2():
print("Function 2 from module 2")
Now you can import the modules from the package.
Example:
from mypackage import module1, module2
module1.func1() # Output: Function 1 from module 1
module2.func2() # Output: Function 2 from module 2
7. Importing from a Package
You can use the from
keyword to import specific functions or classes from a module inside a package.
Example:
from mypackage.module1 import func1
func1() # Output: Function 1 from module 1
8. Relative Imports
Within a package, you can use relative imports to import modules relative to the current module's location.
Example:
# Inside mypackage/module2.py
from .module1 import func1
func1() # Calls func1 from module1
pip
9. Installing External Modules with Python has a vast ecosystem of third-party modules available for installation via pip
, the package installer for Python. You can install external modules from the Python Package Index (PyPI) using the command:
pip install <package_name>
Example:
pip install requests
After installing, you can import and use the requests
module:
import requests
response = requests.get("https://www.example.com")
print(response.status_code) # Output: 200
10. Re-importing Modules
If you modify a module after importing it, Python will not automatically re-import it unless you restart the interpreter. To re-import a module without restarting, you can use importlib.reload()
.
Example:
import mymodule
import importlib
# Modify mymodule.py here
importlib.reload(mymodule) # Reloads the updated module
11. Standard Libraries and Popular Modules
Python includes a large collection of standard libraries, such as:
os
: For interacting with the operating system.sys
: For interacting with the Python runtime environment.datetime
: For working with dates and times.json
: For working with JSON data.re
: For regular expressions.
os
Module
Example: Using the import os
print(os.getcwd()) # Output: Current working directory
os.mkdir("new_folder") # Creates a new directory
Summary of Modules in Python:
- Modules allow code to be organized into reusable blocks. A module is just a Python file, and it can contain functions, classes, or variables.
- You can import entire modules or specific components using
import
andfrom ... import
. - Packages are directories containing multiple modules, and they allow for better project organization.
- Use relative imports within packages to reference sibling or parent modules.
- Python has a vast collection of built-in modules, and third-party modules can be installed using
pip
. - The
__name__
variable allows you to control whether code runs when a module is imported or executed directly.
Object-Oriented Programming in Python
Python supports object-oriented programming (OOP), a paradigm where everything is represented as objects. OOP focuses on organizing code around objects, which contain both data (attributes) and behavior (methods). Key concepts of OOP include classes, objects, inheritance, encapsulation, polymorphism, and abstraction.
1. Classes and Objects
A class is a blueprint for creating objects. An object is an instance of a class. Classes define properties and behaviors (attributes and methods) that the objects will have.
Example: Defining a Class
class Dog:
def __init__(self, name, breed):
self.name = name # Attribute
self.breed = breed # Attribute
def bark(self):
print(f"{self.name} is barking!")
# Creating an object of the Dog class
my_dog = Dog("Buddy", "Golden Retriever")
print(my_dog.name) # Output: Buddy
my_dog.bark() # Output: Buddy is barking!
__init__()
is a special method (constructor) used to initialize the attributes of a class.self
refers to the current instance of the class and is required in method definitions.
2. Attributes and Methods
- Attributes are variables that belong to an object or class. They represent the state or properties of an object.
- Methods are functions that belong to an object or class. They define the behavior of the object.
Example:
class Car:
def __init__(self, make, model):
self.make = make # Attribute
self.model = model # Attribute
def start_engine(self):
print(f"The engine of {self.make} {self.model} has started.")
# Creating an object
my_car = Car("Toyota", "Corolla")
my_car.start_engine() # Output: The engine of Toyota Corolla has started.
3. Encapsulation
Encapsulation is the principle of hiding the internal details of an object and providing controlled access through methods. In Python, this is achieved using private attributes (denoted by a leading underscore _
or double underscore __
).
Example:
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.__balance = balance # Private attribute
def deposit(self, amount):
self.__balance += amount
def withdraw(self, amount):
if amount <= self.__balance:
self.__balance -= amount
else:
print("Insufficient funds")
def get_balance(self):
return self.__balance
# Creating an object
account = BankAccount("Alice", 1000)
account.deposit(500)
print(account.get_balance()) # Output: 1500
account.__balance = 0 # This won't change the balance, as it's private
print(account.get_balance()) # Output: 1500
4. Inheritance
Inheritance allows one class (child class) to inherit attributes and methods from another class (parent class). This promotes code reuse and hierarchical class relationships.
Example:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} makes a sound.")
class Dog(Animal): # Dog inherits from Animal
def speak(self):
print(f"{self.name} barks.")
class Cat(Animal): # Cat inherits from Animal
def speak(self):
print(f"{self.name} meows.")
# Creating objects
dog = Dog("Buddy")
cat = Cat("Whiskers")
dog.speak() # Output: Buddy barks.
cat.speak() # Output: Whiskers meows.
5. Polymorphism
Polymorphism allows objects of different classes to be treated as objects of a common parent class. It also enables method overriding, where child classes can modify methods inherited from the parent class.
Example:
class Shape:
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.1416 * self.radius ** 2
# Using polymorphism
shapes = [Rectangle(3, 4), Circle(5)]
for shape in shapes:
print(shape.area())
Output:
12
78.54
6. Abstraction
Abstraction is the concept of hiding complex implementation details and exposing only the necessary parts. In Python, abstraction is often implemented using abstract base classes (ABCs).
abc
Module:
Example Using from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
# Cannot instantiate abstract class directly
# shape = Shape() # Raises TypeError
# Can instantiate child class
rect = Rectangle(5, 6)
print(rect.area()) # Output: 30
7. Class and Static Methods
- Class methods are methods that are bound to the class and not the instance. They are defined using the
@classmethod
decorator and takecls
as their first parameter instead ofself
. - Static methods are methods that do not depend on the class or instance and are defined using the
@staticmethod
decorator.
Example:
class MathOperations:
@staticmethod
def add(a, b):
return a + b
@classmethod
def description(cls):
return f"This class contains basic math operations for the {cls.__name__}."
# Using static method
print(MathOperations.add(5, 3)) # Output: 8
# Using class method
print(MathOperations.description()) # Output: This class contains basic math operations for the MathOperations.
8. Multiple Inheritance
Python supports multiple inheritance, where a class can inherit from more than one parent class. This allows for more flexible code reuse but can also introduce complexity, such as the diamond problem, which Python resolves using the Method Resolution Order (MRO).
Example:
class A:
def action(self):
print("Action from class A")
class B:
def action(self):
print("Action from class B")
class C(A, B): # Inherits from both A and B
pass
obj = C()
obj.action() # Output: Action from class A (due to MRO)
9. Dunder (Magic) Methods
Dunder (Double Underscore) Methods are special methods in Python that allow you to define how objects behave with built-in operations, like +
, -
, comparison operators, and string representations.
Example:
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return f"'{self.title}' by {self.author}"
def __add__(self, other):
return f"{self.title} and {other.title}"
book1 = Book("1984", "George Orwell")
book2 = Book("Brave New World", "Aldous Huxley")
print(book1) # Output: '1984' by George Orwell
print(book1 + book2) # Output: 1984 and Brave New World
10. Object Comparison and Hashing
You can control object comparison by implementing methods like __eq__()
, __lt__()
, etc. You can also control how objects are used in hash-based collections like dictionaries by implementing __hash__()
.
Example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.age == other.age
def __hash__(self):
return hash((self.name, self.age))
person1 = Person("Alice", 30)
person2 = Person("Bob", 30)
# Object comparison
print(person1 == person2) # Output: True (compares age)
# Using objects as dictionary keys
people_dict = {person1: "Engineer", person2: "Doctor"}
print(people_dict[person1]) # Output: Engineer
Summary of Object-Oriented Programming in Python:
- Classes are blueprints for creating objects, and objects are instances of classes.
- Encapsulation hides internal details of an object, exposing only what is necessary through methods.
- Inheritance allows one class to inherit attributes and methods from another, promoting code reuse.
- Polymorphism enables using a single interface
File I/O in Python
File input/output (I/O) in Python allows you to work with files—reading from and writing to them. Python provides built-in functions and methods for interacting with files, such as open()
, read()
, write()
, and others.
1. Opening Files
The open()
function is used to open a file in Python. It takes two main arguments:
- File path: The path to the file you want to open.
- Mode: The mode in which to open the file (e.g., reading, writing, appending).
Syntax:
file = open("filename", "mode")
Common File Modes:
'r'
: Read (default mode) – Opens a file for reading.'w'
: Write – Opens a file for writing (overwrites if the file exists, creates a new file if it doesn’t).'a'
: Append – Opens a file for appending (adds data to the end of the file if it exists).'x'
: Create – Creates a new file, raises an error if the file already exists.'b'
: Binary mode – Used for reading/writing binary files (e.g., images, executables).'t'
: Text mode (default) – Used for reading/writing text files.
Example:
# Open a file for reading
file = open("example.txt", "r")
# Open a file for writing
file = open("output.txt", "w")
# Open a binary file for reading
file = open("image.jpg", "rb")
2. Reading from Files
You can read the contents of a file using methods like read()
, readline()
, and readlines()
.
Reading the Entire File:
read()
reads the entire content of the file as a string.
file = open("example.txt", "r")
content = file.read()
print(content)
file.close() # Always close the file after use
Reading Line by Line:
readline()
reads one line at a time.readlines()
reads all lines and returns them as a list.
file = open("example.txt", "r")
# Reading one line
line = file.readline()
print(line)
# Reading all lines into a list
lines = file.readlines()
print(lines)
file.close()
3. Writing to Files
To write data to a file, use the write()
or writelines()
method. If the file is opened in write mode ('w'
), the existing contents are overwritten. If opened in append mode ('a'
), the new data is appended to the file.
Example:
# Writing a single string to a file
file = open("output.txt", "w")
file.write("Hello, World!")
file.close()
# Writing multiple lines to a file
lines = ["First line\n", "Second line\n", "Third line\n"]
file = open("output.txt", "w")
file.writelines(lines)
file.close()
with
for File Handling
4. Using It is good practice to use the with
statement when working with files. It automatically closes the file once the block is exited, even if exceptions are raised. This reduces the chances of leaving a file open unintentionally.
Example:
with open("example.txt", "r") as file:
content = file.read()
print(content)
# No need to explicitly close the file
5. Appending to Files
If you want to add data to the end of an existing file without overwriting it, open the file in append mode ('a'
).
Example:
with open("output.txt", "a") as file:
file.write("This is appended text.\n")
6. Binary File I/O
Binary mode ('b'
) is used for reading and writing binary files (such as images, audio files, and executables). The data is read or written in the form of bytes, not strings.
Example: Reading a Binary File
with open("image.jpg", "rb") as file:
binary_data = file.read()
print(binary_data)
Example: Writing a Binary File
with open("output.bin", "wb") as file:
file.write(b"\x00\xFF\x00\xFF") # Writing raw bytes
seek()
7. File Positions and When reading or writing to a file, the file pointer moves. You can move the pointer to a specific position using seek()
and find out the current position using tell()
.
Example:
file = open("example.txt", "r")
print(file.tell()) # Output: 0 (Initial position)
content = file.read(5) # Read the first 5 characters
print(content)
file.seek(0) # Move the file pointer back to the beginning
print(file.read(5)) # Read the first 5 characters again
file.close()
8. File Handling Exceptions
When working with files, exceptions such as FileNotFoundError
or PermissionError
may occur. It's important to handle these exceptions to prevent crashes.
Example:
try:
with open("nonexistent.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("The file was not found.")
except PermissionError:
print("Permission denied.")
os
and shutil
9. Working with Directories using The os
and shutil
modules provide functions for interacting with the file system, such as creating directories, renaming, and deleting files.
Example: Checking if a File Exists
import os
if os.path.exists("example.txt"):
print("The file exists.")
else:
print("The file does not exist.")
Example: Renaming and Deleting Files
import os
# Renaming a file
os.rename("output.txt", "renamed_output.txt")
# Deleting a file
os.remove("renamed_output.txt")
Example: Working with Directories
import os
# Creating a directory
os.mkdir("new_directory")
# Changing the current working directory
os.chdir("new_directory")
# Listing files in the current directory
print(os.listdir("."))
# Removing a directory
os.rmdir("new_directory")
shutil
10. Copying and Moving Files using The shutil
module provides higher-level file operations, such as copying and moving files.
Example:
import shutil
# Copying a file
shutil.copy("example.txt", "copy_of_example.txt")
# Moving a file
shutil.move("copy_of_example.txt", "moved_example.txt")
Summary of File I/O in Python:
- File Modes: Use
'r'
for reading,'w'
for writing,'a'
for appending, and'b'
for binary mode. - Reading: Use
read()
,readline()
, orreadlines()
to read content from a file. - Writing: Use
write()
orwritelines()
to write content to a file. with
Statement: Automatically handles file closing after the block of code is executed.- Binary Files: Use
'b'
mode for working with binary files. - Error Handling: Use
try-except
blocks to handle file-related exceptions likeFileNotFoundError
. - File Operations: Use the
os
andshutil
modules for file system operations like renaming, copying, and deleting files.
Making HTTP Calls in Python
Python provides several libraries for making HTTP requests. One of the most popular and easy-to-use libraries is requests
. It simplifies the process of making HTTP calls, handling response data, and managing headers and authentication.
requests
Library
1. Installing the To get started with HTTP requests, you need to install the requests
library if you haven't already. You can do this using pip
.
pip install requests
2. Making a GET Request
The GET
method is used to retrieve information from a server. The requests.get()
method sends a GET
request to the specified URL and returns a Response
object.
Example:
import requests
response = requests.get("https://jsonplaceholder.typicode.com/posts")
print(response.status_code) # Output: 200
print(response.text) # Response content in plain text
print(response.json()) # Parse response content as JSON
response.status_code
: The HTTP status code (e.g., 200 for success, 404 for not found).response.text
: The response content as a string.response.json()
: Parse the response as JSON (if the response content is in JSON format).
3. Query Parameters in GET Requests
You can pass query parameters in a GET request using the params
argument.
Example:
url = "https://jsonplaceholder.typicode.com/posts"
params = {"userId": 1}
response = requests.get(url, params=params)
print(response.url) # Output: The complete URL with query parameters
print(response.json()) # JSON response filtered by query parameters
4. Making a POST Request
The POST
method is used to send data to a server, often for creating or updating resources. You can send data in the form of JSON, form-encoded data, or files.
Example: Sending JSON Data
url = "https://jsonplaceholder.typicode.com/posts"
data = {
"title": "foo",
"body": "bar",
"userId": 1
}
response = requests.post(url, json=data)
print(response.status_code) # Output: 201 (Created)
print(response.json()) # Output: JSON response from server
json=data
: Sends the data as JSON in the body of the request.
5. Sending Form Data in a POST Request
You can send form data in a POST request using the data
argument.
Example:
url = "https://httpbin.org/post"
data = {"name": "John", "age": 30}
response = requests.post(url, data=data)
print(response.status_code) # Output: 200
print(response.text) # Output: The form data received by the server
6. Sending Files in a POST Request
You can upload files using the files
argument.
Example:
url = "https://httpbin.org/post"
files = {"file": open("example.txt", "rb")}
response = requests.post(url, files=files)
print(response.status_code) # Output: 200
print(response.json()) # Output: Server response after file upload
7. Making PUT, DELETE, and PATCH Requests
Other HTTP methods, such as PUT, DELETE, and PATCH, can be used for updating or deleting resources.
Example: PUT Request (Update)
url = "https://jsonplaceholder.typicode.com/posts/1"
data = {
"id": 1,
"title": "foo updated",
"body": "bar updated",
"userId": 1
}
response = requests.put(url, json=data)
print(response.status_code) # Output: 200 (OK)
print(response.json()) # Output: Updated resource
Example: DELETE Request
url = "https://jsonplaceholder.typicode.com/posts/1"
response = requests.delete(url)
print(response.status_code) # Output: 200 (OK)
8. Handling Headers
You can customize HTTP headers in your request using the headers
argument. This is useful for sending authentication tokens, content types, and more.
Example:
url = "https://jsonplaceholder.typicode.com/posts"
headers = {"Authorization": "Bearer your_token"}
response = requests.get(url, headers=headers)
print(response.status_code) # Output: 200 (if authorized)
9. Handling Timeouts
To avoid waiting indefinitely for a response, you can set a timeout using the timeout
parameter. The request will raise a Timeout
exception if the server takes longer than the specified time to respond.
Example:
url = "https://jsonplaceholder.typicode.com/posts"
try:
response = requests.get(url, timeout=5) # Timeout after 5 seconds
print(response.status_code)
except requests.Timeout:
print("The request timed out")
10. Handling Errors and Exceptions
The requests
library automatically raises exceptions for HTTP errors. You can handle these exceptions using a try-except
block.
Common Exceptions:
requests.Timeout
: Raised when a request times out.requests.ConnectionError
: Raised when there is a network problem.requests.HTTPError
: Raised for invalid HTTP responses (like 4xx or 5xx).
Example:
url = "https://jsonplaceholder.typicode.com/posts/invalid"
try:
response = requests.get(url)
response.raise_for_status() # Raises HTTPError for bad responses
except requests.HTTPError as err:
print(f"HTTP error occurred: {err}")
except requests.ConnectionError:
print("Network connection error")
except requests.Timeout:
print("Request timed out")
except requests.RequestException as err:
print(f"An error occurred: {err}")
11. Session Objects
You can create a session object using requests.Session()
to persist certain parameters (e.g., headers, cookies) across multiple requests. This can improve performance and help maintain state (like keeping a user logged in).
Example:
session = requests.Session()
session.headers.update({"Authorization": "Bearer your_token"})
# Subsequent requests will use the session
response = session.get("https://jsonplaceholder.typicode.com/posts")
print(response.status_code)
session.close() # Always close the session when done
12. Working with Cookies
You can retrieve and send cookies using the cookies
attribute in the requests
library.
Example: Sending and Retrieving Cookies
url = "https://httpbin.org/cookies"
# Sending cookies
cookies = {"session_id": "123456"}
response = requests.get(url, cookies=cookies)
print(response.json()) # Output: Cookies sent to the server
# Retrieving cookies from the response
response_cookies = response.cookies
print(response_cookies)
13. Redirects
By default, the requests
library follows HTTP redirects. You can control this behavior by passing allow_redirects=False
.
Example:
url = "http://httpbin.org/redirect/1"
# Follow redirects
response = requests.get(url)
print(response.url) # Output: Final URL after the redirect
# Disable redirect following
response = requests.get(url, allow_redirects=False)
print(response.status_code) # Output: 302 (Redirect status)
14. SSL Verification
The requests
library verifies SSL certificates by default. You can disable SSL verification using the verify=False
parameter, though it's generally not recommended.
Example:
url = "https://example.com"
response = requests.get(url, verify=False) # Disable SSL verification
print(response.status_code)
Note: Disabling SSL verification can make the connection insecure.
Summary of HTTP Requests in Python:
- GET: Use
requests.get()
to retrieve data from a server. - POST: Use
requests.post()
to send data to a server (JSON, form data, files). - PUT, DELETE: Use
requests.put()
andrequests.delete()
to update or delete resources. - Headers and cookies: Customize requests by adding headers and cookies.
- Error handling: Handle common exceptions like
Timeout
,HTTPError
, andConnectionError
. - Sessions: Use
requests.Session()
for persistent settings across multiple requests.
Working with Databases in Python
Python provides several libraries for working with databases, including support for relational databases like SQLite, PostgreSQL, and MySQL. You can use the sqlite3
module for lightweight local databases, or psycopg2
for PostgreSQL and mysql-connector-python
for MySQL.
1. Working with SQLite
SQLite is a lightweight, serverless database that is built into Python via the sqlite3
module. It is ideal for small to medium-sized applications or development environments.
1.1 Connecting to SQLite Database
You can connect to an SQLite database using sqlite3.connect()
. If the database file does not exist, it will be created.
Example:
import sqlite3
# Connect to SQLite database (or create it if it doesn't exist)
conn = sqlite3.connect("example.db")
cursor = conn.cursor()
# Create a table
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER NOT NULL
)
""")
# Commit changes and close connection
conn.commit()
conn.close()
1.2 Inserting Data into SQLite
To insert data into a table, you use the INSERT INTO
SQL command with the execute()
method.
Example:
conn = sqlite3.connect("example.db")
cursor = conn.cursor()
# Insert data into the table
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Alice", 30))
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Bob", 25))
# Commit changes and close connection
conn.commit()
conn.close()
1.3 Fetching Data from SQLite
You can fetch data from the database using the SELECT
SQL command and the fetchall()
or fetchone()
methods.
Example:
conn = sqlite3.connect("example.db")
cursor = conn.cursor()
# Fetch all data from the users table
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
for row in rows:
print(row)
conn.close()
1.4 Updating and Deleting Data in SQLite
You can update or delete records using the UPDATE
and DELETE
SQL commands.
Example: Updating Data
conn = sqlite3.connect("example.db")
cursor = conn.cursor()
# Update user data
cursor.execute("UPDATE users SET age = ? WHERE name = ?", (32, "Alice"))
# Commit changes and close connection
conn.commit()
conn.close()
Example: Deleting Data
conn = sqlite3.connect("example.db")
cursor = conn.cursor()
# Delete user data
cursor.execute("DELETE FROM users WHERE name = ?", ("Bob",))
# Commit changes and close connection
conn.commit()
conn.close()
2. Working with PostgreSQL
For working with PostgreSQL, you can use the psycopg2
library, which is the most popular PostgreSQL adapter for Python.
psycopg2
2.1 Installing To install psycopg2
, run:
pip install psycopg2
2.2 Connecting to PostgreSQL
You can connect to a PostgreSQL database using psycopg2.connect()
by providing database credentials.
Example:
import psycopg2
# Connect to PostgreSQL database
conn = psycopg2.connect(
dbname="example_db",
user="your_username",
password="your_password",
host="localhost",
port="5432"
)
cursor = conn.cursor()
# Create a table
cursor.execute("""
CREATE TABLE IF NOT EXISTS employees (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
position VARCHAR(100),
salary INTEGER
)
""")
# Commit changes and close connection
conn.commit()
conn.close()
2.3 Inserting Data into PostgreSQL
To insert data into a PostgreSQL table, use the INSERT INTO
command with placeholders %s
.
Example:
conn = psycopg2.connect(dbname="example_db", user="your_username", password="your_password")
cursor = conn.cursor()
# Insert data into the employees table
cursor.execute("INSERT INTO employees (name, position, salary) VALUES (%s, %s, %s)", ("John", "Manager", 60000))
cursor.execute("INSERT INTO employees (name, position, salary) VALUES (%s, %s, %s)", ("Alice", "Developer", 55000))
# Commit changes and close connection
conn.commit()
conn.close()
2.4 Fetching Data from PostgreSQL
You can use SELECT
to fetch data and fetchall()
or fetchone()
to retrieve results.
Example:
conn = psycopg2.connect(dbname="example_db", user="your_username", password="your_password")
cursor = conn.cursor()
# Fetch all data from the employees table
cursor.execute("SELECT * FROM employees")
rows = cursor.fetchall()
for row in rows:
print(row)
conn.close()
3. Working with MySQL
For working with MySQL databases in Python, you can use the mysql-connector-python
library.
3.1 Installing MySQL Connector
To install the MySQL connector, run:
pip install mysql-connector-python
3.2 Connecting to MySQL
You can connect to a MySQL database using mysql.connector.connect()
by providing the database credentials.
Example:
import mysql.connector
# Connect to MySQL database
conn = mysql.connector.connect(
host="localhost",
user="your_username",
password="your_password",
database="example_db"
)
cursor = conn.cursor()
# Create a table
cursor.execute("""
CREATE TABLE IF NOT EXISTS customers (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
address VARCHAR(255)
)
""")
# Commit changes and close connection
conn.commit()
conn.close()
3.3 Inserting Data into MySQL
You can insert data into MySQL using the INSERT INTO
command with placeholders %s
.
Example:
conn = mysql.connector.connect(host="localhost", user="your_username", password="your_password", database="example_db")
cursor = conn.cursor()
# Insert data into the customers table
cursor.execute("INSERT INTO customers (name, address) VALUES (%s, %s)", ("John Doe", "123 Main St"))
cursor.execute("INSERT INTO customers (name, address) VALUES (%s, %s)", ("Jane Smith", "456 Maple Ave"))
# Commit changes and close connection
conn.commit()
conn.close()
3.4 Fetching Data from MySQL
You can retrieve data from MySQL using the SELECT
command and fetchall()
or fetchone()
methods.
Example:
conn = mysql.connector.connect(host="localhost", user="your_username", password="your_password", database="example_db")
cursor = conn.cursor()
# Fetch all data from the customers table
cursor.execute("SELECT * FROM customers")
rows = cursor.fetchall()
for row in rows:
print(row)
conn.close()
4. Parameter Binding to Prevent SQL Injection
When inserting data into a database, you should use parameterized queries to prevent SQL injection attacks. Most database connectors support this using placeholders (?
for SQLite, %s
for PostgreSQL and MySQL).
Example for PostgreSQL:
conn = psycopg2.connect(dbname="example_db", user="your_username", password="your_password")
cursor = conn.cursor()
# Safe parameterized query
cursor.execute("INSERT INTO employees (name, position, salary) VALUES (%s, %s, %s)", ("Alice", "Developer", 50000))
conn.commit()
conn.close()
5. Transactions in Databases
Most databases support transactions, which allow you to group multiple SQL statements into a single, atomic operation. In Python, this is done by calling conn.commit()
after executing the SQL statements.
Example:
conn = psycopg2.connect(dbname="example_db", user="your_username", password="your_password")
cursor = conn.cursor()
try:
# Start a transaction
cursor.execute("UPDATE employees SET salary = salary + 5000 WHERE name = %s", ("John",))
cursor.execute("INSERT INTO employees (name, position, salary) VALUES (%s, %s, %s)", ("Diana", "Tester", 45000))
# Commit the transaction
conn.commit()
except:
# Rollback the transaction in case of an error
conn.rollback()
conn.close()
Summary of Database Operations in Python:
- SQLite: Use the built-in
sqlite3
module for lightweight, serverless databases. - PostgreSQL: Use the
psycopg2
library for working with PostgreSQL databases. - MySQL: Use the
mysql-connector-python
library for working with MySQL databases. - CRUD operations: You can perform create, read, update, and **delete
Generators in Python
Generators in Python are special types of iterators that allow you to iterate through a sequence of values without storing the entire sequence in memory at once. This makes them memory efficient, especially when working with large datasets. A generator yields values one at a time using the yield
keyword.
1. Creating Generators
Generators are created using functions and the yield
keyword. When a generator function is called, it doesn’t execute its code immediately but returns a generator object. The function's code runs only when you iterate over the generator or explicitly request the next value using next()
.
Example: Basic Generator
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator() # Create generator object
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3
# Raises StopIteration when no more items are available
2. Generator Expressions
Generator expressions are similar to list comprehensions, but they return a generator object instead of a list. They use parentheses ()
instead of square brackets []
.
Example:
# Generator expression to create a sequence of squares
gen = (x ** 2 for x in range(5))
for value in gen:
print(value)
# Output: 0 1 4 9 16
3. Advantages of Generators
- Memory Efficiency: Generators yield items one at a time, making them memory-efficient because they don’t require loading all values into memory at once.
- Lazy Evaluation: Values are generated only when requested, making generators faster for larger datasets or infinite sequences.
- Simplified Iteration: Generators provide a simple way to implement custom iterators without needing to write classes and maintain state.
yield
in Generators
4. Using The yield
statement pauses the function, saving all its state, and then resumes from where it left off when next()
is called again. This allows generators to produce a sequence of values lazily.
yield
Example: Using def countdown(n):
while n > 0:
yield n
n -= 1
gen = countdown(5)
print(next(gen)) # Output: 5
print(next(gen)) # Output: 4
print(next(gen)) # Output: 3
# Continue until the generator is exhausted
5. Infinite Generators
Generators can be used to produce infinite sequences. Be careful with infinite generators as they can run indefinitely unless properly handled.
Example: Infinite Generator
def infinite_count(start=0):
while True:
yield start
start += 1
gen = infinite_count()
# Fetch first 5 values
for _ in range(5):
print(next(gen))
# Output: 0 1 2 3 4
send()
, close()
, and throw()
6. Generator Methods: Generators also support methods like send()
, close()
, and throw()
for more advanced control.
send(value)
: Sends a value to the generator, which can be received using theyield
expression.close()
: Closes the generator, raising aGeneratorExit
exception inside the generator function.throw(type)
: Throws an exception inside the generator at the point of theyield
.
send()
Example: Using def accumulator():
total = 0
while True:
value = yield total
if value is None:
break
total += value
gen = accumulator()
print(next(gen)) # Initialize and output: 0
print(gen.send(10)) # Output: 10
print(gen.send(20)) # Output: 30
gen.close()
7. Chaining Generators
You can chain multiple generators together to create more complex data pipelines. This is useful when you want to process data in multiple steps without loading everything into memory.
Example:
def generator1():
for i in range(3):
yield i
def generator2():
for value in generator1():
yield value * 2
for item in generator2():
print(item)
# Output: 0 2 4
8. Using Generators with Built-in Functions
Generators work seamlessly with built-in Python functions like sum()
, max()
, min()
, sorted()
, and others. These functions can consume a generator without needing the entire dataset in memory.
Example:
gen = (x for x in range(1000000) if x % 2 == 0)
# Use sum() with a generator
total = sum(gen)
print(total) # Sum of even numbers from 0 to 999999
9. Comparing Generators with Iterators
Generators are a type of iterator, but they are defined differently. Here’s a quick comparison:
Feature | Generator (Function) | Iterator (Class) |
---|---|---|
Syntax | Defined with yield | Requires __iter__() and __next__() methods |
State Handling | Automatic (using yield ) | Manual (you need to maintain state) |
Memory Usage | Lazy (one item at a time) | Lazy (but you need to write custom logic) |
Example | Function-based | Class-based |
10. Performance and Use Cases for Generators
Generators are ideal when:
- You are working with large datasets or streams of data that don’t fit into memory.
- You want to create pipelines for data processing, where each step generates a new item to be processed by the next.
- You need infinite sequences, like counting numbers or processing a continuous stream of data.
Generators are not ideal if you need to:
- Access elements by index, as generators do not support random access.
Creating a Custom Iterator in Python
In Python, an iterator is an object that can be iterated over, meaning it returns data one element at a time. You can create custom iterators by defining a class that implements the __iter__()
and __next__()
methods.
__iter__()
: Returns the iterator object itself.__next__()
: Returns the next value in the sequence. When there are no more items to return, it should raise aStopIteration
exception.
1. Basic Custom Iterator Example
Let's create a custom iterator that returns numbers from 1 to a specified limit.
Example:
class CountToLimit:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __iter__(self):
return self # The iterator object itself
def __next__(self):
self.current += 1
if self.current <= self.limit:
return self.current
else:
raise StopIteration # No more elements to iterate
# Using the custom iterator
counter = CountToLimit(5)
for num in counter:
print(num)
Output:
1
2
3
4
5
2. Understanding the Custom Iterator
__iter__()
: This method returns the iterator object itself. It is called once when the iterator is initialized (e.g., when afor
loop begins).__next__()
: This method is called every time the next item in the sequence is requested. When the sequence is exhausted, it raises theStopIteration
exception to signal the end.
3. Re-iterable Custom Iterator
The above example doesn't allow the iterator to be reused after it's exhausted. If you want your iterator to be re-iterable, you can modify the __iter__()
method to return a new instance of the iterator.
Example:
class CountToLimit:
def __init__(self, limit):
self.limit = limit
def __iter__(self):
return CountToLimitIterator(self.limit) # Return a new iterator instance
class CountToLimitIterator:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __iter__(self):
return self
def __next__(self):
self.current += 1
if self.current <= self.limit:
return self.current
else:
raise StopIteration
# Using the re-iterable custom iterator
counter = CountToLimit(3)
for num in counter:
print(num) # Output: 1 2 3
# Re-using the iterator
for num in counter:
print(num) # Output: 1 2 3
4. Custom Iterable with a Generator
You can also create custom iterators using generators, which simplify the iterator creation process using the yield
keyword.
Example:
class CountToLimit:
def __init__(self, limit):
self.limit = limit
def __iter__(self):
current = 0
while current < self.limit:
current += 1
yield current
# Using the custom iterator with a generator
counter = CountToLimit(4)
for num in counter:
print(num)
5. Advantages of Custom Iterators
- Flexibility: You can define custom iteration logic to suit any need (e.g., iterating over custom data structures, streams, or APIs).
- Efficiency: You can save memory by not loading all data into memory at once (especially for large datasets), just like using generators.
Summary of Custom Iterators in Python:
- Custom iterators require two methods:
__iter__()
and__next__()
. __iter__()
returns the iterator object itself or a new instance, depending on whether you want the iterator to be re-iterable.__next__()
returns the next item or raisesStopIteration
when the iteration is complete.- You can also use generators with
yield
to simplify creating custom iterators.
timeit
in Python
Measuring Code Performance with The timeit
module in Python is used to measure the execution time of small code snippets. It helps in benchmarking the performance of code by running it multiple times and providing an average time. This is useful for comparing different implementations of the same task or optimizing code.
timeit.timeit()
1. Basic Usage of The timeit.timeit()
function is the most commonly used method for timing small code snippets. By default, it runs the code 1,000,000 times (for small snippets) and returns the total time taken.
Syntax:
timeit.timeit(stmt='pass', setup='pass', timer=<default>, number=1000000, globals=None)
stmt
: The code snippet to be timed (as a string).setup
: The setup code that runs once before thestmt
(useful for imports or variable initialization).number
: The number of times the code is executed (default: 1,000,000).globals
: A dictionary to specify the global variables (optional).
Example: Measuring a Basic Code Snippet
import timeit
# Measuring time for a list comprehension
time_taken = timeit.timeit('[x**2 for x in range(1000)]', number=10000)
print(f"Time taken: {time_taken} seconds")
In this example, the list comprehension is executed 10,000 times, and timeit
returns the total execution time.
timeit
with a Setup Block
2. Using If you need to set up some variables or import modules, you can pass a setup block. This code is executed once before the timed code runs.
Example: Using a Setup Block
import timeit
# Measuring time for sorting a list with setup
time_taken = timeit.timeit('sorted(arr)', setup='arr = list(range(1000, 0, -1))', number=10000)
print(f"Time taken: {time_taken} seconds")
In this example, the setup code creates a reverse-ordered list arr
before timing the sorting operation.
timeit
with Functions
3. Using You can also pass function calls to timeit.timeit()
using the globals()
argument to access global functions or variables.
Example: Timing a Function Call
import timeit
def example_function():
return [x**2 for x in range(1000)]
# Timing the function call
time_taken = timeit.timeit('example_function()', globals=globals(), number=10000)
print(f"Time taken: {time_taken} seconds")
Here, the globals()
argument allows timeit
to access the example_function()
defined in the current namespace.
timeit.repeat()
for Multiple Timings
4. Using If you want to run the timing experiment multiple times and get a list of results, you can use the timeit.repeat()
method. This allows you to measure variations in execution time.
timeit.repeat()
Example: Using import timeit
# Measure the time for multiple runs
times = timeit.repeat('[x**2 for x in range(1000)]', number=10000, repeat=5)
print(f"Execution times: {times}")
This runs the code snippet 10,000 times in 5 separate rounds and returns a list of execution times for each round. This is useful for observing variance in the execution time.
5. Timing Code in Jupyter or IPython Notebooks
If you're working in a Jupyter notebook or an IPython environment, you can use the built-in %timeit
magic command, which simplifies timing code snippets.
%timeit
in Jupyter
Example: Using %timeit [x**2 for x in range(1000)]
This automatically runs the code multiple times and provides the average time and standard deviation.
6. Timing Code with Custom Iterations
You can control the number of iterations using the number
parameter. For very fast code snippets, you may need to increase the number to get accurate timing results.
Example: Timing with a Custom Number of Iterations
import timeit
# Increase the number of executions for accuracy
time_taken = timeit.timeit('sum(range(100))', number=100000)
print(f"Time taken for sum(range(100)): {time_taken} seconds")
7. Comparing Different Implementations
You can use timeit
to compare the performance of different implementations of the same task.
map()
Example: Comparing List Comprehension vs. import timeit
# List comprehension
list_comp_time = timeit.timeit('[x**2 for x in range(1000)]', number=10000)
# map() function
map_time = timeit.timeit('list(map(lambda x: x**2, range(1000)))', number=10000)
print(f"List comprehension time: {list_comp_time}")
print(f"map() function time: {map_time}")
timeit
8. Limitations of - Overhead: Small code snippets may suffer from overhead due to repeated function calls. For extremely fast code, the
timeit
overhead may distort the results. - Garbage Collection: By default,
timeit
disables Python's garbage collector. This prevents interference with the results, but in some cases, you might want to manually control it.
Enabling Garbage Collection:
import timeit
time_taken = timeit.timeit('[x**2 for x in range(1000)]', number=10000, globals=globals(), gc=True)
print(f"Time taken with garbage collection: {time_taken} seconds")
timeit
in Python:
Summary of timeit.timeit()
: Measures the time taken to execute a code snippet.timeit.repeat()
: Repeats the timing experiment multiple times for more accurate results.- Setup and Globals: You can provide setup code and global variables using the
setup
andglobals()
arguments. - Use Case:
timeit
is commonly used for benchmarking and comparing different implementations. - IPython/Jupyter
%timeit
: A convenient way to time code snippets in notebooks.
nonlocal
, global
, Namespaces, and import *
Local and Global Scope, 1. Local and Global Scope
In Python, scope defines where variables can be accessed. Python has local, global, and nonlocal scopes:
- Local scope: Variables defined inside a function. These variables are only accessible within that function.
- Global scope: Variables defined outside of all functions. They can be accessed anywhere in the module, including inside functions (unless shadowed by local variables).
Example: Local and Global Scope
x = 10 # Global variable
def my_function():
x = 5 # Local variable
print(f"Inside function: {x}")
my_function()
print(f"Outside function: {x}")
Output:
Inside function: 5
Outside function: 10
In this example, x
inside the function is a local variable that doesn’t affect the global variable x
.
2. Global Keyword
The global
keyword allows you to modify a global variable inside a function. Without global
, any assignment to a variable inside a function creates a local variable.
global
Keyword
Example: Using x = 10 # Global variable
def modify_global():
global x
x = 5 # Modify the global variable
print(f"Inside function: {x}")
modify_global()
print(f"Outside function: {x}")
Output:
Inside function: 5
Outside function: 5
By using global
, the global variable x
is modified inside the function.
3. Nonlocal Keyword
The nonlocal
keyword allows you to modify a variable in the enclosing (non-global) scope. This is useful in nested functions when you want to modify a variable in the outer (but not global) function.
nonlocal
Keyword
Example: Using def outer_function():
x = 10
def inner_function():
nonlocal x
x = 20
print(f"Inside inner function: {x}")
inner_function()
print(f"Inside outer function: {x}")
outer_function()
Output:
Inside inner function: 20
Inside outer function: 20
Here, nonlocal
allows the inner function to modify the x
variable defined in the outer function.
4. Namespaces
A namespace is a mapping between names and objects. In Python, different types of namespaces exist:
- Local namespace: Inside functions.
- Global namespace: At the module level.
- Built-in namespace: Contains Python’s built-in functions (like
print()
,len()
).
You can use the locals()
and globals()
functions to view the local and global namespaces, respectively.
Example: Viewing Namespaces
x = 10 # Global variable
def my_function():
y = 5 # Local variable
print("Local namespace:", locals())
my_function()
print("Global namespace:", globals().keys()) # Show all global variables
5. Built-in Functions and Namespace
Python provides many built-in functions (like len()
, range()
, min()
, max()
). These are part of the built-in namespace, which is automatically available in every Python program.
Example:
# Using built-in functions
print(len([1, 2, 3])) # Output: 3
print(max([1, 5, 3])) # Output: 5
You can view all the built-in functions using dir(__builtins__)
.
Example:
print(dir(__builtins__))
import *
and Importing Modules
6. Using import *
imports all names (functions, variables, classes) from a module into the current namespace. However, it is not recommended due to namespace pollution, which can lead to conflicts between imported names and local variables.
Example:
from math import *
print(sqrt(16)) # Output: 4.0
Here, all functions from the math
module are imported. However, it's better to use import math
to avoid conflicts.
7. Turtle Method
The turtle
module is used for graphics and drawing. It provides a way to control a virtual turtle that moves around the screen.
Example: Drawing a Square with Turtle
import turtle
# Set up the turtle screen
screen = turtle.Screen()
t = turtle.Turtle()
# Draw a square
for _ in range(4):
t.forward(100) # Move forward 100 units
t.right(90) # Turn right 90 degrees
# Close the turtle window when clicked
screen.exitonclick()
In this example, the turtle moves forward and turns right to draw a square.
global
, nonlocal
, and Scope in a Nutshell
8. Python's - Local scope: Variables defined within a function.
- Global scope: Variables defined outside of any function.
global
keyword: Allows modifying global variables inside functions.nonlocal
keyword: Allows modifying variables in an enclosing (non-global) function.- Namespaces: Python has local, global, and built-in namespaces.
- Built-in functions: Automatically available and include functions like
len()
,print()
, andrange()
.
Summary of Scope, Namespaces, and Keywords:
- Scope: Determines the visibility of variables (local vs. global scope).
global
keyword: Used to modify global variables inside functions.nonlocal
keyword: Used to modify variables in the enclosing function.- Namespaces: Python uses local, global, and built-in namespaces.
import *
: Imports all names from a module but is discouraged due to potential name conflicts.turtle
: Provides tools for drawing and controlling a virtual turtle for graphics.