# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: trainingAI
#     language: python
#     name: trainingai
# ---

# # Introduction
# <hr style="border:2px solid black"> </hr>

#
# **What?** `__call__` method
#
#

# # Definition
# <hr style="border:2px solid black"> </hr>

#
# - `__call__` is a built-in method which enables to write classes where the instances behave like functions and can be called like a function.
# - In practice: `object()` is shorthand for `object.__call__()`
#
#

# # _ _call_ _ vs. _ _init_ _
# <hr style="border:2px solid black"> </hr>

#
# - `__init__()` is properly defined as Class Constructor which builds an instance of a class, whereas `__call__` makes such a instance callable as a function and therefore can be modifiable.
# - Technically `__init__` is called once by `__new__` when object is created, so that it can be initialised
# - But there are many scenarios where you might want to redefine your object, say you are done with your object, and may find a need for a new object. With `__call__` you can redefine the same object as if it were new.
#
#

# # Example #1
# <hr style="border:2px solid black"> </hr>

class Example():
    def __init__(self):
        print("Instance created")
    
    # Defining __call__ method
    def __call__(self):
        print("Instance is called via special method __call__")


e = Example()

e.__init__()

e.__call__()


# # Example #2
# <hr style="border:2px solid black"> </hr>

class Product():
    def __init__(self):
        print("Instance created")
    
    # Defining __call__ method
    def __call__(self, a, b):
        print("Instance is called via special method __call__")
        print(a*b)


p = Product()

p.__init__()

# Is being call like if p was a function
p(2,3)

# The cell above is equivalent to this call
p.__call__(2,3)


# # Example #3
# <hr style="border:2px solid black"> </hr>

class Stuff(object):
    def __init__(self, x, y, Range):
        super(Stuff, self).__init__()
        self.x = x
        self.y = y
        self.Range = Range
    
    def __call__(self, x, y):
        self.x = x
        self.y = y
        print("__call with (%d, %d)" % (self.x, self.y))
    
    def __del__(self, x, y):
        del self.x
        del self.y
        del self.Range       
        


s = Stuff(1, 2, 3)

s.x

s(7,8)

s.x


# # Example #4
# <hr style="border:2px solid black"> </hr>

class Sum():
    def __init__(self, x, y):        
        self.x = x
        self.y = y        
        print("__init__ with (%d, %d)" % (self.x, self.y))
    
    def __call__(self, x, y):
        self.x = x
        self.y = y        
        print("__call__ with (%d, %d)" % (self.x, self.y))
    
    def sum(self):
        return self.x + self.y        


sum_1 = Sum(2,2)
sum_1.sum()

sum_1 = Sum(2,2)
sum_1(3,3)

sum_1 = Sum(2,2)
# This is equivalent to
sum_1.__call__(3,3)

# You can also do this
sum_1 = Sum(2,2)(3,3)

# # References
# <hr style="border:2px solid black"> </hr>

#
# - https://www.geeksforgeeks.org/__call__-in-python/
#
#



StarcoderdataJupyterScriptsDedupFiltered