In Python, special methods (also known as magic methods or dunder methods) are methods that start and end with a double underscore, such as __init__
, __str__
, and __add__
. These methods are called automatically by the Python interpreter in response to certain events or actions, such as object creation, string conversion, or arithmetic operations.
Special methods allow Python classes to behave like built-in types, and provide a way to customize the behavior of objects and operators. For example, the __add__
method can be used to define how two objects of a custom class should be added together, while the __str__
method can be used to define how an object should be converted to a string.
Here's an example of using a special method to customize the behavior of an object:
In this example, we define a Vector
class with an __add__
method that adds two vectors together, and a __str__
method that returns a string representation of the vector.
We then create two instances of the Vector
class, add them together using the +
operator, and print the result to the console. The __add__
method is called automatically by the Python interpreter, and the result is a new Vector
object with the sum of the two input vectors.
__add__
method can be used to define how two objects of a custom class should be added together, and the __str__
method can be used to define how an object should be converted to a string.__
). This makes them easy to identify and avoids naming conflicts with other methods.Overall, special methods are a powerful and flexible feature of Python, and can be used to customize the behavior of classes in a wide variety of ways. However, they should be used judiciously and with care, as they can make code more complex and harder to understand if overused.
Well , i want write a class and with one of object of my class , define and show some of important dunder method to see what happen in back ground of python :
consider this class :
now i make a instance for my class :
note : when i use print() for my instance python interpreter call __str__ magic method in Student calss and then return - > Matin Mohammadi is 20 Y.O and he/she live in Kashan .Matin Mohammadi ID's is 40011182
well !
now , let's go to main target of this article , define some of dunder method ...
in last screen-shot i make an object , now i write the name of my object and with dot-notation and 2 dash + Tab , see the picture below :
In Python, every object has a class, which is a blueprint or a template for creating objects of that type. When you create an object, Python automatically assigns it a class based on the type of the object.
The `__class__`
attribute is a special attribute in Python that returns the class of an object. It is a reference to the class that the object belongs to.
Here's an example:
In this example, `person`
is an instance of the `Person`
class. The `__class__`
attribute of `person`
returns the class of the object, which is `Person`
.
__delattr__ :✅
In Python, `__delattr__`
is a special method that can be defined in a class to customize the behavior of deleting an attribute from an object.
When an attribute is deleted using the `del`
statement, Python looks for the `__delattr__`
method in the object's class. If it exists, the method is called with the name of the attribute to be deleted as its argument.
Here's an example:
In this example, we define the `Person`
class with an `__init__`
method that initializes two instance variables `name`
and `age`
. We also define the `__delattr__`
method that prints a message when an attribute is deleted and then deletes the attribute using the `delattr`
built-in function.
We then create an instance of the `Person`
class called `person`
, and delete the `age`
attribute using the `del`
statement. This triggers the `__delattr__`
method, which prints a message and deletes the attribute.
It's worth noting that `__delattr__`
is not commonly used, and should be used with caution as it can lead to unexpected behavior if not implemented correctly.
In Python, __dict__
is a special attribute that is present in every object and contains a dictionary that maps the object's attributes to their values.
When you access an attribute of an object, Python first looks for the attribute in the object's __dict__
. If the attribute is not found in the __dict__
, Python looks for it in the object's class, and then in its superclass, and so on.
Here's an example:
In this example, we define the Person
class with an __init__
method that initializes two instance variables name
and age
. We then create an instance of the Person
class called person
, and print its __dict__
attribute. This prints a dictionary that maps the object's attributes to their values.
You can also modify the object's __dict__
directly to add, remove, or modify attributes:
person.__dict__['address'] = '123 Main St.' print(person.__dict__) del person.__dict__['age'] print(person.__dict__)
Output:
{'name': 'John', 'age': 30, 'address': '123 Main St.'} {'name': 'John', 'address': '123 Main St.'}
In this example, we add a new attribute address
to the object's __dict__
by directly accessing it as a dictionary. We then delete the age
attribute from the __dict__
, which removes it from the object.
__dir__ :✅
In Python, __dir__
is a special method that can be defined in a class to customize the behavior of the built-in dir()
function when called on objects of that class.
When you call dir(obj)
on an object obj
, Python looks for the __dir__
method in the object's class. If it exists, the method is called with no arguments, and should return a list of strings representing the names of the attributes and methods of the object.
Here's an example:
In this example, we define the Person
class with an __init__
method that initializes two instance variables name
and age
. We also define the __dir__
method that returns a list of the object's attributes and methods. We define a get_name
method that returns the object's name
attribute.
We then create an instance of the Person
class called person
, and call the dir
function on it. This calls the __dir__
method in the Person
class, which returns a list of strings representing the names of the object's attributes and methods.
Note that the __dir__
method is not commonly used, and should be used with caution as it can lead to unexpected behavior if not implemented correctly.
In Python, __doc__
is a special attribute that can be defined in a class or a function to provide documentation for that object.
The __doc__
attribute is a string that contains the documentation for the object. You can access this attribute using the dot notation on the object.
Here's an example using last class i wrote :
__eq__ :✅
In Python, __eq__
is a special method that can be defined in a class to customize the behavior of the equality operator ==
when comparing objects of that class.
Here's a simple example:
Note that when defining the __eq__
method, it is recommended to check if the other object is an instance of the same class before comparing its attributes, to avoid raising an error when comparing objects of different classes.
In Python, __format__
is a special method that can be defined in a class to customize the behavior of the format()
function when formatting objects of that class into strings.
The __format__
method takes a format specification as an argument and should return a string that represents the formatted object according to the specification.
Here's an example:
Note that the __format__
method is not commonly used, and should be used with caution as it can lead to unexpected behavior if not implemented correctly. It is recommended to use the built-in formatting options provided by Python, such as the str.format()
method and f-strings.
`__ge__`
is a special method that can be defined in a class to customize the behavior of the greater-than-or-equal-to operator >=
when comparing objects of that class.
Note that when defining the __ge__
method, it is recommended to check if the other object is an instance of the same class before comparing its attributes, to avoid raising an error when comparing objects of different classes. Also, it is recommended to define the __gt__
, __le__
, and __lt__
methods for a complete ordering of objects.
`__getattribute__`
is a special method that can be defined in a class to customize the behavior of attribute access for instances of that class.
When you access an attribute of an object using the dot notation (object.attribute
), Python calls the __getattribute__
method of the object and passes the name of the attribute as a string. The __getattribute__
method should return the value of the attribute if it exists, or raise an AttributeError
if the attribute does not exist.
an example :
Note that the __getattribute__
method is not commonly used, and should be used with caution as it can lead to unexpected behavior if not implemented correctly. It is recommended to use the built-in attribute access methods provided by Python, such as __getattr__
, __setattr__
, and __delattr__
.
this special method that can be defined in a class to customize the behavior of the greater-than operator `> `
when comparing objects of that class.
is a special method that can be defined in a class to customize the hashing behavior of objects of that class.
Hashing is a way of converting a Python object into an integer value, which is used as an index in a hash table to look up the object quickly. Python dictionaries and sets use hashing to store and retrieve values.
By default, objects in Python have a hash value that is based on their memory address. However, some objects, such as mutable objects, cannot be hashed by default, because their hash value would change if their contents were modified. To enable hashing for custom objects, you can define the __hash__
method in the class.
The __hash__
method should return an integer value that represents the hash of the object. The hash value should be based on the object's contents, so that objects with the same contents have the same hash value. If the contents of the object cannot be hashed, the __hash__
method should return None
.
We also create a set called people
that contains the three Person
objects. Because the Person
class defines the __hash__
method, the people
set can be created successfully, even though Person
objects are mutable. If the Person
class did not define the __hash__
method, a TypeError
would be raised when trying to create the set.
__init__
is a special method in Python that is used to initialize objects of a class. It is called automatically when a new instance of the class is created. The purpose of __init__
is to set the initial state of the object by assigning values to its instance variables.
###### Here is a simple example: ###### class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person1 = Person("Alice", 25)
print(person1.name) # "Alice"
print(person1.age) # 25
We can then access the name
and age
instance variables of person1
using dot notation, and verify that they were set correctly.
__init__
is not the only special method for initializing objects in Python. There is also __new__
, which is responsible for creating a new instance of the class, and __call__
, which allows an object to be called like a function. However, __init__
is the most commonly used method for initializing objects in Python.
__init_subclass__ :✅
`__init_subclass__`
is a special method in Python 3 that is called when a subclass is created. It allows you to customize the behavior of the subclass when it is created, by adding new methods, modifying attributes, or even raising errors.
Here's an example:
In this example, we define a MyParentClass
with an __init_subclass__
method. When the MyChildClass
is created as a subclass of MyParentClass
, the __init_subclass__
method is called automatically. In this case, it simply prints a message to the console.
You can use __init_subclass__
to add class-level attributes, methods or modify the class behavior. It can be useful for implementing class hierarchies with specific behaviors that need to be set up for all subclasses.
For example, you could use __init_subclass__
to enforce a particular interface for all subclasses of a certain class. Or, you could use it to register all subclasses of a particular class with a central registry.
It is worth noting that __init_subclass__
is a class method, meaning that it is called on the class itself (i.e., the cls
argument), not on an instance of the class. This means that you can't access instance variables or methods from __init_subclass__
. Instead, you'll need to modify class-level attributes and methods.
`__le__`
is a special method in Python that defines the behavior of the less than or equal to comparison operator <=
when applied to objects of a class. It takes two arguments: self
and other
, and should return True
if self
is less than or equal to other
, and False
otherwise.
well ! let's next one ...
`__lt__`
is a special method in Python that defines the behavior of the less than comparison operator <
when applied to objects of a class. It takes two arguments: self
and other
, and should return True
if self
is less than other
, and False
otherwise.
this is awesome guys . Let's check the __module__ dunder method ...
`__module__`
is a special attribute in Python that is present in all objects, including classes and instances. It contains the name of the module in which the object was defined .
see a simple example :
# file: mymodule.py
class MyClass:
pass
def my_function():
pass
In this example, we define a module named mymodule
that contains a class named MyClass
and a function named my_function
. If we import this module and create an instance of the MyClass
class, we can access the __module__
attribute to see the name of the module in which the class was defined:
import mymodule
obj = mymodule.MyClass()
print(obj.__module__) # 'mymodule'
This will print the name of the module, which is 'mymodule'
.
The __module__
attribute can be useful in various scenarios, such as when you need to check if a class or function was defined in a particular module, or when you want to import a module dynamically based on the name of an object's module.
`__ne__`
is a special method in Python that defines the behavior of the not equal to comparison operator !=
when applied to objects of a class. It takes two arguments: self
and other
, and should return True
if self
is not equal to other
, and False
otherwise.
__new__
is a special method in Python that is called when an object is created. It is responsible for creating and returning a new instance of the class.
The __new__
method is a static method, which means that it takes a class as its first argument, rather than an instance of the class. It is usually used to customize the creation of objects, such as by allocating a specific amount of memory or initializing attributes.
Here's an example of how the __new__
method can be used:
In this example, we define a Person
class with a __new__
method that initializes the name
and age
attributes. When we create a new instance of the Person
class, the __new__
method is called automatically. It prints a message to the console, creates a new instance of the Person
class using the super()
method, initializes the name
and age
attributes, and returns the instance.
The __new__
method is rarely used in Python because most of the time, the __init__
method is sufficient for initializing attributes. However, __new__
can be useful in situations where you need to customize the creation of objects, such as when working with immutable objects or implementing a singleton pattern.
__reduce__
is a special method in Python that is used to define how an object can be serialized and deserialized using the Pickle module.
Pickle is a module in Python that can be used to serialize and deserialize Python objects into a binary format that can be stored or transmitted over a network. When Pickle encounters an object that it does not know how to serialize, it looks for the object's __reduce__
method and uses it to determine how to serialize the object.
The __reduce__
method should return a tuple that contains a callable object and a tuple of arguments. The callable object will be called when the object is deserialized, and the arguments will be passed to the callable object to recreate the object.
Here's an example of how the __reduce__
method can be used:
In this example, we define a Person
class with a name
and age
instance variable. We also define the __reduce__
method to return a tuple that contains the class and its arguments. When we serialize the object using Pickle, it looks for the __reduce__
method and uses it to determine how to serialize the object. When we deserialize the object, Pickle calls the callable object and passes the arguments to recreate the object.
__reduce__
is just one of several special methods that can be used to customize the behavior of Python objects.
`__reduce_ex__`
is a special method in Python that is similar to the __reduce__
method, but with an additional argument that allows for more fine-grained control over the serialization and deserialization process.
Like __reduce__
, __reduce_ex__
is used to define how an object can be serialized and deserialized using the Pickle module. However, in addition to returning a tuple that contains a callable object and a tuple of arguments, __reduce_ex__
also takes an integer argument that specifies the protocol version to use when pickling the object.
The protocol version specifies how the pickled data should be formatted and can affect the performance, compatibility, and security of the serialization process. By default, the highest protocol version available is used, but it can be useful to specify a lower protocol version to ensure compatibility with older versions of Python or other Pickle implementations.
Here's an example of how the __reduce_ex__
method can be used:
In this example, we define a Person
class with a name
and age
instance variable. We also define the __reduce_ex__
method to return a tuple that contains the class and its arguments. When we serialize the object using Pickle, we specify protocol version 2 to ensure compatibility with older versions of Python. When we deserialize the object, Pickle calls the callable object and passes the arguments to recreate the object.
__reduce_ex__
is an advanced feature that is rarely used in practice, but it can be useful in certain situations where fine-grained control over the serialization process is required.
__repr__ :✅
`__repr__`
is a special method in Python that returns a string representation of an object. It is used to provide a more informative and unambiguous string representation of an object, which can be used for debugging, logging, or other purposes.
When you call the built-in repr
function on an object, it will return the string representation of the object by calling its __repr__
method. The string returned by __repr__
should be a valid Python expression that can be used to recreate the object.
Here's an example of how the __repr__
method can be used:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Person(name={self.name}, age={self.age})"
p = Person("Alice", 25)
print(repr(p)) # "Person(name=Alice, age=25)"
In this example, we define a Person
class with a name
and age
instance variable. We also define the __repr__
method to return a string that represents the object as a Python expression. When we call the repr
function on an instance of the Person
class, it will call the __repr__
method and return a string representation of the object.
It's important to note that the __repr__
method is used to provide a detailed and unambiguous string representation of an object, while the __str__
method is used to provide a more user-friendly string representation of an object. The str
function and the print
statement will call the __str__
method if it is defined, otherwise they will fall back to the __repr__
method.
__setattr__ :✅
__setattr__
is a special method in Python that is called when an attribute of an object is assigned a new value using the assignment operator =
. This method can be used to customize the behavior of attribute assignment for instances of a class.
The default behavior of __setattr__
is to simply assign the new value to the attribute. However, if you define your own __setattr__
method, you can add additional behavior or checks before or after the assignment.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __setattr__(self, name, value):
if name == "age" and value < 0:
raise ValueError("Age cannot be negative")
super().__setattr__(name, value)
p = Person("Alice", 25)
p.name = "Bob"
p.age = -10 # Raises a ValueError
In this example, we define a Person
class with a name
and age
instance variable. We also define the __setattr__
method to add a check for the age
attribute. If the new value of age
is negative, a ValueError
is raised. Otherwise, the default behavior of assigning the new value to the attribute is called using the super()
function.
When we assign a new value to the age
attribute of an instance of the Person
class, it will call the __setattr__
method and apply the additional check that we defined.
It's important to note that __setattr__
can be used to customize the behavior of all attribute assignments for an instance of a class, including assignments that happen inside methods or other parts of the code. Therefore, it's important to use this method carefully and avoid unintended side effects.
`__sizeof__`
is a special method in Python that returns the size of an object in bytes. The __sizeof__
method is automatically called when the sys.getsizeof()
function is called on an object, and it can be defined in a class to customize the size calculation for instances of the class.
Here's an example of how the __sizeof__
method can be used:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __sizeof__(self):
return object.__sizeof__(self) + sum(map(sys.getsizeof, (self.name, self.age)))
p = Person("Alice", 25)
print(sys.getsizeof(p)) # prints the size of the Person instance in bytes
In this example, we define a Person
class with a name
and age
instance variable. We also define the __sizeof__
method to calculate the size of the instance by adding the sizes of the object itself and its two instance variables. The object.__sizeof__(self)
call returns the base size of the object, and the sum(map(sys.getsizeof, (self.name, self.age)))
call returns the sizes of the name
and age
instance variables.
When we call the sys.getsizeof()
function on an instance of the Person
class, it will call the __sizeof__
method to calculate the size of the instance.
It's important to note that the size of an object can vary depending on the implementation of Python and the system it's running on. Therefore, the __sizeof__
method may not always provide an accurate representation of the actual memory usage of an object.
`__weakref__`
is a special attribute in Python that allows an object to have weak references to it. Weak references are references to an object that do not increase its reference count, and do not prevent the object from being garbage-collected.
In Python, objects that support weak references must define a __weakref__
attribute that acts as a handle to the weak references. This attribute is automatically created when an object is created, and it can be used to create weak references to the object using the weakref
module.
an simple example :
import weakref
class Person:
def __init__(self, name):
self.name = name
def __repr__(self):
return f"Person({self.name})"
p1 = Person("Alice")
p2 = Person("Bob")
ref1 = weakref.ref(p1)
ref2 = weakref.ref(p2)
print(ref1(), ref2()) # prints the objects referenced by the weakrefs
del p1, p2
print(ref1(), ref2()) # prints None, indicating the objects have been garbage-collected
In this example, we define a Person
class with a name
instance variable. We also define the __repr__
method to provide a string representation of the object.
We then create two instances of the Person
class, p1
and p2
, and create weak references to them using the weakref.ref()
function. We print the objects referenced by the weak references using the ref()
method.
Next, we delete the references to the objects p1
and p2
. Since these objects are no longer referenced by any strong references, they become eligible for garbage collection. We print the objects referenced by the weak references again, and we see that they now return None
, indicating that the objects have been garbage-collected.
The __weakref__
attribute is used behind the scenes by the weakref
module to manage weak references. It allows Python to keep track of the weak references to an object without affecting the object's reference count, and without preventing it from being garbage-collected.
`__subclasshook__`
is a special method in Python that allows a class to customize the behavior of the issubclass()
built-in function. This method is used to determine if a class is a subclass of another class.
The __subclasshook__
method should take two arguments: the class being checked, and the class that is being checked against. It should return True
if the first class is a subclass of the second class, False
if it is not, or NotImplemented
if the method does not know how to handle the check.
i wrote an axample , let's see :
In this example, we define several classes representing animals. We also define a SomeClass
class with a __subclasshook__
method that checks if a given class is a subclass of Animal
, Mammal
, or Bird
. If it is, it returns True
, otherwise it returns NotImplemented
.
We then use the issubclass()
built-in function to check if various classes are subclasses of Animal
or Mammal
. The results of these checks are printed to the console.
When we check if Dog
is a subclass of Animal
, we get True
, since Dog
is a subclass of Mammal
, which is a subclass of Animal
. When we check if Parrot
is a subclass of Mammal
, we get False
, since Parrot
is not a subclass of Mammal
. When we check if Fish
is a subclass of Animal
, we get False
, since Fish
is not a subclass of Animal
. Finally, when we check if SomeClass
is a subclass of Animal
, we get True
, since the __subclasshook__
method of SomeClass
returns True
for Animal
.
Finally we reached the last item
Let's go see and finish it ??
`__str__`
is a special method in Python that returns a string representation of an object. This method is called by the built-in str()
function, and is also used when an object is printed using the print()
statement.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} ({self.age})"
person = Person("John Doe", 25)
print(person) # prints "John Doe (25)"
In this example, we define a Person
class with a __str__
method that returns a string representation of the object, consisting of the person's name and age.
We then create an instance of the Person
class, and print it using the print()
statement. The __str__
method is called automatically by the print()
statement, and the string representation of the Person
object is printed to the console.
The __str__
method is useful for providing a human-readable string representation of an object. It can be used for debugging and logging purposes, and can also be used to provide a user-friendly representation of an object in a GUI or other interface.
note :
Good luck ...