Python is a high-level, interpreted, dynamically-typed programming language renowned for its readability and ease of use. Its extensive standard library and vibrant ecosystem make it a popular choice across various domains, including web development, data analysis, artificial intelligence, scientific computing, and automation.
Python Syntax: Indentation Matters
Unlike many languages that use curly braces {} to define code blocks, Python uses indentation (typically four spaces). Consistent indentation is crucial for defining the scope of loops, functions, classes, and conditional statements.
# Example of indentation defining scope
x =5
if x >0:
print("x is positive") # This line is part of the 'if' block
print("Indentation groups statements")
else:
print("x is non-positive") # This line is part of the 'else' block
print("This line executes after the if/else block")
Immutable vs Mutable Objects
Python objects can be broadly classified based on whether their state can be changed after creation. Understanding this distinction is fundamental to how Python manages data and memory.
Immutable Objects
These objects cannot be changed after they are created. Any operation that appears to modify an immutable object actually creates a new object.
Numbers (int, float, complex)
Strings (str)
Tuples (tuple)
Frozen Sets (frozenset)
# Attempting to modify an immutable string
my_string ="Hello"
try:
my_string[0] ="J"# This will raise a TypeError
exceptTypeErroras e:
print(f"Error: {e}")
# Reassignment creates a *new* string object
my_string ="Jello"
print(my_string) # Output: Jello
Mutable Objects
These objects can be changed in place after they are created. Modifications affect the original object directly.
sorted_chars =sorted(chars) # Returns a new sorted list: ['a', 'b', 'c'] (Original unchanged)
# Other Useful Methods & Operations
numbers =[1, 2, 3, 1, 2, 1]
print(numbers.count(1)) # 3 (counts occurrences of 1)
print(numbers.index(2)) # 1 (index of first occurrence of 2)
print(min(numbers)) # 1
print(max(numbers)) # 3
print(sum(numbers)) # 10
print(2in numbers) # True (Membership check)
# List Comprehensions (Concise way to create lists)
squares =[x**2for x inrange(5)]# [0, 1, 4, 9, 16]
evens =[x for x inrange(10) if x %2==0]# [0, 2, 4, 6, 8]
print(squares)
print(evens)
# Copying Lists
original =[1, [2, 3]]
shallow_copy = original.copy() # Creates a new list, but nested objects are shared
import copy
deep_copy = copy.deepcopy(original) # Creates a new list and copies nested objects recursively
shallow_copy[1][0]=99
print(original) # [1, [99, 3]] (Nested list was affected!)
print(shallow_copy) # [1, [99, 3]]
# Reset original for deep copy demo
original =[1, [2, 3]]
deep_copy[1][0]=55
print(original) # [1, [2, 3]] (Nested list NOT affected)
print(deep_copy) # [1, [55, 3]]
Tuples (tuple)
Tuples are immutable, ordered sequences. They are often used for fixed collections of items or when data integrity is important (e.g., dictionary keys, returning multiple values from functions).
print(point *2) # (10, 20, 10, 20) (New tuple created)
# Immutable Nature
try:
point[0] =15# This will raise a TypeError
exceptTypeErroras e:
print(f"Error: {e}")
# Tuple Methods (Limited due to immutability)
numbers = (1, 2, 2, 3, 2)
print(numbers.count(2)) # 3
print(numbers.index(3)) # 3 (index of first occurrence)
# Tuple Unpacking
x, y = point # Assign elements to variables
print(f"x={x}, y={y}") # Output: x=10, y=20
# Returning Multiple Values from Functions
defget_coordinates():
return15, 25# Returns a tuple (15, 25)
lat, lon =get_coordinates()
print(f"Latitude: {lat}, Longitude: {lon}")
# Use Cases
# 1. Dictionary Keys (must be immutable)
locations = {(40.71, -74.00): "New York", (34.05, -118.24): "Los Angeles"}
# 2. Fixed Data Structures
rgb_color = (255, 0, 0) # Red
# Named Tuples for Readability
from collections import namedtuple
Point =namedtuple('Point',['x', 'y']) # Creates a Point class
p1 =Point(11,y=22)
print(p1.x, p1.y) # Access by name: 11 22
print(p1[0]) # Access by index: 11
Dictionaries (dict)
Dictionaries are mutable collections of key-value pairs. Keys must be unique and immutable (strings, numbers, tuples are common). Dictionaries are optimized for fast lookups by key. (Note: Dictionaries are insertion-ordered since Python 3.7).
# 'is' checks for identity (if two variables point to the exact same object in memory)
a =[1, 2, 3]
b =[1, 2, 3]
c = a
print(a == b) # True (values are the same)
print(a is b) # False (they are different objects in memory)
print(a is c) # True (c points to the same object as a)
# Use 'is' primarily for checking against None, True, False
config =None
if config isNone:
print("Configuration not set.")
# Pattern Matching (Python 3.10+)
http_status =404
match http_status:
case200|201:
print("Success")
case404:
print("Not Found")
case500:
print("Server Error")
case _: # Wildcard/Default case
print("Other status code")
Loops (for, while)
Repeat blocks of code multiple times. for loops iterate over sequences, while while loops continue as long as a condition is true.
# for loop: Iterating over sequences
fruits =['apple', 'banana', 'cherry']
print("Fruits:")
for fruit in fruits:
print(f"- {fruit}")
print("\nCharacters in 'Python':")
for char in"Python":
print(char,end='') # Prints P y t h o n
print()
# for loop with range()
print("\nNumbers 0 to 4:")
for i inrange(5): # Generates numbers from 0 up to (but not including) 5
print(i)
print("\nNumbers 2 to 8 (step 2):")
for i inrange(2,9,2): # Start, Stop (exclusive), Step
print(i)
# while loop: Executes as long as condition is True
count =0
print("\nWhile loop countdown:")
while count <3:
print(f"Count is {count}")
count +=1# Important: Update the condition variable
# Loop Control Statements: break and continue
print("\nLoop with break:")
for i inrange(10):
if i ==5:
print("Breaking loop")
break# Exit the loop immediately
print(i)
print("\nLoop with continue:")
for i inrange(5):
if i ==2:
print("Skipping iteration 2")
continue# Skip the rest of this iteration and go to the next
print(i)
# else block with loops
# The else block executes if the loop completes normally (i.e., not terminated by break)
print("\nLoop with else:")
for i inrange(3):
print(f"Iteration {i}")
else:
print("Loop finished normally.")
num =5
while num >0:
print(num)
num -=1
# if num == 2: break # If uncommented, the else block below would NOT run
else:
print("While loop finished normally.")
# Nested Loops
print("\nNested loops:")
for i inrange(2): # Outer loop
for j in['a', 'b']: # Inner loop
print(f"i={i}, j={j}")
# Iterating with enumerate (getting index and value)
print("\nEnumerate:")
for index, fruit inenumerate(fruits):
print(f"Index {index}: {fruit}")
# Iterating with zip (combining multiple iterables)
print("\nZip:")
names =['Alice', 'Bob']
scores =[95, 88]
for name, score inzip(names, scores):
print(f"{name} scored {score}")
Function Definitions
Functions are named blocks of reusable code that perform a specific task. They improve organization, readability, and maintainability.
# Basic function definition
defgreet(name):
"""This is a docstring explaining what the function does."""
message =f"Hello, {name}!"
return message
# Calling the function
greeting =greet("World")
print(greeting) # Output: Hello, World!
Docstrings
Docstrings ("""Docstring goes here""") are crucial for documentation. They are placed immediately after the def line and explain the function’s purpose, arguments, and return value.
defcalculate_area(length, width):
"""Calculates the area of a rectangle.
Args:
length (float or int): The length of the rectangle.
width (float or int): The width of the rectangle.
Returns:
float or int: The calculated area (length * width).
Returns None if inputs are invalid (e.g., negative).
"""
if length <0or width <0:
returnNone# Handle invalid input
return length * width
# Accessing the docstring
help(calculate_area)
# print(calculate_area.__doc__)
Function Parameters and Arguments
Python offers flexible ways to define and pass arguments:
Positional Arguments: Passed in order, matched by position.
Keyword Arguments: Passed using name=value, order doesn’t matter after positional args.
Default Argument Values: Provide a default if an argument isn’t passed.
Variable-Length Arguments (*args): Collects extra positional arguments into a tuple.
Variable-Keyword Arguments (**kwargs): Collects extra keyword arguments into a dictionary.
OOP uses classes and objects to model real-world entities and relationships, organizing code around data (attributes) and behaviors (methods).
Creating Classes
Define a class using the class keyword. The __init__ method is a special initializer (like a constructor) called when an object is created. self is the conventional name for the first parameter of instance methods, representing the instance itself.
classDog:
# Class attribute (shared by all instances)
species ="Canis familiaris"
def__init__(self, name, breed):
"""Initializer for the Dog class."""
# Instance attributes (unique to each instance)
self.name = name
self.breed = breed
self.tricks =[]# Each dog starts with an empty list of tricks
# Instance method
defbark(self):
"""Makes the dog bark."""
print(f"{self.name} says Woof!")
# Instance method
defadd_trick(self, trick):
"""Adds a trick to the dog's repertoire."""
self.tricks.append(trick)
print(f"{self.name} learned {trick}!")
# Creating instances (objects) of the Dog class
dog1 =Dog("Buddy","Golden Retriever")
dog2 =Dog("Lucy","Poodle")
# Accessing attributes and calling methods
print(f"{dog1.name} is a {dog1.breed}.") # Buddy is a Golden Retriever.
print(f"Species: {dog1.species}") # Species: Canis familiaris (accessing class attribute)
Allows a class (child/subclass) to inherit attributes and methods from another class (parent/superclass), promoting code reuse. Use super() to call methods from the parent class.
# Parent class
classAnimal:
def__init__(self, name):
self.name = name
defspeak(self):
raiseNotImplementedError("Subclass must implement abstract method")
# Child class inheriting from Animal
classCat(Animal):
def__init__(self, name, color):
# Call the parent's __init__ method
super().__init__(name)
self.color = color
# Override the speak method
defspeak(self):
returnf"{self.name} says Meow!"
# Another child class
classLoudDog(Dog): # Inherits from Dog (which has Animal's 'species')
Uses the `__init__` method for initialization. The `self` parameter must be explicitly included as the first argument in instance methods to refer to the instance.