Python Explained
1. Introduction
1.1 What is Python?
Python is a high-level, interpreted programming language known for its simplicity and readability. Created by Guido van Rossum and first released in 1991, Python has grown to become one of the most popular programming languages in the world. Its design philosophy emphasizes code readability with its notable use of significant whitespace.
Python is versatile and widely used in various domains including web development, data science, artificial intelligence, scientific computing, automation, and more. Whether you're a complete beginner to programming or an experienced developer looking to add another language to your toolkit, Python offers a gentle learning curve while providing powerful capabilities.
This guide aims to help you learn the fundamental concepts of Python programming. We'll cover everything from basic syntax to more advanced topics like object-oriented programming, ensuring you have a solid foundation to build upon. By the end of this guide, you'll be able to write your own Python programs and understand code written by others.
1.2 Basic Data Types
Python has several built-in data types that are used to define the operations possible on them and the storage method for each of them. The most fundamental data types in Python include:
The basic data types in Python are:
- Integers (int): Whole numbers like 3, 100, -10
- Floating-point numbers (float): Decimal numbers like 3.14, -0.001
- Strings (str): Text enclosed in quotes like "Hello", 'Python'
- Booleans (bool): True or False values
- None: Represents the absence of a value
Python is dynamically typed, which means you don't need to declare the type of a variable when you create it. The interpreter automatically determines the type based on the assigned value.
Examples of basic data types in Python:
- age = 25 # Integer
- pi = 3.14159 # Float
- name = "Alice" # String
- is_student = True # Boolean
- empty_value = None # NoneType
You can check the type of any variable using the built-in type() function:
# Checking variable types age = 25 pi = 3.14159 name = "Alice" is_student = True print(type(age)) #print(type(pi)) # print(type(name)) # print(type(is_student)) #
1.3 Collections
Python provides several built-in collection types that allow you to store multiple items in a single variable. The main collection types are lists, tuples, sets, and dictionaries.
# Lists - ordered, mutable collections
fruits = ["apple", "banana", "cherry"]
print(fruits[0]) # Accessing by index: apple
fruits.append("orange") # Adding an item
print(fruits) # ['apple', 'banana', 'cherry', 'orange']
# Tuples - ordered, immutable collections
coordinates = (10, 20)
print(coordinates[1]) # 20
# coordinates[0] = 15 # Error! Tuples cannot be modified
# Sets - unordered collections of unique items
colors = {"red", "green", "blue", "red"} # Duplicate is removed
print(colors) # {'green', 'red', 'blue'} (order may vary)
colors.add("yellow") # Adding an item
# Dictionaries - key-value pairs
person = {
"name": "John",
"age": 30,
"city": "New York"
}
print(person["name"]) # John
person["email"] = "john@example.com" # Adding a new key-value pair
1.4 Type Hints
Type hints in Python are a way to indicate the expected data types of variables, function parameters, and return values. They were introduced in Python 3.5 with PEP 484 and are completely optional but can make your code more readable and maintainable.
Basic Syntax of Type Hints:
variable_name: type = value
type: The expected data type.
value: The initial value (optional).
Example 1 - Using Type Hints for Variables:
from typing import List, Dict, Tuple
# Simple type hints
name: str = "Alice"
age: int = 25
is_student: bool = True
# Type hints for collections
scores: List[int] = [95, 89, 78, 92]
user_info: Dict[str, str] = {"name": "Bob", "email": "bob@example.com"}
point: Tuple[int, int] = (10, 20)
Example 2 - Using Type Hints for Functions:
from typing import List, Optional
def greet(name: str) -> str:
return f"Hello, {name}!"
def calculate_average(numbers: List[float]) -> float:
return sum(numbers) / len(numbers)
def find_user(user_id: int) -> Optional[str]:
# Optional indicates the function might return None
users = {1: "Alice", 2: "Bob"}
return users.get(user_id) # Returns None if user_id not found
1.5 Key Takeaways
Python type hints provide a way to indicate expected types without enforcing them at runtime. They help with code readability, IDE support, and static type checking. The following table summarizes different aspects of type hints in Python:
| Feature | Description | Example |
|---|---|---|
| Basic Type Hints | Annotate variables with their expected types | name: str = "Alice" |
| Function Annotations | Specify parameter and return types | def add(x: int, y: int) -> int: |
| Collection Types | Specify types for collections | items: List[str] |
| Optional Types | Indicate a value might be None | def find(id: int) -> Optional[User]: |
| Union Types | Indicate multiple possible types | data: Union[str, int] |
| Type Checking | Tools like mypy can check type consistency | mypy script.py |
In Python, type hints are optional and don't affect runtime behavior. They're primarily used for documentation, IDE support, and static type checking with tools like mypy. Python remains dynamically typed regardless of type hints.
2. Basic Concepts
2.1 Basic Structure of a Python Program
Python programs are typically organized into modules and functions. Unlike many other programming languages, Python doesn't require a main function to start execution - the code is executed from top to bottom.
- Import Statements - To include modules and libraries
- Functions and Classes - Define reusable blocks of code
- Main Code - The primary execution path
- Indentation - Python uses indentation to define code blocks
Example: Writing a Basic Python Program
# Import a module
import math
# Define a function
def greet(name):
return f"Hello, {name}!"
# Main code
if __name__ == "__main__":
# This block runs when the script is executed directly
print(greet("World")) # Output: Hello, World!
print(f"The value of pi is approximately {math.pi:.2f}")
Explanation:
- import math - Imports the math module for mathematical functions
- def greet(name): - Defines a function that takes a name parameter
- if __name__ == "__main__": - A common pattern to determine if the script is run directly
- print() - Outputs text to the console
2.2 Variables and Data Types in Python
Variables in Python are dynamically typed, meaning you don't need to declare their type explicitly. The type is determined at runtime based on the assigned value.
Common Data Types in Python:
| Data Type | Example | Description |
|---|---|---|
int |
age = 25 |
Whole numbers (e.g., 10, -5) |
float |
price = 19.99 |
Decimal numbers (e.g., 3.14, -0.001) |
str |
name = "Alice" |
Text sequences (e.g., "Hello", 'Python') |
bool |
is_active = True |
Boolean values (True or False) |
list |
numbers = [1, 2, 3] |
Ordered, mutable collections |
tuple |
point = (10, 20) |
Ordered, immutable collections |
dict |
person = {"name": "John"} |
Key-value pairs |
set |
unique_nums = {1, 2, 3} |
Unordered collection of unique items |
Understanding these data types is crucial for effective Python programming.
# Variable examples
age = 25 # Integer
height = 5.9 # Float
name = "Alice" # String
is_student = True # Boolean
numbers = [1, 2, 3, 4] # List
point = (10, 20) # Tuple
person = { # Dictionary
"name": "Bob",
"age": 30,
"city": "New York"
}
unique_colors = {"red", "green", "blue"} # Set
# Printing variables
print(f"Name: {name}, Age: {age}")
print(f"Height: {height}")
print(f"Is student: {is_student}")
print(f"First number: {numbers[0]}")
2.3 Operators in Python
Python includes various operators for performing operations on variables and values.
| Operator | Example | Description |
|---|---|---|
+ |
x + y |
Addition |
- |
x - y |
Subtraction |
* |
x * y |
Multiplication |
/ |
x / y |
Division (returns float) |
// |
x // y |
Floor division (returns int) |
% |
x % y |
Modulus (Remainder) |
** |
x ** y |
Exponentiation (x to the power of y) |
== |
x == y |
Equal to |
!= |
x != y |
Not equal to |
and |
x and y |
Logical AND |
or |
x or y |
Logical OR |
Example: Using Operators in Python
# Arithmetic operators
a = 10
b = 3
print(f"Sum: {a + b}") # 13
print(f"Difference: {a - b}") # 7
print(f"Product: {a * b}") # 30
print(f"Division: {a / b}") # 3.3333...
print(f"Floor Division: {a // b}") # 3
print(f"Modulus: {a % b}") # 1
print(f"Exponent: {a ** b}") # 1000
# Comparison operators
print(f"Equal: {a == b}") # False
print(f"Not Equal: {a != b}") # True
print(f"Greater Than: {a > b}") # True
# Logical operators
x = True
y = False
print(f"x AND y: {x and y}") # False
print(f"x OR y: {x or y}") # True
print(f"NOT x: {not x}") # False
2.4 Conditional Statements in Python
Python provides if, elif, and else statements to control the flow of execution based on conditions.
1. if Statement
Executes a block of code if the condition is true.
age = 18
if age >= 18:
print("You are an adult.")
2. if-else Statement
Executes one block if true, another if false.
num = 10
if num % 2 == 0:
print("Even number")
else:
print("Odd number")
3. if-elif-else Statement
Used when you have multiple conditions to check.
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
elif score >= 60:
grade = "D"
else:
grade = "F"
print(f"Your grade is {grade}")
4. Loops in Python
Loops are used to execute a block of code multiple times.
4.1 for Loop
Used to iterate over a sequence (like a list, tuple, string) or other iterable objects.
# Iterating through a range
for i in range(1, 6): # 1 to 5
print(f"Iteration: {i}")
# Iterating through a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(f"Fruit: {fruit}")
4.2 while Loop
Executes code while a condition is true.
count = 1
while count <= 5:
print(f"Count: {count}")
count += 1
5. List Comprehensions
A concise way to create lists based on existing lists.
# Creating a list of squares
squares = [x**2 for x in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]
# Creating a filtered list
even_numbers = [x for x in range(1, 11) if x % 2 == 0]
print(even_numbers) # [2, 4, 6, 8, 10]
3. Functions
3.1 Functions
Functions in Python are blocks of reusable code designed to perform a specific task. They help in organizing code, making it more modular and easier to maintain.
# Basic function definition
def greet(name):
"""This function greets the person passed in as a parameter"""
print(f"Hello, {name}!")
# Function call
greet("Alice")
Key components of a Python function:
- The
defkeyword marks the start of a function definition - A function name that follows Python's naming rules
- Parameters (optional) enclosed in parentheses
- A docstring (optional) that describes what the function does
- The function body, indented under the definition
- A return statement (optional) to send a value back
3.2 Function Parameters
Python functions can accept different types of parameters:
# 1. Required parameters
def greet(name):
print(f"Hello, {name}!")
# 2. Default parameters
def greet_with_message(name, message="Good morning"):
print(f"{message}, {name}!")
# 3. Keyword arguments
def describe_person(name, age, city):
print(f"{name} is {age} years old and lives in {city}.")
# Function calls
greet("Alice") # Required parameter
greet_with_message("Bob") # Using default parameter
greet_with_message("Charlie", "Welcome") # Overriding default
describe_person(name="David", age=30, city="New York") # Keyword arguments
3.3 Function Calls
When you call a function in Python, you're executing the code inside that function. Here's an example using Python's built-in math functions:
import math
# Using built-in math functions
x = 2.5
# Calculate x raised to power 3
y = math.pow(x, 3)
print(f"{x} raised to power 3 is: {y}")
# Calculate square root
z = math.sqrt(16)
print(f"Square root of 16 is: {z}")
# Using math functions in expressions
result = 2 + math.pow(5, x)
print(f"2 + (5 raised to power {x}) is: {result}")
Output:
2.5 raised to power 3 is: 15.625
Square root of 16 is: 4.0
2 + (5 raised to power 2.5) is: 57.9017
3.4 Types of Functions
Python supports various types of functions to handle different programming needs:
import random
# Function that generates random numbers
def generate_random_numbers(count, start=1, end=100):
"""Generate a list of random numbers within a range"""
numbers = []
for _ in range(count):
numbers.append(random.randint(start, end))
return numbers
# Main code
if __name__ == "__main__":
print("--- Random Numbers Generator ---\n")
# Get user input for seed (optional in Python)
seed = int(input("Enter a seed value for the random generator: "))
random.seed(seed) # Set the random seed
# Generate three random numbers
random_numbers = generate_random_numbers(3)
print(f"\nThree random numbers: {random_numbers[0]} {random_numbers[1]} {random_numbers[2]}")
Python functions can be categorized based on their behavior:
- Functions that perform actions but don't return values
- Functions that compute and return values
- Functions that modify their arguments
- Functions that have side effects (like printing or writing to files)
3.5 Types of Functions in Python
Function Without Parameters & Return: A function that doesn't take input or return anything.
def greet():
print("Hello, welcome to Python functions!")
# Function call
greet()
- def greet(): - Defines a function with no parameters
- The function prints a message but doesn't return anything
- greet() calls the function
3.6 Function With Return Value
A function that takes input and returns a value.
def add(a, b):
return a + b
# Function call
sum_result = add(5, 3)
print(f"Sum: {sum_result}") # Output: Sum: 8
# Using the return value directly in an expression
print(f"Result: {add(10, 20) * 2}") # Output: Result: 60
- def add(a, b): - Takes two parameters
- return a + b - Sends back the result
- sum_result = add(5, 3) - Calls add(), stores the result
3.7 Function With Default Parameters
A function that uses default values if no argument is provided.
def display_info(name="Guest", age=18, city="Unknown"):
print(f"Name: {name}, Age: {age}, City: {city}")
# Function calls
display_info() # Uses all defaults
display_info("Alice") # Overrides just the name
display_info("Bob", 25) # Overrides name and age
display_info("Charlie", 30, "London") # Overrides all parameters
display_info(name="David", city="Paris") # Named parameters, age uses default
- Default parameters provide values when arguments aren't specified
- You can override any combination of default parameters
- Named arguments allow skipping parameters in any order
3.8 Lambda Functions
Lambda functions are small, anonymous functions defined with the lambda keyword.
# Regular function
def square(x):
return x ** 2
# Equivalent lambda function
square_lambda = lambda x: x ** 2
print(square(5)) # Output: 25
print(square_lambda(5)) # Output: 25
# Lambda functions are often used with map(), filter(), and sorted()
numbers = [1, 5, 3, 9, 2, 6]
# Using lambda with map() to square all numbers
squared = list(map(lambda x: x ** 2, numbers))
print(f"Squared: {squared}") # [1, 25, 9, 81, 4, 36]
# Using lambda with filter() to get even numbers
even = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Even numbers: {even}") # [2, 6]
# Using lambda with sorted() for custom sorting
people = [("Alice", 25), ("Bob", 30), ("Charlie", 20)]
sorted_by_age = sorted(people, key=lambda person: person[1])
print(f"Sorted by age: {sorted_by_age}") # [('Charlie', 20), ('Alice', 25), ('Bob', 30)]
Lambda functions are best used when:
- You need a simple function for a short period
- You're passing a function as an argument to another function
- You want to define a function inline without a separate def statement
3.9 Args and Kwargs
Python allows functions to accept a variable number of arguments using *args and **kwargs.
# *args: Variable positional arguments (as a tuple)
def sum_all(*args):
total = 0
for num in args:
total += num
return total
print(sum_all(1, 2, 3)) # 6
print(sum_all(10, 20, 30, 40)) # 100
# **kwargs: Variable keyword arguments (as a dictionary)
def display_person(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
display_person(name="Alice", age=25, city="New York")
# Output:
# name: Alice
# age: 25
# city: New York
# Combining positional, *args, default, and **kwargs
def example_function(pos1, pos2, *args, default1="default", **kwargs):
print(f"Positional: {pos1}, {pos2}")
print(f"Args: {args}")
print(f"Default: {default1}")
print(f"Kwargs: {kwargs}")
example_function(1, 2, 3, 4, 5, default1="custom", key1="value1", key2="value2")
# Output:
# Positional: 1, 2
# Args: (3, 4, 5)
# Default: custom
# Kwargs: {'key1': 'value1', 'key2': 'value2'}
Using *args and **kwargs provides flexibility when:
- You don't know in advance how many arguments will be passed
- You want to create wrapper functions that pass all arguments to another function
- You need to create highly flexible APIs