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.
Key Characteristics
Interpreted: Code is executed line-by-line by an interpreter, simplifying debugging and development without a separate compilation step.
Dynamically Typed: Variable types are checked during runtime, offering flexibility but requiring careful testing.
High-Level: Abstracts away complex hardware details, allowing developers to focus on problem-solving.
Readability: Emphasizes clean syntax, often resembling plain English.
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
print ( " x is positive " ) # This line is part of the 'if' block
print ( " Indentation groups statements " )
print ( " x is non-positive " ) # This line is part of the 'else' block
print ( " This line executes after the if/else block " )
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[ 0 ] = " J " # This will raise a TypeError
# Reassignment creates a *new* string object
print ( my_string ) # Output: Jello
Mutable Objects
These objects can be changed in place after they are created. Modifications affect the original object directly.
Lists (list
)
Dictionaries (dict
)
Sets (set
)
Byte Arrays (bytearray
)
# Modifying a mutable list
print ( f "Original list: {my_list} , ID: { id ( my_list ) } " )
my_list[ 0 ] = 100 # Modifies the list in place
print ( f "Modified list: {my_list} , ID: { id ( my_list ) } " )
# Output shows the same ID, confirming in-place modification
Memory Model Visualization
The following diagram illustrates how references (variables) in the stack can point to objects stored in the heap. Note how multiple references (str1
, str2
in this example, if assigned the same immutable string) might point to the same object in memory for efficiency (string interning). Mutable objects generally get unique memory locations unless explicitly assigned.
═══════════════════════════════════
Stack (References) Heap (Objects)
┌──────────────────┐ ╔════════════════════╗
│ str1 ("Hello")───┼─────→ ║ "Hello" ║ ← (Interned/Cached)
│ str2 ("Hello")───┼─┐ ╚════════════════════╝
│ │ │ ╔════════════════════╗
│ list1 ([1,2])────┼─┼───→ ║ List Object ║
│ │ │ ╚════════════════════╝
│ │ │ ╔════════════════════╗
│ list2 ([1,2])────┼─┴───→ ║ List Object ║ ← (Different Object)
└──────────────────┘ ╚════════════════════╝
╔════════════════════════════════════════════════════╗
║ Memory Model Characteristics ║
╠════════════════════════════════════════════════════╣
║ • Objects stored in heap memory. ║
║ • References (names) stored in stack/frame memory. ║
║ • Multiple references can point to the same object.║
║ • Immutable objects (like small ints/strings) may ║
║ be cached/interned for efficiency. ║
║ • Memory managed by Python's garbage collector. ║
╚════════════════════════════════════════════════════╝
Python offers a rich set of built-in data types, each suited for different kinds of data and operations.
Numbers
Integers (int
), floating-point (float
), complex (complex
).
Example: 123
, 3.14
, 2+3j
Sequences
Ordered collections.
Strings (str
): Immutable sequences of characters. Example: "Hello"
Lists (list
): Mutable sequences, can hold mixed types. Example: [1, "hi", 3.0]
Tuples (tuple
): Immutable sequences. Example: (1, "hi", 3.0)
Mappings
Dictionaries (dict
): Mutable collections of unordered key-value pairs (ordered since Python 3.7). Example: {'name': 'Alice', 'age': 30}
Sets
Mutable, unordered collections of unique elements.
Sets (set
): Example: {1, 2, 3}
Frozen Sets (frozenset
): Immutable version. Example: frozenset([1, 2, 3])
Booleans
Logical values True
and False
. Internally represented as 1 and 0.
NoneType
Represents the absence of a value. The single value is None
.
Python supports integers, floating-point numbers, and complex numbers with a wide range of operations.
# Number Types & Basic Arithmetic
integer = 42 # int: whole numbers
float_num = 3.14 # float: decimal numbers
complex_num = 2 + 3 j # complex: real + imaginary
quotient = x / y # 3.3333... (float division)
floor_div = x // y # 3 (integer division)
remainder = x % y # 1 (modulus)
power = x ** y # 1000 (exponentiation)
# Built-in Number Functions & Conversions
print ( pow ( 2 , 3 )) # 8 (same as 2 ** 3)
print ( divmod ( 10 , 3 )) # (3, 1) (quotient and remainder)
print ( bin ( 10 )) # '0b1010' (binary)
print ( oct ( 10 )) # '0o12' (octal)
print ( hex ( 10 )) # '0xa' (hexadecimal)
print ( int ( ' 1010 ' , 2 )) # 10 (binary to decimal)
# Float Methods & Precision Issues
print ( round ( pi , 2 )) # 3.14
print ( float . is_integer ( 3.0 )) # True
print ( 0.1 + 0.2 ) # 0.30000000000000004 (potential precision issue)
# Math Module for Advanced Operations
print ( math. sqrt ( 16 )) # 4.0
print ( math. ceil ( 3.7 )) # 4
print ( math. floor ( 3.7 )) # 3
print ( math.pi ) # 3.141592653589793
print ( math. log ( 100 , 10 )) # 2.0
print ( abs ( z )) # 5.0 (magnitude)
print ( z. conjugate ()) # (3-4j)
# Decimal Module for High Precision
from decimal import Decimal, getcontext
getcontext ().prec = 30 # Set precision
print ( d1 + d2 ) # Decimal('0.3') - Precise!
print ( random. randint ( 1 , 10 )) # Random integer
print ( random. random ()) # Random float [0.0, 1.0)
Choosing Number Types
Use int
for whole numbers, float
for most decimal calculations, and Decimal
when high precision is critical (like financial applications). complex
is used in specific scientific and engineering domains.
Strings are immutable sequences of Unicode characters, essential for handling text.
# String Creation & Basic Operations
multi_line = ''' This is a
raw_string = r ' C: \p ath \t o \f ile ' # Backslashes are literal
concatenated = s1 + s2 + s3 # "Hello World"
repeated = s1 * 3 # "HelloHelloHello"
length = len ( concatenated ) # 11
print ( text [ 0 ]) # 'P' (first character)
print ( text [ - 1 ]) # 'n' (last character)
print ( text [ 1 : 4 ]) # 'yth' (slice from index 1 up to 4)
print ( text [ :: - 1 ]) # 'nohtyP' (reverse the string)
message = " Learn Python Programming! "
print ( message. upper ()) # " LEARN PYTHON PROGRAMMING! "
print ( message. lower ()) # " learn python programming! "
print ( message. strip ()) # "Learn Python Programming!" (remove leading/trailing whitespace)
print ( message. replace ( ' Python ' , ' Java ' )) # " Learn Java Programming! "
print ( message. strip () . split ( ' ' )) # ['Learn', 'Python', 'Programming!'] (split into words)
print ( filename. startswith ( ' report ' )) # True
print ( filename. endswith ( ' .pdf ' )) # False
print ( " 12345 " . isdigit ()) # True
print ( " Python " . isalpha ()) # True
# String Formatting (f-strings are recommended)
f_string = f "Name: { name } , Age: { age } " # Recommended: "Name: Alice, Age: 30"
format_method = " Name: {} , Age: {} " . format ( name , age ) # "Name: Alice, Age: 30"
percent_format = " Name: %s , Age: %d " % (name, age) # Older style: "Name: Alice, Age: 30"
words = [ ' Python ' , ' is ' , ' fun ' ]
sentence = " " . join ( words ) # "Python is fun"
csv_data = " apple,banana,cherry "
items = csv_data. split ( ' , ' ) # ['apple', 'banana', 'cherry']
# Searching within Strings
text = " Python is easy, Python is powerful "
print ( text. find ( ' Python ' )) # 0 (index of first occurrence)
print ( text. rfind ( ' Python ' )) # 17 (index of last occurrence)
print ( text. count ( ' Python ' )) # 2
print ( ' easy ' in text ) # True
Note
Strings are immutable. Methods like upper()
, replace()
, strip()
do not change the original string; they return a new modified string.
Lists are mutable, ordered sequences capable of holding items of different data types. They are highly versatile and commonly used.
numbers = [ 1 , 2 , 3 , 4 , 5 ]
mixed = [ 1 , " hello " , 3.14 , True , None ]
nested = [[ 1 , 2 ] , [ 3 , 4 ]]
list_from_range = list ( range ( 5 )) # [0, 1, 2, 3, 4]
# Basic Operations & Indexing/Slicing (Similar to Strings)
nums = [ 10 , 20 , 30 , 40 , 50 ]
print ( nums [ 1 : 3 ]) # [20, 30]
print ( nums + [ 60 , 70 ] ) # [10, 20, 30, 40, 50, 60, 70] (Concatenation)
print ( nums * 2 ) # [10, 20, 30, 40, 50, 10, 20, 30, 40, 50] (Repetition)
# Modifying Lists (Mutable)
nums[ 0 ] = 100 # Replace item at index 0
print ( nums ) # [100, 20, 30, 40, 50]
nums. append ( 60 ) # Add item to end: [100, 20, 30, 40, 50, 60]
nums. insert ( 1 , 15 ) # Insert 15 at index 1: [100, 15, 20, 30, 40, 50, 60]
nums. extend ( [ 70 , 80 ] ) # Add multiple items from iterable: [... 60, 70, 80]
popped_item = nums. pop () # Remove and return last item (80)
removed_item = nums. pop ( 1 ) # Remove and return item at index 1 (15)
nums. remove ( 30 ) # Remove first occurrence of value 30
print ( nums ) # [100, 20, 40, 50, 60, 70]
chars. sort () # Sorts in-place: ['a', 'b', 'c']
chars. reverse () # Reverses in-place: ['c', 'b', 'a']
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 ( 2 in numbers ) # True (Membership check)
# List Comprehensions (Concise way to create lists)
squares = [ x ** 2 for x in range ( 5 ) ] # [0, 1, 4, 9, 16]
evens = [ x for x in range ( 10 ) if x % 2 == 0 ] # [0, 2, 4, 6, 8]
shallow_copy = original. copy () # Creates a new list, but nested objects are shared
deep_copy = copy. deepcopy ( original ) # Creates a new list and copies nested objects recursively
print ( original ) # [1, [99, 3]] (Nested list was affected!)
print ( shallow_copy ) # [1, [99, 3]]
# Reset original for deep copy demo
print ( original ) # [1, [2, 3]] (Nested list NOT affected)
print ( deep_copy ) # [1, [55, 3]]
Shallow vs. Deep Copy
Be mindful when copying lists containing mutable objects (like other lists). A shallow copy (.copy()
or [:]
) only copies references to nested objects, while copy.deepcopy()
creates fully independent copies.
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).
single_item_tuple = ( 1 ,) # Comma is necessary!
point = ( 10 , 20 ) # Represents coordinates
mixed_tuple = ( 1 , ' hello ' , 3.14 )
# Basic Operations & Indexing/Slicing (Read-only)
print ( point + ( 30 , 40 ) ) # (10, 20, 30, 40) (New tuple created)
print ( point * 2 ) # (10, 20, 10, 20) (New tuple created)
point[ 0 ] = 15 # This will raise a TypeError
# 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)
x, y = point # Assign elements to variables
print ( f "x= {x} , y= {y} " ) # Output: x=10, y=20
# Returning Multiple Values from Functions
return 15 , 25 # Returns a tuple (15, 25)
lat, lon = get_coordinates ()
print ( f "Latitude: {lat} , Longitude: {lon} " )
# 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
print ( p1.x , p1.y ) # Access by name: 11 22
print ( p1 [ 0 ]) # Access by index: 11
Tip
Tuples are generally slightly more memory-efficient and faster to iterate over than lists, but their primary advantage is immutability, ensuring data doesn’t change unexpectedly.
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).
student = { ' name ' : ' Alice ' , ' age ' : 25 , ' major ' : ' Computer Science ' }
person = dict ( name = ' Bob ' , age = 30 ) # Using dict() constructor
from_pairs = dict ( [ ( ' city ' , ' Paris ' ), ( ' country ' , ' France ' ) ] )
print ( student [ ' name ' ]) # 'Alice' (Raises KeyError if key not found)
print ( student. get ( ' age ' )) # 25 (Returns None if key not found)
print ( student. get ( ' gpa ' , ' N/A ' )) # 'N/A' (Returns default value if key not found)
student[ ' age ' ] = 26 # Update existing value
student[ ' gpa ' ] = 3.8 # Add new key-value pair
del student[ ' major ' ] # Remove by key (Raises KeyError if not found)
gpa = student. pop ( ' gpa ' ) # Remove by key and return value (Raises KeyError)
item = student. popitem () # Remove and return an arbitrary (last in 3.7+) item (key, value)
print ( student ) # {'name': 'Alice', 'age': 26}
print ( f "Popped GPA: {gpa} , Popped item: {item} " )
# Getting Keys, Values, and Items (Views)
keys = student. keys () # dict_keys(['name', 'age']) - Dynamic view
values = student. values () # dict_values(['Alice', 26]) - Dynamic view
items = student. items () # dict_items([('name', 'Alice'), ('age', 26)]) - Dynamic view
# Iterating over Dictionaries
# Iterate over keys (default)
print ( f "Key: {key} , Value: {student [ key ] } " )
# Iterate over key-value pairs (recommended)
for key, value in student. items ():
print ( f " {key} -> {value} " )
for value in student. values ():
print ( ' name ' in student ) # True (Checks keys)
print ( ' Alice ' in student. values ()) # True (Checks values)
# Method 1: update() modifies dict1
# dict1.update(dict2) # dict1 becomes {'a': 1, 'b': 3, 'c': 4}
# Method 2: Dictionary unpacking (Python 3.5+) creates new dict
merged = { ** dict1, ** dict2} # {'a': 1, 'b': 3, 'c': 4} (Later key 'b' wins)
# Method 3: Union operator (Python 3.9+) creates new dict
merged_union = dict1 | dict2 # {'a': 1, 'b': 3, 'c': 4}
# Dictionary Comprehensions
squares = {x: x ** 2 for x in range ( 5 )} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
' user1 ' : { ' name ' : ' John ' , ' city ' : ' New York ' },
' user2 ' : { ' name ' : ' Jane ' , ' city ' : ' London ' }
print ( users [ ' user1 ' ] [ ' city ' ] ) # New York
Python provides robust tools for reading from and writing to files.
# Using 'with' statement (Recommended: ensures file is closed automatically)
file_path = ' my_file.txt '
# Writing to a file (creates or overwrites)
with open ( file_path , ' w ' , encoding = ' utf-8 ' ) as f:
f. write ( " Hello from Python! \n " )
f. write ( " This is the second line. \n " )
lines_to_write = [ " Third line. \n " , " Fourth line. \n " ]
f. writelines ( lines_to_write )
print ( f "Successfully wrote to {file_path} " )
print ( f "Error writing file: {e} " )
with open ( file_path , ' r ' , encoding = ' utf-8 ' ) as f:
# Method 1: Read entire content
# print("Full content:\n", content)
# Method 2: Read line by line (memory efficient for large files)
print ( f " \n Reading {file_path} line by line:" )
print ( line. strip ()) # strip() removes leading/trailing whitespace (like newline)
# Method 3: Read all lines into a list
# f.seek(0) # Go back to the beginning of the file
# all_lines = f.readlines()
# print("\nAll lines as list:", all_lines)
except FileNotFoundError :
print ( f "Error: File ' {file_path} ' not found." )
print ( f "Error reading file: {e} " )
with open ( file_path , ' a ' , encoding = ' utf-8 ' ) as f:
f. write ( " This line was appended. \n " )
print ( f " \n Successfully appended to {file_path} " )
print ( f "Error appending to file: {e} " )
# 'w' : Write (truncates file if exists, creates if not)
# 'a' : Append (adds to end, creates if not)
# 'x' : Exclusive creation (fails if file exists)
# 'b' : Binary mode (for non-text files like images, executables)
# 't' : Text mode (default)
# '+' : Update (allows reading and writing, e.g., 'r+', 'w+', 'a+')
# Working with Binary Files
image_path = ' logo.png ' # Assume this exists
output_path = ' logo_copy.png '
with open ( image_path , ' rb ' ) as infile, open ( output_path , ' wb ' ) as outfile:
chunk_size = 4096 # Read in chunks
chunk = infile. read ( chunk_size )
print ( f " \n Copied binary file to {output_path} " )
except FileNotFoundError :
print ( f "Error: Binary file ' {image_path} ' not found." )
print ( f "Error handling binary file: {e} " )
# os module for file system operations
if os.path. exists ( file_path ):
print ( f " \n File ' {file_path} ' exists." )
print ( f "File size: {os.path. getsize ( file_path ) } bytes" )
# os.rename(file_path, 'new_name.txt') # Rename
# os.remove('new_name.txt') # Delete
print ( f "File ' {file_path} ' does not exist." )
Best Practice: `with` statement
Always use the with open(...) as ...:
syntax. It automatically handles closing the file, even if errors occur within the block, preventing resource leaks.
Control the flow of execution based on whether conditions are true or false.
# Basic if-elif-else structure
grade = ' C ' # This condition is met
print ( f "Score: {score} , Grade: {grade} " ) # Output: Score: 75, Grade: C
# Boolean Logic: and, or, not
if age >= 18 and has_ticket:
if is_weekend or is_holiday:
print ( " Enjoy your day off! " )
# Truth Value Testing (Falsy vs Truthy)
# Falsy values: False, None, 0, 0.0, '', [], (), {}, set()
# All other values are Truthy.
print ( " List has items. " ) # This won't print
print ( " List is empty. " ) # This will print
print ( f "Name is: {name} " ) # This will print
# Ternary Conditional Operator (Conditional Expression)
status = " Adult " if age >= 18 else " Minor "
print ( f "Age: {age} , Status: {status} " ) # Output: Age: 25, Status: Adult
# Comparing Identity (is vs ==)
# '==' checks for equality of value
# 'is' checks for identity (if two variables point to the exact same object in memory)
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
print ( " Configuration not set. " )
# Pattern Matching (Python 3.10+)
case _: # Wildcard/Default case
print ( " Other status code " )
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 ( " \n Characters in 'Python': " )
print ( char , end = ' ' ) # Prints P y t h o n
print ( " \n Numbers 0 to 4: " )
for i in range ( 5 ): # Generates numbers from 0 up to (but not including) 5
print ( " \n Numbers 2 to 8 (step 2): " )
for i in range ( 2 , 9 , 2 ): # Start, Stop (exclusive), Step
# while loop: Executes as long as condition is True
print ( " \n While loop countdown: " )
print ( f "Count is {count} " )
count += 1 # Important: Update the condition variable
# Loop Control Statements: break and continue
print ( " \n Loop with break: " )
break # Exit the loop immediately
print ( " \n Loop with continue: " )
print ( " Skipping iteration 2 " )
continue # Skip the rest of this iteration and go to the next
# The else block executes if the loop completes normally (i.e., not terminated by break)
print ( " \n Loop with else: " )
print ( " Loop finished normally. " )
# if num == 2: break # If uncommented, the else block below would NOT run
print ( " While loop finished normally. " )
for i in range ( 2 ): # Outer loop
for j in [ ' a ' , ' b ' ] : # Inner loop
# Iterating with enumerate (getting index and value)
for index, fruit in enumerate ( fruits ):
print ( f "Index {index} : {fruit} " )
# Iterating with zip (combining multiple iterables)
for name, score in zip ( names , scores ):
print ( f " {name} scored {score} " )
Functions are named blocks of reusable code that perform a specific task. They improve organization, readability, and maintainability.
# Basic function definition
""" This is a docstring explaining what the function does. """
message = f "Hello, { name } !"
greeting = greet ( " World " )
print ( greeting ) # Output: Hello, World!
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.
def calculate_area ( length , width ) :
""" Calculates the area of a rectangle.
length (float or int): The length of the rectangle.
width (float or int): The width of the rectangle.
float or int: The calculated area (length * width).
Returns None if inputs are invalid (e.g., negative).
if length < 0 or width < 0 :
return None # Handle invalid input
# Accessing the docstring
# print(calculate_area.__doc__)
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.
def process_data ( required_arg , default_arg= " default_value " , *args , **kwargs ) :
""" Demonstrates different parameter types. """
print ( f "Required: {required_arg} " )
print ( f "Default: {default_arg} " )
print ( f "Extra positional (*args): {args} " ) # Tuple
print ( f "Extra keyword (**kwargs): {kwargs} " ) # Dictionary
# Extra positional (*args): ()
# Extra keyword (**kwargs): {}
process_data ( 200 , " custom " , 1 , 2 , 3 , key1 = " value1 " , key2 = " value2 " )
# Extra positional (*args): (1, 2, 3)
# Extra keyword (**kwargs): {'key1': 'value1', 'key2': 'value2'}
process_data ( required_arg = 300 , key3 = " value3 " )
# Extra positional (*args): ()
# Extra keyword (**kwargs): {'key3': 'value3'}
Small, one-line functions can be created using lambda
. Useful for short operations, often with functions like map()
, filter()
, sorted()
.
# Lambda function to add two numbers
print ( add ( 5 , 3 )) # Output: 8
# Using lambda with sorted()
points = [ ( 1 , 5 ), ( 3 , 2 ), ( 2 , 8 ) ]
# Sort points based on the second element (y-coordinate)
points_sorted_by_y = sorted ( points , key =lambda point : point [ 1 ])
print ( points_sorted_by_y ) # Output: [(3, 2), (1, 5), (2, 8)]
`help()` Function
Use the built-in help(function_name)
to view the docstring and signature of any function, whether built-in or user-defined. It’s a great way to quickly understand how to use a function.
OOP uses classes and objects to model real-world entities and relationships, organizing code around data (attributes) and behaviors (methods).
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.
# 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 .tricks = [] # Each dog starts with an empty list of tricks
""" Makes the dog bark. """
print ( f " { self .name} says Woof!" )
def add_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)
dog1. bark () # Buddy says Woof!
dog2. bark () # Lucy says Woof!
print ( f " {dog1.name} 's tricks: {dog1.tricks} " ) # Buddy's tricks: ['fetch']
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.
def __init__ ( self , name ) :
raise NotImplementedError ( " Subclass must implement abstract method " )
# Child class inheriting from Animal
def __init__ ( self , name , color ) :
# Call the parent's __init__ method
# Override the speak method
return f " { self .name } says Meow!"
class LoudDog ( Dog ): # Inherits from Dog (which has Animal's 'species')
def __init__ ( self , name , breed , volume ) :
super (). __init__ ( name , breed ) # Call Dog's __init__
# Override the bark method
print ( f " { self .name} says WOOF! loudly (Volume: { self .volume} )" )
# Add a new method specific to LoudDog
print ( f "I am { self .name} , the { self .breed} !" )
my_cat = Cat ( " Whiskers " , " grey " )
print ( my_cat. speak ()) # Whiskers says Meow!
loud_buddy = LoudDog ( " Buddy " , " Golden Retriever " , 11 )
loud_buddy. bark () # Buddy says WOOF! loudly (Volume: 11)
loud_buddy. add_trick ( " roll over " )
print ( f " {loud_buddy.name} 's tricks: {loud_buddy.tricks} " )
While both languages support OOP, their syntax and conventions differ, particularly around object initialization and instance reference.
JavaScript (`constructor`, `this`)
Uses the constructor
keyword for initialization. The this
keyword implicitly refers to the current instance within methods.
constructor ( make , model ) {
this . make = make ; // 'this' refers to the new object
console . log ( ` Accelerating to ${ this . speed } km/h ` );
const myCar = new Car ( " Toyota " , " Camry " );
myCar . accelerate ( 50 ); // Output: Accelerating to 50 km/h
<Card title="Python ( ` __init__ ` , ` self ` )" icon="python">
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.
def __init__ ( self , make , model ):
self . make = make # ' self ' is the explicit reference
def accelerate ( self , amount ):
print ( f " Accelerating to { self . speed } km / h " )
my_car = Car ( " Toyota " , " Camry " )
my_car . accelerate ( 50 ) # Output: Accelerating to 50 km / h
Key Differences Summary
Initializer: constructor
(JS) vs. __init__
(Python).
Instance Reference: Implicit this
(JS) vs. Explicit self
(Python). self
is a convention, but strongly followed; it must be the first parameter in instance methods.