✅ Special Method in Python ✅

Hello to Python lovers.This article is written with the aim of reviewing several important magic methods in Python.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.- Now i want add some useful and impotant note about dunder method :Special methods are not meant to be called directly. They are called automatically by the Python interpreter in response to specific events or actions, such as object creation, string conversion, or arithmetic operations.Special methods allow Python classes to define their own behavior for built-in functions and operators. For example, the __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.Special methods follow a naming convention of starting and ending with double underscores (__). This makes them easy to identify and avoids naming conflicts with other methods.Special methods can be overridden in subclasses to customize the behavior of the subclass. This allows subclasses to inherit the behavior of the parent class while adding their own customizations.Special methods can be used to make custom classes more intuitive and easy to use, by allowing them to behave like built-in types. This can make code more readable and reduce the amount of boilerplate code needed.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 40011182well !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 :✅ 1 . __class__ : ✅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`.✅ 2 . __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.✅ 3 . __dict__ : ✅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. ✅4 . __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.✅5 . __doc__ :✅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 :✅6 . __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.✅7 . __format__ :✅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.✅8 . __ge__ :✅`__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.✅9 . __getattribute__ :✅`__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__.✅10 . __gt__ :✅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.✅11 . __hash__ :✅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.✅12 . __init__ :✅__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 = ageperson1 = Person(&quotAlice&quot, 25)print(person1.name) # &quotAlice&quotprint(person1.age) # 25We 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.✅13 . __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.✅14 . __le__ :✅`__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 ...✅15 . __lt__ :✅`__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 ...✅16 . __module__ :✅`__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.pyclass MyClass: passdef my_function(): passIn 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 mymoduleobj = 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.✅17 . __ne__ :✅`__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.✅18 . __new__ :✅__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.✅19 . __reduce__ :✅__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.✅20 . __reduce_ex__ :✅`__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.✅21 . __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&quotPerson(name={self.name}, age={self.age})&quotp = Person(&quotAlice&quot, 25)print(repr(p)) # &quotPerson(name=Alice, age=25)&quotIn 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.✅22 . __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 == &quotage&quot and value < 0: raise ValueError(&quotAge cannot be negative&quot) super().__setattr__(name, value)p = Person(&quotAlice&quot, 25)p.name = &quotBob&quotp.age = -10 # Raises a ValueErrorIn 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.✅23 . __sizeof__ :✅`__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(&quotAlice&quot, 25)print(sys.getsizeof(p)) # prints the size of the Person instance in bytesIn 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.✅24 . __weakref__ :✅`__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 weakrefclass Person: def __init__(self, name): self.name = name def __repr__(self): return f&quotPerson({self.name})&quot p1 = Person(&quotAlice&quot)p2 = Person(&quotBob&quot)ref1 = weakref.ref(p1)ref2 = weakref.ref(p2)print(ref1(), ref2()) # prints the objects referenced by the weakrefsdel p1, p2print(ref1(), ref2()) # prints None, indicating the objects have been garbage-collectedIn 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.https://t.me/MunitionBox✅25 . __subclasshook__ :✅`__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 itemLet's go see and finish it ??✅26 . __str__ :✅`__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&quot{self.name} ({self.age})&quotperson = Person(&quotJohn Doe&quot, 25)print(person) # prints &quotJohn Doe (25)&quotIn 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 :There are other things that you can check yourself and enjoy all the wonders of Python :)) reference : https://chat.openai.com/ , https://docs.python.org/ , Realpython.comGood luck ...