Python Forum
The function of double underscore back and front in a class function name?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
The function of double underscore back and front in a class function name?
#1
I am just looking at Mandelbrot fractals. This class comes from realpython.com I know next to nothing about classes.

I saved this class as a module in my myModules folder and import the class MandelbrotSet :

from mandelbrotV2 import MandelbrotSet

Why does __contains__() use double underscore? I read the double underscore keeps the function hidden??

# mandelbrotV2.py

from dataclasses import dataclass
from math import log

@dataclass
class MandelbrotSet:
    max_iterations: int
    escape_radius: float = 2.0

    def __contains__(self, c: complex) -> bool:
        return self.stability(c) == 1

    def stability(self, c: complex, smooth=False, clamp=True) -> float:
        value = self.escape_count(c, smooth) / self.max_iterations
        return max(0.0, min(value, 1.0)) if clamp else value

    def escape_count(self, c: complex, smooth=False) -> int | float:
        z = 0
        for iteration in range(self.max_iterations):
            z = z ** 2 + c
            if abs(z) > self.escape_radius:
                if smooth:
                    return iteration + 1 - log(log(abs(z))) / log(2)
                return iteration
        return self.max_iterations
Reply
#2
Dunder methods, are specially named methods that have magic powers.

https://docs.python.org/3/reference/data...ecialnames

For example, the method __str__() is called when you print an instance of a class. A class that has the methods __getitme__() and __setitem__ supports indexing or like a list or dictionary, or maybe slicing like a list, or maybe both.

Quote:object.__contains__(self, item)¶
Called to implement membership test operators. Should return true if item is in self, false otherwise. For mapping objects, this should consider the keys of the mapping rather than the values or the key-item pairs.

For objects that don’t define __contains__(), the membership test first tries iteration via __iter__(), then the old sequence iteration protocol via __getitem__(), see this section in the language reference.

In your MandelbrotSet example, calling "if complex_number in mandelbrot_instance: will call mandelbrot_instance.__contains__(complex_number) to get the answer.
Pedroski55 likes this post
Reply
#3
To give a eaiser example then is easier to see how special method work.
They give extra behavior to a class,in this case can now use in operator and len().
class MyList:
    def __init__(self, data):
        self.data = data

    def __contains__(self, item):
        return item in self.data

    def __len__(self):
        return len(self.data)
Use Class:
>>> my_list = MyList([1, 2, 3, 4, 5])
>>> my_list.data
[1, 2, 3, 4, 5]
>>> # Now will "in" work,because of __contains__
>>> print(3 in my_list)
True
>>> print(9 in my_list)
False
>>> # len() works because of __len__
>>> len(my_list)
5
In summary, by defining the __contains__ method,
the MandelbrotSet class allows you to use the in operator to check if a complex number is part of the Mandelbrot set.
This makes it easy to check membership in the set with syntax like if c in mandelbrot_set.
Pedroski55 likes this post
Reply
#4
On more question if I may: In the Mandelbrot fractal tutorial, the classes shown do not use __init__

When is __init__ necessary?

For example, this Class, although I know it is just an example:

class MyList:
    def __init__(self, data):
        self.data = data
 
    def __contains__(self, item):
        return item in self.data
 
    def __len__(self):
        return len(self.data)
can be reduced to this:

class ML:
    data: list

a = ML()
a.data = [1, 2, 3, 4, 5]
a.data
[1, 2, 3, 4, 5]
len(a.data)
5
What do I gain from using __init__ ??
Reply
#5
Using __init__ in classes is fundamental for initializing(getting data in from outside) to the newly created objects.
While it's true that you can manually set attributes on an instance as in your second example.
Also try len(a) and see that it won't work as that what __len__ dos in my class.

Classes with an __init__ method are generally more readable and maintainable.
Anyone reading your code can easily understand what attributes an instance of the class is expected to have and what initial state it will be in.

Consistency:
Using __init__ ensures that every instance of the class is initialized consistently.
Without __init__ you rely on manually setting attributes after object creation,
which can lead to errors or inconsistencies if some attributes are forgotten or mistyped

So can also add documentation and eg type hint to make even more clear what the class dos.
Then will see when create a object that it need a list of int and help(MtList) will work.
class MyList:
    """A custom list class that encapsulates a list and provides
    special methods to interact with its data.
    """
    def __init__(self, data: list[int]):
        self.data = data

    def __contains__(self, item: int) -> bool:
        """Check if the item exists in the data."""
        return item in self.data

    def __len__(self) -> int:
        """Return the number of items in the data."""
        return len(self.data)
To give one more example the more realistic as there often several values that you what to initialized class with.
class Car:
    """Represents a car with specific attributes and capabilities."""
    def __init__(self, color: str, mileage: float, fuel_efficiency: float):
        self.color = color
        self.mileage = mileage
        self.fuel_efficiency = fuel_efficiency

    def calculate_gas_usage(self, distance: float) -> float:
        """Calculate the amount of gas used over a given distance."""
        return distance / self.fuel_efficiency

    def __repr__(self):
        return f"Car(color={self.color!r}, mileage={self.mileage}, fuel_efficiency={self.fuel_efficiency})"

    def __str__(self):
        return f"A {self.color} car with {self.mileage} miles, fuel efficiency {self.fuel_efficiency} mpg."
Use this class.
>>> my_car = Car('red', 10, 14.5)
>>> # This work because of __repr__
>>> my_car
Car(color='red', mileage=10, fuel_efficiency=14.5)
>>> # This work because of __str__
>>> print(my_car)
A red car with 10 miles, fuel efficiency 14.5 mpg.
>>> my_car.calculate_gas_usage(100)
6.896551724137931
>>> help(Car)
Help on class Car in module __main__:

class Car(builtins.object)
 |  Car(color: str, mileage: float, fuel_efficiency: float)
 |
 |  Represents a car with specific attributes and capabilities.
 |
 |  Methods defined here:
 |
 |  __init__(self, color: str, mileage: float, fuel_efficiency: float)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |
 |  __repr__(self)
 |      Return repr(self).
 |
 |  __str__(self)
 |      Return str(self).
 |
 |  calculate_gas_usage(self, distance: float) -> float
 |      Calculate the amount of gas used over a given distance.
Reply
#6
(Feb-19-2024, 07:39 AM)Pedroski55 Wrote: On more question if I may: In the Mandelbrot fractal tutorial, the classes shown do not use __init__

The __init__ method initializes the instance, but the use of the decorator @dataclass overwrite the __init__ method with its own and calls __post_init__, if this method exists.

Class vs Dataclass
from dataclasses import dataclass


# type annotations are optional and not checked during runtime
class Foo1:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
        print(self.name, self.age)


# type annotations for dataclass is mandatory, but nothing is checked if the type is right
@dataclass
class Foo2:
        name : str
        age : int
        def __post_init__(self):
            print(self.name, self.age)


print(Foo1("Foo1", 10))
print(Foo2("Foo2", 10)) # <- the dataclass gives you a nice representation
Output:
Foo1 10 <__main__.Foo1 object at 0x000002AAC81F1820> Foo2 10 Foo2(name='Foo2', age=10)
Pedroski55 likes this post
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#7
So a good example bye DeaD_EyE using @dataclass.
As you new to classes Pedroski55,you should first understand the fundamentals of classes.
Car class could be written like this using @dataclass,
will automatically adding generated special methods such as __init__ and __repr__ to the class.
from dataclasses import dataclass

@dataclass
class Car:
    """Represents a car with specific attributes and capabilities."""
    color: str
    mileage: float
    fuel_efficiency: float

    def calculate_gas_usage(self, distance: float) -> float:
        """Calculate the amount of gas used over a given distance."""
        return distance / self.fuel_efficiency
Using the class see that it work the same,could add __str__ to make just the same.
Now __repr__ work the same for object and print().
>>> my_car = Car('blue', 5, 14.5)
>>> my_car.color
'blue'
>>> # __repr__ are added automatically
>>> my_car
Car(color='blue', mileage=5, fuel_efficiency=14.5)
>>> # Work the same for print add __str__ if want to change
>>> print(my_car)
Car(color='blue', mileage=5, fuel_efficiency=14.5)
>>> my_car.calculate_gas_usage(70.5)
4.862068965517241
Pedroski55 likes this post
Reply
#8
Thank you both for your help!

I read that a class is basically a dictionary. I believe the value of a dictionary key:value pair can be a function.

So in the case of class Car() do we have:

Car = {"color": 'blue', "mileage": 5, "fuel_efficiency": 14.5, "efficiency": calculate_gas_usage() }

???
Reply
#9
(Feb-19-2024, 02:21 PM)Pedroski55 Wrote: I read that a class is basically a dictionary. I believe the value of a dictionary key:value pair can be a function.

So in the case of class Car() do we have:

Car = {"color": 'blue', "mileage": 5, "fuel_efficiency": 14.5, "efficiency": calculate_gas_usage() }
Now is classes💪 is lot more that just a dictionary,it store values values from __init__ internally.
Can look at this:
>>> my_car = Car('blue', 8.8, 10.5)
>>> my_car.__dict__
{'color': 'blue', 'fuel_efficiency': 10.5, 'mileage': 8.8}
>>> my_car.__dict__['color']
'blue'
The calculate_gas_usage method is not stored in this dictionary.
Instead, it's a part of the class definition and is accessed through the class's namespace.
When you call my_car.calculate_gas_usage(distance),
Python automatically passes my_car as the first argument to the calculate_gas_usage method.
>>> my_car.calculate_gas_usage(275.5)
26.238095238095237
Reply
#10
Not quite. calculate_gas_milage() would try to call a function named calculate_gas_milage and return a result. This would either raise a NameError, or map "efficiency" to the result, not the function. More importantly, python knows that it needs to change instance.method(args) into insance.__class__.method(instance, args) when it is calling an instance method of a class. It would not know to do that if you just stuffed a function in a dictionary.

I'm occasionally tasked with maintaining Python projects that use dictionaries to mimic classes, even though Python always had classes. Let me just say that doing this is a bad idea and leave it at that. Shudder!
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  front-end js and back-end python 3lnyn0 0 997 Jun-03-2022, 08:51 PM
Last Post: 3lnyn0
  TimeOut a function in a class ? Armandito 1 1,662 Apr-25-2022, 04:51 PM
Last Post: Gribouillis
  Calling a class from a function jc4d 5 1,831 Dec-17-2021, 09:04 PM
Last Post: ndc85430
  Exit function from nested function based on user input Turtle 5 2,931 Oct-10-2021, 12:55 AM
Last Post: Turtle
  a function common to methods of a class Skaperen 7 2,633 Oct-04-2021, 07:07 PM
Last Post: Skaperen
  Tuple generator, and function/class syntax quazirfan 3 3,899 Aug-10-2021, 09:32 AM
Last Post: buran
Question Stopping a parent function from a nested function? wallgraffiti 1 3,690 May-02-2021, 12:21 PM
Last Post: Gribouillis
Question exiting the outer function from the inner function banidjamali 3 3,559 Feb-27-2021, 09:47 AM
Last Post: banidjamali
  Struggling for the past hour to define function and call it back godlyredwall 2 2,233 Oct-29-2020, 02:45 PM
Last Post: deanhystad
  Passing argument from top-level function to embedded function JaneTan 2 2,273 Oct-15-2020, 03:50 PM
Last Post: deanhystad

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020