Objects in Python
An object is an instance of a class. It’s a concrete entity that has state (attributes) and behavior (methods). In Python, everything is an object - from simple data types like integers and strings to complex user-defined classes.
Creating Objects
Objects are created by calling a class (instantiation):
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f"Hello, I'm {self.name}"
# Creating objects (instances)
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)
print(person1.greet()) # Hello, I'm Alice
print(person2.greet()) # Hello, I'm Bob
Object Identity and References
Every object has a unique identity and can be referenced:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# Creating objects
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = p1 # p3 references the same object as p1
print(id(p1)) # Unique identity (memory address)
print(id(p2)) # Different identity
print(id(p3)) # Same as p1's identity
print(p1 is p2) # False (different objects)
print(p1 is p3) # True (same object)
print(p1 == p2) # False (unless __eq__ is defined)
Object Attributes
Instance Attributes
- Belong to specific instances
- Stored in
__dict__
class Car:
def __init__(self, brand, model):
self.brand = brand # Instance attribute
self.model = model # Instance attribute
self.speed = 0 # Instance attribute
car1 = Car("Toyota", "Camry")
car2 = Car("Honda", "Civic")
print(car1.brand) # Toyota
print(car2.brand) # Honda
# Adding attributes dynamically
car1.color = "Red"
print(car1.color) # Red
# Viewing all attributes
print(car1.__dict__) # {'brand': 'Toyota', 'model': 'Camry', 'speed': 0, 'color': 'Red'}
Class Attributes
- Shared among all instances
- Accessed through class or instance
class Student:
school = "Python University" # Class attribute
def __init__(self, name, grade):
self.name = name # Instance attribute
self.grade = grade # Instance attribute
student1 = Student("Alice", "A")
student2 = Student("Bob", "B")
print(student1.school) # Python University
print(student2.school) # Python University
print(Student.school) # Python University
# Modifying class attribute affects all instances
Student.school = "New Python University"
print(student1.school) # New Python University
print(student2.school) # New Python University
Object Methods
Instance Methods
- Operate on instance data
- Take
selfas first parameter
class BankAccount:
def __init__(self, balance):
self.balance = balance
def deposit(self, amount):
self.balance += amount
return f"Deposited ${amount}. Balance: ${self.balance}"
def withdraw(self, amount):
if self.balance >= amount:
self.balance -= amount
return f"Withdrew ${amount}. Balance: ${self.balance}"
return "Insufficient funds"
def get_balance(self):
return self.balance
account = BankAccount(1000)
print(account.deposit(500)) # Deposited $500. Balance: $1500
print(account.withdraw(200)) # Withdrew $200. Balance: $1300
print(account.get_balance()) # 1300
Method Binding
- Methods are bound to instances
selfis automatically passed
class Calculator:
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
calc = Calculator()
# These are equivalent:
result1 = calc.add(5, 3)
result2 = Calculator.add(calc, 5, 3)
print(result1) # 8
print(result2) # 8
Object Introspection
Python provides tools to examine objects at runtime:
class Example:
class_var = "I'm a class variable"
def __init__(self, value):
self.instance_var = value
def method(self):
return "I'm a method"
obj = Example("test")
# Type checking
print(type(obj)) # <class '__main__.Example'>
print(isinstance(obj, Example)) # True
# Attribute inspection
print(hasattr(obj, 'instance_var')) # True
print(hasattr(obj, 'nonexistent')) # False
# Getting attributes
print(getattr(obj, 'instance_var')) # test
print(getattr(obj, 'nonexistent', 'default')) # default
# Setting attributes
setattr(obj, 'new_attr', 'new_value')
print(obj.new_attr) # new_value
# Listing attributes
print(dir(obj)) # List all attributes and methods
print(obj.__dict__) # Dictionary of instance attributes
Object Lifecycle
Creation
class LifecycleExample:
def __new__(cls, *args, **kwargs):
print("1. __new__ called - creating instance")
return super().__new__(cls)
def __init__(self, value):
print("2. __init__ called - initializing instance")
self.value = value
def __del__(self):
print("3. __del__ called - destroying instance")
obj = LifecycleExample("test")
# Output:
# 1. __new__ called - creating instance
# 2. __init__ called - initializing instance
del obj
# Output: 3. __del__ called - destroying instance
Garbage Collection
- Python automatically manages memory
- Objects are destroyed when no references remain
import gc
class MemoryExample:
def __init__(self, name):
self.name = name
def __del__(self):
print(f"Destroying {self.name}")
# Creating and destroying objects
obj1 = MemoryExample("Object 1")
obj2 = MemoryExample("Object 2")
# Removing references
del obj1
del obj2
# Force garbage collection
gc.collect()
Object Relationships
Composition (Has-A)
class Engine:
def __init__(self, horsepower):
self.horsepower = horsepower
def start(self):
return "Engine started"
class Car:
def __init__(self, brand, engine_hp):
self.brand = brand
self.engine = Engine(engine_hp) # Composition
def start_car(self):
return f"{self.brand} car: {self.engine.start()}"
car = Car("Toyota", 200)
print(car.start_car()) # Toyota car: Engine started
Aggregation (Uses-A)
class Student:
def __init__(self, name):
self.name = name
self.courses = [] # Aggregation
def enroll(self, course):
self.courses.append(course)
def get_courses(self):
return [course.name for course in self.courses]
class Course:
def __init__(self, name):
self.name = name
student = Student("Alice")
course1 = Course("Python Programming")
course2 = Course("Data Structures")
student.enroll(course1)
student.enroll(course2)
print(student.get_courses()) # ['Python Programming', 'Data Structures']
Object Comparison
Identity vs Equality
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Point):
return False
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = p1
print(p1 is p2) # False (different objects)
print(p1 is p3) # True (same object)
print(p1 == p2) # True (same values)
print(p1 == p3) # True (same object)
Object Serialization
Pickle (Python’s built-in serialization)
import pickle
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person(name={self.name}, age={self.age})"
# Creating object
person = Person("Alice", 30)
# Serializing to file
with open('person.pkl', 'wb') as f:
pickle.dump(person, f)
# Deserializing from file
with open('person.pkl', 'rb') as f:
loaded_person = pickle.load(f)
print(loaded_person) # Person(name=Alice, age=30)
Summary Table
| Concept | Description | Example |
|---|---|---|
| Object Creation | obj = ClassName() |
person = Person("Alice", 30) |
| Object Identity | id(obj) |
id(person) |
| Object Reference | obj1 is obj2 |
person1 is person2 |
| Instance Attributes | obj.attribute |
person.name |
| Class Attributes | ClassName.attribute |
Person.species |
| Method Call | obj.method() |
person.greet() |
| Attribute Check | hasattr(obj, 'attr') |
hasattr(person, 'name') |
| Object Destruction | del obj |
del person |
Key Interview Points
- Everything in Python is an object - including integers, strings, functions
- Objects have identity, type, and value
selfrefers to the instance in methods- Objects are created by calling classes
- Instance attributes belong to specific objects
- Class attributes are shared among all instances
- Object lifecycle includes creation, usage, and destruction
- Garbage collection automatically manages memory
- Object relationships include composition and aggregation
- Object comparison can be identity-based (
is) or value-based (==)
Understanding objects is crucial for Python programming and object-oriented design!
Interview angle
- “
isversus==?” —iscompares identity (same object in memory),==compares value via__eq__. Useisonly for singletons:None,True,False. Small-int and string interning makesisappear to work on values, which is exactly why it produces bugs that pass in testing. - “Are Python arguments passed by value or reference?” — neither exactly; it’s pass-by-object-reference. The function receives a reference to the same object, so mutating a list argument is visible to the caller, but rebinding the name inside the function is not.
- “What does
id()tell you?” — the object’s identity for its lifetime. It can be reused after garbage collection, so comparing storedid()values across time is unsound. - “How does attribute lookup work?” — instance
__dict__, then the class and its MRO, with data descriptors taking precedence over the instance dict, and__getattr__as the fallback when everything else fails. See 08_descriptors_in_python.md.