Dunder (double underscore) or magic methods are special methods in Python that allow for customization of classes and objects. These methods are called magic because they can change the behavior of code in unexpected ways. Understanding and implementing these methods can greatly enhance the functionality and flexibility of your Python programs.
Constructing Objects and Expressions
In Python, objects are instances of classes, which define the attributes and methods of the object. The process of creating an object in Python involves defining a class, which specifies the structure and behavior of the object, and then creating instances of that class.
Defining Classes in Python
To define a class in Python, you use the class
keyword, followed by the name of the class. For example, the following code defines a simple class called Person
:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f"Hello, my name is {self.name} and I am {self.age} years old.")
The __init__
method is a special method that is called when an instance of the class is created. It initializes the attributes of the object.
Python __init__
Magic Method
The __init__
method is a special magic method that is called when an instance of a class is created. It initializes the attributes of the object. In the example above, the __init__
method takes two parameters, name
and age
, which are used to initialize the name
and age
attributes of the object.
Creating Instances of Classes in Python
To create an instance of a class, you call the class as if it were a function, passing any arguments that the __init__
dunder method requires. For example, the following code creates two instances of the Person
class:
# Defining a car class
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def describe_car(self):
print(f"The car is a {self.year} {self.make} {self.model}.")
# Creating an instance of Car class
car1 = Car("Honda", "Accord", 2021)
# Calling the describe_car method
car1.describe_car()
# Output: The car is a 2021 Honda Accord.
# Defining a book class
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
def describe_book(self):
print(f"The book '{self.title}' is written by {self.author} and has {self.pages} pages.")
# Creating an instance of Book class
book1 = Book("The Alchemist", "Paulo Coelho", 208)
# Calling the describe_book method
book1.describe_book()
# Output: The book 'The Alchemist' is written by Paulo Coelho and has 208 pages.
Creating Iterator Objects
An iterator is an object that enables sequential iteration (looping) over a collection of items, one item at a time. In Python, you can create iterator objects using classes or functions.
Python Generator Class
You can create an iterator using the generator class in Python. The generator class is a type of object that is used to create iterable objects using the yield
statement.
class MyGenerator:
def __init__(self):
self.num = 0
def __iter__(self):
return self
def __next__(self):
if self.num <= 5:
value = self.num
self.num += 1
return value
else:
raise StopIteration
def my_generator():
num = 0
while num <= 5:
yield num
num += 1
# Using the generator class
gen = MyGenerator()
for x in gen:
print(x)
# Using the function generator
gen = my_generator()
for x in gen:
print(x)
In this example, MyGenerator
is a generator class that inherits from the built-in object
class. It defines an __init__()
method that initializes the num
attribute to 0. It also defines __iter__()
method that returns the iterator object (self
in this case) and __next__()
magic method that generates the next value in the sequence.
You can also create an iterator using a Python function generator. A function generator is afunction that contains the yield
statement.
In this example, my_generator
function is a function generator that uses the yield
statement to generate the next value in the sequence.
In both examples above, you can create an iterator object as follows:
Both code examples will output the values 0
, 1
, 2
, 3
, 4
, and 5
when iterated over.
Handling Attribute References
Attribute references are used to access attributes of an object in Python. They can be accessed using the dot notation syntax and can also be accessed dynamically using the getattr()
function.
The getattr()
function takes two arguments - the object whose attribute needs to be accessed and the name of the attribute as a string. If the attribute is not found, an AttributeError
is raised.
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
my_dog = Dog("Max", "German Shepherd")
print(my_dog.name) ### Output
my_cat = {"name": "Fluffy", "breed": "Persian"}
cat_name = getattr(my_cat, "name")
print(cat_name) ### Output
In first case, we create a Dog
class and access the name
attribute using the dot notation syntax.
In second case, we create a dictionary object my_cat
and access the name
attribute dynamically using the getattr()
function. We store the value of the attribute in cat_name
and print it out.
Representing Objects as Strings with Magic Method
In Python, we can represent objects as string using the __repr__()
dunder method. This method is called when we use the repr()
function or when we print an object using print()
function.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
p = Point(2, 3)
print(p) ### Output
In the above code, we have defined a Point
class with x
and y
attributes. We have also defined a __repr__()
dunder method that returns a string representation of the Point
object. When we print the p
object, it calls the __repr__()
magic method to get its string representation.
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def __repr__(self):
return f"Car(make={self.make}, model={self.model}, year={self.year})"
c = Car("Toyota", "Camry", 2021)
print(c) ### Output
In this example, we have defined a Car
class with make
, model
, and year
attributes. We have also defined a __repr__()
method that returns a string representation of the Car
object. When we print the c
object, it calls the __repr__()
dunder method to get its string representation.
Cleaning Up Objects with Dunder Method
In Python, objects are automatically garbage collected when they are no longer needed. However, sometimes it may be necessary to define additional cleanup actions for an object. This can be done using the __del__
method, which is called when the object is about to be destroyed.
This dunder method is useful for releasing resources such as files, network connections, or other system-level objects that are not automatically managed by Python.
class MyClass:
def __init__(self):
self.file = open('example.txt', 'r')
def __del__(self):
self.file.close()
In this example, the MyClass
constructor creates a file object and stores it in the file
instance variable. When the object is destroyed, the __del__
method is called, which closes the file.
Performing Comparisons with Dunder Methods
Python provides multiple ways to compare values, variables, or expressions. Some commonly used operators for performing comparisons include ==
, !=
, >
, <
, >=
, <=
, in
, and is
.
Python Compare Strings
The __lt__()
method is used to implement the less than comparison operator in Python. It returns True
if the first string is less than the second string and False
otherwise.
string1 = "apple"
string2 = "banana"
if string1.__lt__(string2):
print("string1 is less than string2")
else:
print("string1 is greater than or equal to string2")
# Output:
#string1 is less than string2
fruits = ["apple", "banana", "orange", "kiwi"]
sorted_fruits = sorted(fruits, key=lambda x: x.__lt__("c"))
print(sorted_fruits)
# Output:
# ['orange', 'kiwi', 'apple', 'banana']
In the above example, we have sorted the list of fruits in ascending order based on whether the first character of each string is less than or greater than c
. lambda x: x.__lt__(
c)
returns True
if the first character of x
is less than c
and False
otherwise.
Contribute with us!
Do not hesitate to contribute to Python tutorials on GitHub: create a fork, update content and issue a pull request.