class Dog:
species = "Canis familiaris" # Class attribute
def __init__(self, name):
self.name = name # Instance attribute
self
in __init__
class Employee:
company = "TechCorp" # Shared constant
next_id = 1000 # Shared counter
def __init__(self, name):
self.name = name
self.id = Employee.next_id
Employee.next_id += 1
# Usage
emp1 = Employee("Alice") # ID: 1000
emp2 = Employee("Bob") # ID: 1001
__del__
class FileHandler:
def __init__(self, filename):
self.file = open(filename, 'w')
print(f"Opened {filename}")
def __del__(self):
self.file.close()
print("File closed safely")
# Automatic cleanup when object is destroyed
handler = FileHandler("data.txt")
# File automatically closed when handler is garbage collected
__del__
?Timing is unpredictable (handled by garbage collector)
Better Alternative: Context Managers (with
statement) are preferred for resource management!
__del__
Called?__del__
class Example:
def __init__(self, name):
print(f"Example instance created")
self.name = name
def __del__(self):
print(f"{self.name} is being destroyed")
# Cases when __del__ is called
obj = Example("Object1")
obj = None # Reference count = 0, __del__ called
__del__
is called when the function exits
def create_temp():
temp = Example("TempObject")
# __del__ called when function ends
create_temp() # TempObject destroyed here
with
Statementπ βdel is not deprecated, but itβs generally considered bad practice. Use context managers (with / enter, exit) or try/finally for resource cleanup instead.β
with
statement__enter__
and __exit__
__del__
class DatabaseConnection:
def __enter__(self):
print("Opening connection")
# Setup code here
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Closing connection")
# Cleanup happens even if error occurs
# Usage
with DatabaseConnection() as db:
# Use the connection
pass # Connection auto-closed after this block
π Dunder methods (a.k.a. magic methods)
In Python, dunder methods are special methods with double underscores (e.g. __len__
, __str__
, __add__
) that let your objects interact with built-in functions and operators.
Dunder methods define how your class behaves in the Python ecosystem (e.g. len(obj) β obj.__len__()
, str(obj) β obj.__str__()
).
https://realpython.com/python-magic-methods/
Using len or any other dunder method is mostly for convenience and interoperability because it is standard across many object types.
class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __str__(self):
return f"Book: {self.title}" # Human-readable
def __repr__(self):
return f"Book('{self.title}', {self.pages})" # Debug info
def __len__(self):
return self.pages # len(book)
def __eq__(self, other):
return self.title == other.title # book1 == book2
def __getitem__(self, page):
return f"Content of page {page}" # book[5]
# Usage
book = Book("Python Guide", 300)
print(str(book)) # "Book: Python Guide"
print(len(book)) # 300
print(book[10]) # "Content of page 10"
def __len__
vs .len()
__len__
dunder method
len(obj)
β
list
, str
, dict
, array
, β¦if obj:
calls __len__
if __bool__
missing)class Playlist:
def __len__(self): return 300
len(Playlist()) # 300
.len()
regular instance method
obj.len()
class Playlist:
def len(self): return 300
Playlist().len() # 300
len(Playlist()) # TypeError
π Takeaway: Use __len__
to make your class a first-class Python container.