Python has become one of the most popular programming languages in the world, beloved for its readability, versatility, and powerful ecosystem. Whether you’re building web applications, analyzing data, automating tasks, or diving into machine learning, Python provides an elegant and accessible foundation. This comprehensive guide will take you through the fundamental concepts that form the backbone of Python programming.
Getting Started with Python#
Python’s philosophy emphasizes code readability and simplicity. The language uses indentation to define code blocks rather than curly braces or keywords, making programs naturally readable. Before diving into specifics, it’s worth understanding that Python is an interpreted language, meaning your code is executed line by line rather than being compiled into machine code first.
The Python Interpreter#
When you run a Python program, the interpreter reads your source code and executes it directly. You can interact with Python in two primary ways: through scripts (files with a .py extension) or interactively through the Python shell or REPL (Read-Eval-Print Loop). The interactive mode is excellent for experimentation and learning, while scripts are used for building complete applications.
Variables and Data Types#
Python is dynamically typed, meaning you don’t need to declare variable types explicitly. The interpreter infers the type based on the value assigned. This flexibility makes Python particularly approachable for beginners while remaining powerful for advanced use cases.
Basic Data Types#
Python includes several fundamental data types that serve as building blocks for more complex structures.
Integers represent whole numbers without decimal points. Python 3 handles arbitrarily large integers natively, limited only by available memory. You can create integers in decimal, binary (with 0b prefix), octal (with 0o prefix), or hexadecimal (with 0x prefix) notation.
1
2
3
4
| age = 25
population = 7_800_000_000 # Underscores improve readability
binary_value = 0b1010 # Equals 10 in decimal
hex_value = 0xFF # Equals 255 in decimal
|
Floating-point numbers represent real numbers with decimal points. They follow the IEEE 754 standard for double-precision floating-point arithmetic. Be aware that floating-point arithmetic can introduce small precision errors due to how numbers are represented in binary.
1
2
3
| temperature = 98.6
scientific_notation = 3.14e-10 # 3.14 × 10^-10
pi = 3.141592653589793
|
Strings are sequences of characters enclosed in single, double, or triple quotes. Triple quotes allow multi-line strings and are commonly used for documentation strings (docstrings). Strings in Python are immutable, meaning once created, they cannot be changed in place.
1
2
3
4
5
6
| name = 'Alice'
message = "Hello, World!"
multiline = """This is a
multi-line string
spanning several lines"""
raw_string = r"C:\Users\name\file.txt" # Raw strings ignore escape sequences
|
Booleans represent truth values: True or False. They’re essential for control flow and logical operations. In Python, many values can be evaluated in a boolean context, with concepts like “truthy” and “falsy” values determining their behavior in conditionals.
1
2
| is_active = True
has_permission = False
|
Type Conversion#
Python provides built-in functions to convert between types, a process known as type casting. These conversions are explicit and help ensure your data is in the correct format for operations.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # String to integer
age = int("25")
# Integer to string
age_string = str(25)
# String to float
price = float("19.99")
# Float to integer (truncates decimal)
rounded = int(3.7) # Results in 3
# Any type to boolean
bool(0) # False
bool(42) # True
bool("") # False
bool("text") # True
|
None Type#
Python has a special constant called None that represents the absence of a value. It’s commonly used as a default value for function parameters or to indicate that a variable has no value yet. None is a singleton object, meaning there’s only one None object in memory.
1
2
3
| result = None
if result is None:
print("No result yet")
|
Operators#
Operators are symbols that perform operations on variables and values. Python supports a rich set of operators for arithmetic, comparison, logical operations, and more.
Arithmetic Operators#
Python provides standard mathematical operators with intuitive syntax. The division operator always returns a float, while floor division returns an integer by rounding down.
1
2
3
4
5
6
7
| addition = 10 + 5 # 15
subtraction = 10 - 5 # 5
multiplication = 10 * 5 # 50
division = 10 / 3 # 3.3333...
floor_division = 10 // 3 # 3
modulus = 10 % 3 # 1 (remainder)
exponentiation = 2 ** 3 # 8
|
Comparison Operators#
Comparison operators evaluate relationships between values and return boolean results. They can be chained in Python, making complex comparisons more readable.
1
2
3
4
5
6
7
8
9
| equal = 5 == 5 # True
not_equal = 5 != 3 # True
greater = 10 > 5 # True
less = 3 < 8 # True
greater_equal = 5 >= 5 # True
less_equal = 3 <= 2 # False
# Chained comparisons
is_in_range = 1 < x < 10 # More readable than: 1 < x and x < 10
|
Logical Operators#
Logical operators combine boolean expressions using short-circuit evaluation, meaning Python stops evaluating as soon as the result is determined.
1
2
3
4
5
6
| and_result = True and False # False
or_result = True or False # True
not_result = not True # False
# Short-circuit behavior
result = expensive_function() or cached_value # If cached_value is truthy, function isn't called
|
Assignment Operators#
Assignment operators combine assignment with an operation, providing concise syntax for common patterns.
1
2
3
4
5
6
7
8
| x = 10
x += 5 # Equivalent to x = x + 5
x -= 3 # Equivalent to x = x - 3
x *= 2 # Equivalent to x = x * 2
x /= 4 # Equivalent to x = x / 4
x //= 2 # Floor division assignment
x %= 3 # Modulus assignment
x **= 2 # Exponentiation assignment
|
Data Structures#
Python’s built-in data structures provide powerful ways to organize and manipulate collections of data. Understanding when and how to use each structure is fundamental to writing effective Python code.
Lists#
Lists are ordered, mutable sequences that can contain elements of any type. They’re one of the most versatile data structures in Python, supporting dynamic resizing and a wide range of operations.
1
2
3
4
5
6
7
8
9
| # Creating lists
numbers = [1, 2, 3, 4, 5]
mixed = [1, "two", 3.0, True, None]
nested = [[1, 2], [3, 4], [5, 6]]
empty = []
# Accessing elements (zero-indexed)
first = numbers[0] # 1
last = numbers[-1] # 5 (negative indices count from end)
|
Lists support slicing, which creates a new list from a subset of elements. Slice notation uses the format [start:stop:step], where start is inclusive, stop is exclusive, and step determines the increment.
1
2
3
4
5
6
7
8
| numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Slicing
first_three = numbers[0:3] # [0, 1, 2]
middle = numbers[3:7] # [3, 4, 5, 6]
last_three = numbers[-3:] # [7, 8, 9]
every_other = numbers[::2] # [0, 2, 4, 6, 8]
reversed_list = numbers[::-1] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
Lists are mutable, allowing modification after creation. Common list methods provide powerful functionality for manipulation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| fruits = ["apple", "banana"]
# Adding elements
fruits.append("cherry") # Adds to end
fruits.insert(1, "orange") # Inserts at specific index
fruits.extend(["mango", "grape"]) # Adds multiple elements
# Removing elements
fruits.remove("banana") # Removes first occurrence
popped = fruits.pop() # Removes and returns last element
popped_index = fruits.pop(0) # Removes and returns element at index
del fruits[1] # Deletes element at index
fruits.clear() # Removes all elements
# Other useful methods
fruits = ["apple", "banana", "cherry", "banana"]
count = fruits.count("banana") # 2
index = fruits.index("cherry") # 2
fruits.sort() # Sorts in place
fruits.reverse() # Reverses in place
|
Tuples#
Tuples are ordered, immutable sequences. Once created, their elements cannot be changed, making them suitable for data that shouldn’t be modified. Tuples are also more memory-efficient than lists and can be used as dictionary keys.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Creating tuples
coordinates = (10, 20)
single_element = (42,) # Comma required for single-element tuple
without_parens = 1, 2, 3 # Parentheses optional
empty = ()
# Accessing elements (same as lists)
x, y = coordinates # Unpacking
first = coordinates[0]
# Tuples are immutable
# coordinates[0] = 15 # This raises an error
# Use cases
point = (100, 200)
rgb_color = (255, 128, 0)
database_record = (1, "Alice", 30, "[email protected]")
|
Dictionaries#
Dictionaries are unordered collections of key-value pairs, providing fast lookup times. Keys must be immutable types (strings, numbers, tuples), while values can be any type. As of Python 3.7, dictionaries maintain insertion order.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| # Creating dictionaries
person = {
"name": "Alice",
"age": 30,
"city": "New York"
}
# Alternative creation
person = dict(name="Alice", age=30, city="New York")
empty = {}
# Accessing values
name = person["name"] # Raises KeyError if key doesn't exist
age = person.get("age") # Returns None if key doesn't exist
age = person.get("age", 0) # Returns default value if key doesn't exist
# Modifying dictionaries
person["email"] = "[email protected]" # Adding new key-value pair
person["age"] = 31 # Updating existing value
del person["city"] # Removing key-value pair
removed = person.pop("email") # Remove and return value
# Dictionary methods
keys = person.keys() # View of all keys
values = person.values() # View of all values
items = person.items() # View of all (key, value) pairs
# Checking existence
if "name" in person:
print("Name exists")
# Merging dictionaries (Python 3.9+)
defaults = {"theme": "dark", "language": "en"}
settings = {"language": "fr"}
combined = defaults | settings # {"theme": "dark", "language": "fr"}
|
Sets#
Sets are unordered collections of unique elements. They’re excellent for membership testing, removing duplicates, and mathematical set operations like union and intersection.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # Creating sets
fruits = {"apple", "banana", "cherry"}
numbers = set([1, 2, 3, 3, 4, 4, 5]) # {1, 2, 3, 4, 5} - duplicates removed
empty = set() # Note: {} creates an empty dict, not a set
# Adding and removing elements
fruits.add("orange")
fruits.remove("banana") # Raises KeyError if not found
fruits.discard("grape") # Doesn't raise error if not found
popped = fruits.pop() # Removes and returns arbitrary element
# Set operations
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
union = set1 | set2 # {1, 2, 3, 4, 5, 6}
intersection = set1 & set2 # {3, 4}
difference = set1 - set2 # {1, 2}
symmetric_diff = set1 ^ set2 # {1, 2, 5, 6}
# Membership testing (very fast)
if "apple" in fruits:
print("Found")
# Removing duplicates from list
unique_numbers = list(set([1, 2, 2, 3, 3, 3, 4]))
|
Control Flow#
Control flow structures determine the order in which code executes. Python’s clean syntax makes control flow intuitive and readable.
Conditional Statements#
Conditional statements execute different code blocks based on boolean conditions. Python uses indentation to define code blocks, making the structure visually clear.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| # Basic if statement
age = 18
if age >= 18:
print("You are an adult")
# if-else
temperature = 25
if temperature > 30:
print("It's hot")
else:
print("It's comfortable")
# if-elif-else chain
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"
# Nested conditions
x = 10
if x > 0:
if x > 5:
print("x is greater than 5")
else:
print("x is between 0 and 5")
else:
print("x is not positive")
# Ternary operator (conditional expression)
status = "adult" if age >= 18 else "minor"
max_value = a if a > b else b
|
Loops#
Loops allow repeated execution of code blocks. Python provides two primary loop types: for loops for iteration over sequences and while loops for condition-based repetition.
For Loops#
For loops iterate over sequences like lists, strings, ranges, or any iterable object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| # Iterating over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# Iterating over a string
for char in "Hello":
print(char)
# Using range
for i in range(5): # 0, 1, 2, 3, 4
print(i)
for i in range(2, 10): # 2 through 9
print(i)
for i in range(0, 10, 2): # 0, 2, 4, 6, 8 (step of 2)
print(i)
# Iterating with index
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
# Iterating over dictionary
person = {"name": "Alice", "age": 30}
for key in person:
print(key, person[key])
for key, value in person.items():
print(key, value)
# Nested loops
for i in range(3):
for j in range(3):
print(f"({i}, {j})")
|
While Loops#
While loops continue executing as long as a condition remains true. They’re useful when the number of iterations isn’t known in advance.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| # Basic while loop
count = 0
while count < 5:
print(count)
count += 1
# User input loop
password = ""
while password != "secret":
password = input("Enter password: ")
# Infinite loop with break
while True:
response = input("Continue? (y/n): ")
if response.lower() == 'n':
break
print("Continuing...")
# While with else (executes if loop completes normally)
n = 5
while n > 0:
print(n)
n -= 1
else:
print("Countdown complete!")
|
Loop Control Statements#
Python provides keywords to control loop execution flow.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # break - exits the loop immediately
for i in range(10):
if i == 5:
break
print(i) # Prints 0, 1, 2, 3, 4
# continue - skips to next iteration
for i in range(10):
if i % 2 == 0:
continue
print(i) # Prints only odd numbers
# pass - placeholder that does nothing
for i in range(5):
if i == 2:
pass # TODO: implement later
print(i)
# Loop with else clause (executes if no break occurs)
for i in range(5):
if i == 10:
break
else:
print("Loop completed without break")
|
Functions#
Functions are reusable blocks of code that perform specific tasks. They promote code organization, reusability, and modularity. Functions in Python are first-class objects, meaning they can be assigned to variables, passed as arguments, and returned from other functions.
Defining Functions#
Functions are defined using the def keyword, followed by the function name, parameters in parentheses, and a colon. The function body is indented.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| # Basic function
def greet():
print("Hello, World!")
greet() # Call the function
# Function with parameters
def greet_person(name):
print(f"Hello, {name}!")
greet_person("Alice")
# Function with return value
def add(a, b):
return a + b
result = add(5, 3) # 8
# Function with multiple return values (actually returns a tuple)
def get_coordinates():
return 10, 20
x, y = get_coordinates()
# Function with default parameters
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Uses default greeting
greet("Bob", "Hi") # Overrides default
# Function with keyword arguments
def create_user(name, age, city="Unknown"):
return {"name": name, "age": age, "city": city}
user = create_user(name="Alice", age=30)
user = create_user(age=25, name="Bob", city="NYC") # Order doesn't matter
|
Variable-Length Arguments#
Functions can accept arbitrary numbers of arguments using special syntax.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # *args - accepts variable number of positional arguments
def sum_all(*numbers):
total = 0
for num in numbers:
total += num
return total
result = sum_all(1, 2, 3, 4, 5) # 15
# **kwargs - accepts variable number of keyword arguments
def create_profile(**details):
for key, value in details.items():
print(f"{key}: {value}")
create_profile(name="Alice", age=30, city="NYC")
# Combining different parameter types
def complex_function(required, *args, default="value", **kwargs):
print(f"Required: {required}")
print(f"Args: {args}")
print(f"Default: {default}")
print(f"Kwargs: {kwargs}")
complex_function(1, 2, 3, default="custom", extra="info")
|
Lambda Functions#
Lambda functions are small anonymous functions defined using the lambda keyword. They’re useful for short, simple operations, especially when passing functions as arguments.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # Basic lambda
square = lambda x: x ** 2
print(square(5)) # 25
# Lambda with multiple parameters
add = lambda a, b: a + b
print(add(3, 4)) # 7
# Using lambda with built-in functions
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers)) # [1, 4, 9, 16, 25]
# Filtering with lambda
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]
# Sorting with lambda
people = [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Charlie", "age": 35}
]
sorted_by_age = sorted(people, key=lambda p: p["age"])
|
Scope and Closures#
Variables have different scopes depending on where they’re defined. Python follows the LEGB rule: Local, Enclosing, Global, Built-in.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| # Global scope
global_var = "I'm global"
def outer_function():
# Enclosing scope
enclosing_var = "I'm in enclosing scope"
def inner_function():
# Local scope
local_var = "I'm local"
print(local_var)
print(enclosing_var)
print(global_var)
inner_function()
outer_function()
# Modifying global variables
count = 0
def increment():
global count # Declare we're using global variable
count += 1
increment()
print(count) # 1
# Closures - inner function remembers enclosing scope
def make_multiplier(n):
def multiply(x):
return x * n
return multiply
times_two = make_multiplier(2)
times_three = make_multiplier(3)
print(times_two(5)) # 10
print(times_three(5)) # 15
|
Docstrings#
Docstrings are string literals that document functions, classes, and modules. They’re enclosed in triple quotes and can be accessed using the __doc__ attribute.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| def calculate_area(length, width):
"""
Calculate the area of a rectangle.
Args:
length (float): The length of the rectangle
width (float): The width of the rectangle
Returns:
float: The area of the rectangle
Examples:
>>> calculate_area(5, 3)
15
"""
return length * width
print(calculate_area.__doc__)
|
List Comprehensions and Generators#
Python provides elegant syntax for creating lists and generators through comprehensions, allowing concise and readable code for common patterns.
List Comprehensions#
List comprehensions create new lists by applying an expression to each element in an iterable, optionally filtering elements.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # Basic list comprehension
squares = [x ** 2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# With condition
evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# Transforming strings
names = ["alice", "bob", "charlie"]
capitalized = [name.capitalize() for name in names]
# ["Alice", "Bob", "Charlie"]
# Nested list comprehension
matrix = [[i * j for j in range(1, 4)] for i in range(1, 4)]
# [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
# With if-else
numbers = [1, 2, 3, 4, 5]
labels = ["even" if x % 2 == 0 else "odd" for x in numbers]
# ["odd", "even", "odd", "even", "odd"]
# Flattening nested list
nested = [[1, 2], [3, 4], [5, 6]]
flat = [item for sublist in nested for item in sublist]
# [1, 2, 3, 4, 5, 6]
|
Dictionary and Set Comprehensions#
Similar syntax works for creating dictionaries and sets.
1
2
3
4
5
6
7
8
9
10
11
12
| # Dictionary comprehension
squares_dict = {x: x ** 2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# Set comprehension
unique_lengths = {len(word) for word in ["hello", "world", "hi"]}
# {2, 5}
# Practical example: inverting dictionary
original = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in original.items()}
# {1: "a", 2: "b", 3: "c"}
|
Generator Expressions#
Generators produce values on-demand rather than storing them all in memory, making them memory-efficient for large datasets.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| # Generator expression (uses parentheses)
squares_gen = (x ** 2 for x in range(1000000))
# Generators are iterators - consume values one at a time
first_square = next(squares_gen) # 0
second_square = next(squares_gen) # 1
# Using in loops
for square in (x ** 2 for x in range(10)):
print(square)
# Generator functions (using yield)
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
for num in fibonacci(10):
print(num)
# Infinite generator
def infinite_counter():
n = 0
while True:
yield n
n += 1
counter = infinite_counter()
print(next(counter)) # 0
print(next(counter)) # 1
|
Exception Handling#
Exceptions are events that disrupt normal program flow. Python’s exception handling mechanism allows you to gracefully handle errors and maintain program stability.
Try-Except Blocks#
The try block contains code that might raise an exception, while except blocks handle specific exceptions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| # Basic exception handling
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero!")
# Multiple exception types
try:
number = int(input("Enter a number: "))
result = 10 / number
except ValueError:
print("Invalid input - not a number")
except ZeroDivisionError:
print("Cannot divide by zero")
# Catching multiple exceptions together
try:
# risky operation
pass
except (ValueError, TypeError, KeyError) as e:
print(f"Error occurred: {e}")
# Generic exception handler (use sparingly)
try:
# some operation
pass
except Exception as e:
print(f"An error occurred: {e}")
# Else clause (executes if no exception occurs)
try:
number = int(input("Enter a number: "))
except ValueError:
print("Invalid input")
else:
print(f"You entered: {number}")
# Finally clause (always executes)
try:
file = open("data.txt", "r")
data = file.read()
except FileNotFoundError:
print("File not found")
finally:
# Cleanup code (always runs)
if 'file' in locals():
file.close()
|
Raising Exceptions#
You can raise exceptions intentionally to signal errors or invalid states.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| # Raising built-in exceptions
def validate_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
if age > 150:
raise ValueError("Age seems unrealistic")
return age
# Re-raising exceptions
try:
validate_age(-5)
except ValueError as e:
print(f"Validation failed: {e}")
raise # Re-raises the same exception
# Custom exceptions
class InsufficientFundsError(Exception):
"""Raised when account balance is insufficient"""
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError("Insufficient funds for withdrawal")
return balance - amount
try:
new_balance = withdraw(100, 150)
except InsufficientFundsError as e:
print(e)
|
File Operations#
Python provides straightforward methods for reading from and writing to files, essential for data persistence and processing.
Reading Files#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # Reading entire file
try:
with open("data.txt", "r") as file:
content = file.read()
print(content)
except FileNotFoundError:
print("File not found")
# Reading line by line
with open("data.txt", "r") as file:
for line in file:
print(line.strip()) # strip() removes newline characters
# Reading all lines into a list
with open("data.txt", "r") as file:
lines = file.readlines()
# Reading specific number of characters
with open("data.txt", "r") as file:
chunk = file.read(100) # Read first 100 characters
|
Writing Files#
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Writing to file (overwrites existing content)
with open("output.txt", "w") as file:
file.write("Hello, World!\n")
file.write("Second line\n")
# Appending to file
with open("output.txt", "a") as file:
file.write("Additional line\n")
# Writing multiple lines
lines = ["First line\n", "Second line\n", "Third line\n"]
with open("output.txt", "w") as file:
file.writelines(lines)
|
File Modes#
Python supports various file modes for different operations:
r - Read (default mode)w - Write (overwrites existing file)a - Append (adds to end of file)r+ - Read and writeb - Binary mode (e.g., rb, wb)x - Exclusive creation (fails if file exists)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Binary mode for non-text files
with open("image.png", "rb") as file:
binary_data = file.read()
# Context manager (with statement) automatically closes file
with open("data.txt", "r") as file:
content = file.read()
# File is automatically closed here
# Manual file handling (not recommended)
file = open("data.txt", "r")
try:
content = file.read()
finally:
file.close() # Always close files
|
Object-Oriented Programming#
Object-oriented programming (OOP) in Python allows you to structure code around objects that combine data and behavior. This paradigm promotes code reusability, modularity, and intuitive design.
Classes and Objects#
Classes define blueprints for objects, encapsulating data (attributes) and behavior (methods).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # Defining a class
class Dog:
# Class attribute (shared by all instances)
species = "Canis familiaris"
# Constructor method
def __init__(self, name, age):
# Instance attributes
self.name = name
self.age = age
# Instance method
def bark(self):
return f"{self.name} says woof!"
def get_info(self):
return f"{self.name} is {self.age} years old"
# Creating objects (instances)
dog1 = Dog("Buddy", 5)
dog2 = Dog("Max", 3)
# Accessing attributes and methods
print(dog1.name) # "Buddy"
print(dog1.bark()) # "Buddy says woof!"
print(dog1.species) # "Canis familiaris"
|
Inheritance#
Inheritance allows classes to derive properties and methods from parent classes, promoting code reuse.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| # Parent class
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass # To be implemented by subclasses
# Child classes
class Dog(Animal):
def speak(self):
return f"{self.name} barks"
class Cat(Animal):
def speak(self):
return f"{self.name} meows"
# Using inherited classes
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # "Buddy barks"
print(cat.speak()) # "Whiskers meows"
# Calling parent class methods
class GoldenRetriever(Dog):
def __init__(self, name, age):
super().__init__(name) # Call parent constructor
self.age = age
def get_details(self):
return f"{self.speak()} and is {self.age} years old"
golden = GoldenRetriever("Charlie", 4)
print(golden.get_details())
|
Encapsulation#
Encapsulation restricts direct access to some components, using naming conventions to indicate privacy levels.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner # Public
self._balance = balance # Protected (convention: one underscore)
self.__account_number = self._generate_account_number() # Private (name mangling)
def _generate_account_number(self): # Protected method
import random
return random.randint(10000000, 99999999)
def deposit(self, amount):
if amount > 0:
self._balance += amount
return True
return False
def withdraw(self, amount):
if 0 < amount <= self._balance:
self._balance -= amount
return True
return False
def get_balance(self):
return self._balance
account = BankAccount("Alice", 1000)
account.deposit(500)
print(account.get_balance()) # 1500
|
Class Methods and Static Methods#
Python supports different types of methods for various use cases.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| class MathOperations:
precision = 2 # Class attribute
def __init__(self, value):
self.value = value
# Instance method (operates on instance)
def add(self, other):
return self.value + other
# Class method (operates on class, not instance)
@classmethod
def set_precision(cls, precision):
cls.precision = precision
@classmethod
def from_string(cls, value_string):
# Alternative constructor
return cls(int(value_string))
# Static method (doesn't access instance or class)
@staticmethod
def is_even(number):
return number % 2 == 0
# Using different method types
math = MathOperations(10)
print(math.add(5)) # 15
print(MathOperations.is_even(4)) # True
MathOperations.set_precision(3)
# Alternative constructor
math2 = MathOperations.from_string("20")
|
Special Methods (Magic Methods)#
Special methods (also called dunder methods) allow classes to implement behavior for built-in operations.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
# String representation for print()
return f"Vector({self.x}, {self.y})"
def __repr__(self):
# Official string representation
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
# Define behavior for + operator
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other):
# Define behavior for == operator
return self.x == other.x and self.y == other.y
def __len__(self):
# Define behavior for len()
return int((self.x ** 2 + self.y ** 2) ** 0.5)
v1 = Vector(2, 3)
v2 = Vector(3, 4)
v3 = v1 + v2 # Uses __add__
print(v3) # Uses __str__
print(v1 == v2) # Uses __eq__
|
Modules and Packages#
Modules organize code into reusable files, while packages group related modules together. This structure promotes code organization and reusability across projects.
Creating and Importing Modules#
A module is simply a Python file containing definitions and statements. The file name is the module name with a .py extension.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # In file: math_operations.py
def add(a, b):
return a + b
def multiply(a, b):
return a * b
PI = 3.14159
# In another file: main.py
import math_operations
result = math_operations.add(5, 3)
print(math_operations.PI)
# Alternative import methods
from math_operations import add, multiply
result = add(5, 3)
from math_operations import * # Import everything (not recommended)
import math_operations as mo # Use alias
result = mo.add(5, 3)
from math_operations import add as addition
result = addition(5, 3)
|
Standard Library Modules#
Python includes a rich standard library with modules for common tasks.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| # Math operations
import math
print(math.sqrt(16)) # 4.0
print(math.pi) # 3.141592653589793
# Random numbers
import random
print(random.randint(1, 10)) # Random integer between 1 and 10
print(random.choice(["apple", "banana", "cherry"]))
# Date and time
from datetime import datetime, timedelta
now = datetime.now()
tomorrow = now + timedelta(days=1)
print(now.strftime("%Y-%m-%d %H:%M:%S"))
# Working with JSON
import json
data = {"name": "Alice", "age": 30}
json_string = json.dumps(data)
parsed_data = json.loads(json_string)
# Regular expressions
import re
pattern = r"\d+" # Match one or more digits
matches = re.findall(pattern, "abc123def456") # ["123", "456"]
# Operating system operations
import os
print(os.getcwd()) # Current working directory
files = os.listdir(".") # List files in directory
|
Creating Packages#
Packages are directories containing an __init__.py file and one or more modules.
1
2
3
4
5
6
7
| my_package/
__init__.py
module1.py
module2.py
subpackage/
__init__.py
module3.py
|
1
2
3
4
5
6
7
| # In __init__.py (can be empty or contain initialization code)
from .module1 import function1
from .module2 import function2
# Using the package
from my_package import function1
from my_package.subpackage import module3
|
Python offers multiple ways to format strings, each with its own advantages for different situations.
F-strings, introduced in Python 3.6, provide the most readable and efficient string formatting method.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| name = "Alice"
age = 30
height = 5.7
# Basic f-string
message = f"My name is {name} and I am {age} years old"
# Expressions in f-strings
print(f"Next year I'll be {age + 1}")
print(f"Name in uppercase: {name.upper()}")
# Formatting numbers
price = 19.99
print(f"Price: ${price:.2f}") # Two decimal places
# Alignment and padding
print(f"{name:<10}") # Left align (width 10)
print(f"{name:>10}") # Right align
print(f"{name:^10}") # Center align
# Multiple values
print(f"{name} is {height:.1f} feet tall")
# Debugging (Python 3.8+)
x = 10
print(f"{x=}") # Prints: x=10
|
The format() method provides powerful formatting capabilities.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Basic usage
message = "Hello, {}!".format("World")
message = "Hello, {0}!".format("World")
# Multiple placeholders
text = "{0} is {1} years old".format("Alice", 30)
text = "{name} is {age} years old".format(name="Alice", age=30)
# Formatting specifications
price = 19.995
print("Price: ${:.2f}".format(price)) # $19.99
print("Binary: {:b}".format(10)) # Binary: 1010
print("Hex: {:x}".format(255)) # Hex: ff
print("Percentage: {:.1%}".format(0.75)) # Percentage: 75.0%
|
Common String Methods#
Python strings have numerous built-in methods for manipulation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| text = " Hello, World! "
# Case conversion
print(text.lower()) # " hello, world! "
print(text.upper()) # " HELLO, WORLD! "
print(text.title()) # " Hello, World! "
print(text.capitalize()) # " hello, world! "
# Whitespace handling
print(text.strip()) # "Hello, World!"
print(text.lstrip()) # "Hello, World! "
print(text.rstrip()) # " Hello, World!"
# Searching and replacing
print("Hello, World!".replace("World", "Python"))
print("Hello, World!".find("World")) # Returns index or -1
print("Hello, World!".count("o")) # Count occurrences
# Splitting and joining
words = "apple,banana,cherry".split(",") # ["apple", "banana", "cherry"]
joined = "-".join(words) # "apple-banana-cherry"
# Checking content
print("hello".isalpha()) # True
print("123".isdigit()) # True
print("hello123".isalnum()) # True
print("hello world".startswith("hello")) # True
print("hello world".endswith("world")) # True
# Padding
print("5".zfill(3)) # "005"
print("hello".center(10, "*")) # "**hello***"
|
Conclusion#
Python’s fundamentals form a solid foundation for tackling more advanced topics and real-world programming challenges. The language’s emphasis on readability and simplicity, combined with its powerful features, make it an excellent choice for beginners and experienced developers alike.
As you continue your Python journey, you’ll discover that these fundamental concepts serve as building blocks for understanding frameworks, libraries, and advanced programming paradigms. Whether you’re interested in web development with Django or Flask, data science with Pandas and NumPy, machine learning with TensorFlow or PyTorch, or automation and scripting, these core principles remain constant.
The best way to solidify your understanding is through practice. Start with small projects, experiment with different approaches, and don’t be afraid to make mistakes. The Python community is vast and welcoming, with abundant resources, documentation, and support available. Embrace the Pythonic way of thinking - write code that’s clear, concise, and elegant - and you’ll find yourself solving complex problems with surprising ease.
Remember that mastering Python is a journey, not a destination. Even experienced Python developers continually learn new techniques, patterns, and best practices. Keep exploring, keep building, and most importantly, enjoy the process of bringing your ideas to life through code.