Decoding Python Magic: __contains__ and __call__
Let’s enhance our understanding of Python by diving into the __contains__
and __call__.
Hello everyone, thank you for reading my other articles in the “Decoding Python Magic Series.” It’s truly refreshing to write about these topics. Today, let’s cover the next two magic methods, which most of us do not know.
__contains__
__contains__ simply give power to collections which allow in operator to be applied on them.
l = [1, 2, 3, 4, 5]
(1 in l) is (l.__contains__(1))
in
operator calls the__contains__
internally.
Odd Number Container
from collections.abc import Container
class OddContainer(Container):
def __contains__(self, x):
if not isinstance(x, int) or not x % 2:
return False
return True
odd_container = OddContainer()
print(1 in odd_container) # True
print(2 in odd_container) # False
print(21111111112 in odd_container) # False
print("hello" in odd_container) # False
Fruit Bucket
from collections import UserList
class FruitBucker(UserList):
def add(self, fruit):
self.data.append(fruit)
def __contains__(self, item):
return item in self.data
bucket = FruitBucker(["apple", "banana", "orange"])
print("apple" in bucket) # True
print("mango" in bucket) # False
bucket.add("mango")
print("mango" in bucket) # True
__call__
The __call__
method in Python allows an object to be called as if it were a function. This method can be defined in a class to enable instances of that class to be callable. When you use the ()
operator on an object instance, Python internally calls the __call__
method of that object.
Self Adder
class Adder:
def __init__(self, n):
self.n = n
def __call__(self, x):
self.n += x
return self.n
# Create an instance of Adder
add_five = Adder(5)
# Call the instance as a function
result = add_five(10)
print(result, add_five.n) # Output: 15, 15
result = add_five(10)
print(result) # Output: 25
Animal Factory
class Animal:
def __init__(self, species):
self.species = species
class AnimalFactory:
def __call__(self, *args, **kwargs):
return Animal(*args, **kwargs)
create_animal = AnimalFactory()
dog = create_animal("Dog")
print(dog.species) # Output: Dog
cat = create_animal("Cat")
print(cat.species) # Output: Cat
Class Based Decorator Implementation
class Repeat:
def __init__(self, times):
self.times = times
def __call__(self, func):
def wrapper(*args, **kwargs):
for _ in range(self.times):
func(*args, **kwargs)
return wrapper
class LogArgs:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print(f"Arguments: {args}, {kwargs}")
return self.func(*args, **kwargs)
@Repeat(3)
@LogArgs
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Arguments: ('Alice',), {}
Hello, Alice!
Arguments: ('Alice',), {}
Hello, Alice!
Arguments: ('Alice',), {}
Hello, Alice!
Conclusion
Python’s readability and elegance make it a top contender for the best programming language each year, along with its practical applications. Magic methods play a crucial role, enhancing readability and maintainability.
Mastering Python without understanding these methods is challenging. They keep code clean and visually appealing, allowing customization of class behavior for more expressive and understandable code. Overriding these methods for custom data structures saves time and effort, crucial for maintainable code. Continual learning and exploration of these features are key to becoming a proficient Python programmer.
Other similar articles by me.