OBJECT ORIENTED PROGRAMMING




Skip to a subsection :

  1. DUNDER METHODS
  2. DOCUMENTING A CLASS
  3. A FEW MORE THINGS TO KNOW
  4. PROPERTY DECORATOR


DUNDER METHODS

Dunder methods ( double underscore methods ) are special methods surrounded by double underscores. They are predefined methods that can be used when creating custom objects. The example below illustrates what can be done with these methods.

class Point:
	def __init__(self, x, y, name = 'P'):
		self.x = x
		self.y = y
		self.name = name
	def __setattr__(self, name, value):
		self.__dict__[name] = value
	def __repr__(self):
		return f"({self.name}, x = {self.x}, y = {self.y})"
	def __str__(self):
		return f"({self.name} has coordinates x = {self.x}, y = {self.y})"
	def __eq__(self,other):
		if isinstance(other, Point):
			return self.x == other.x and self.y == other.y
		else:
			return False
	def __hash__(self):
		return hash((self.x, self.y))
		
class Polygon:
	def __init__(self, *points):
		self.points = list(points)
	def __iter__(self):
		return iter(self.points)
	def __len__(self):
		return len(self.points)
	def __dir__(self):
		return ['__iter__','__add__','__len__','__getitem__']
	def __bool__(self):
		return len(self) > 1
	def __add__(self, point:Point):
		if isinstance(point, Point):
			self.points.append(point)
		else:
			raise TypeError("Only instances of Point can be added")
	def __getitem__(self, index):
		return self.points[index]
	def __setitem__(self, index, point:Point):
		if isinstance(point, Point):
			self.points[index] = point
		else:
			raise TypeError("Only instances of Point can be added")
	def __delitem__(self, index):
		del self.points[index]
	def __repr__(self) :
            point_list = ', '.join(repr(point) for point in self.points)
            return f"Polygon([{point_list}])"
    	
class Triangle(Polygon):
    def __init__(self, point1:Point, point2:Point, point3:Point, name='Triangle'):
        super().__init__(point1, point2, point3)

Now this illustrates how those objects and methods can be used :

# __init__
A = Point(1, 1, 'point A')
B = Point(2, 1, 'point_B')
C = Point(0, 2, 'point_C')
D = Point(0, 0, 'point_D')
polygon_1 = Polygon(A,B,C)

#__setattr__
__setattr__(name = 'color', value = 'blue')
print(A.color)

# __repr__ and __str__
print(A.__repr__())
print(A.__str__())

#__eq__
print(A.__eq__(B))

#__hash__
my_dict={A:"value for A",B:'value for B'}

#__iter__
for point in polygon_1:
	print(point.x)

#__len__
print(len(polygon_1))

#__bool__
print(polygon_1.__bool__())

#__add__
polygon_1.__add__(C)

#__getitem __
print(polygon_1[0].x)

#__delitem__
polygon_1.__delitem__(2) 

#__setitem__
polygon_1.__setitem__(2, D)

#__dir__
print(dir(polygon_1))

# inheritance
Tr1=Triangle(A,B,C)
print(len(Tr1))

DOCUMENTING A CLASS

This is how to document a class and its methods :

class Point:
    """A class representing a point in 2D space.
    This class defines a point with x and y coordinates and an optional name.
    Args:
        x (float): The x-coordinate of the point.
        y (float): The y-coordinate of the point.
        name (str): An optional name for the point (default is 'point').
    Attributes:
        x (float): The x-coordinate of the point.
        y (float): The y-coordinate of the point.
        name (str): The name of the point.
    """

    def __init__(self, x: float, y: float, name: str = 'point'):
        """Initialize a point .
        Args:
            x (float): The x-coordinate of the point.
            y (float): The y-coordinate of the point.
            name (str): An optional name for the point (default is 'point').
        """
        self.x = x
        self.y = y
        self.name = name

A FEW MORE THINGS TO KNOW


PROPERTY DECORATOR

@property in Python is a decorator that allows you to define a method as an attribute, providing a way to control how attributes are accessed and modified

Among other things, the `@property` decorator allows for:

class Point:
    def __init__(self, x, y, name = 'P'):
        self._x = x
        self.y = y
    @property
    def x(self):
        print('x accessed')
        return self._x
    @x.setter
    def x(self,x):
        if isinstance(x,int):
            print('x modified')
            self._x = x
        else : 
            print('integer expected, x not modified')
    @property
    def distance_to_0(self) :
        return np.sqrt(self._x**2 + self.y**2)

P = Point(2,4)
P.x = 3
P.x
print(P.x)
print(P.distance_to_0)

# prints :
x modified
x accessed
x accessed
3
5.0