How the parenthesis are used right after the object's name?
I want to know about the parenthesis after the Object's name.

I am learning AI and building the AI model, now in the Tutorial's code the author has written a line which is containing the Parenthesis right after the object's name which is : self.model(...) 

Where self.model is the Object of the Network class.
How the objects are having parenthesis being an object, not a function?
Now I want to know about the parenthesis after the Object's name.

class Network(nn.Module):

    def __init__(self, input_size, nb_action):
        super(Network, self).__init__()
        self.input_size = input_size
        self.nb_action = nb_action
        self.fc1 = nn.Linear(input_size, 30)
        self.fc2 = nn.Linear(30, nb_action)

    def forward(self, state):
        x = F.relu(self.fc1(state))
        q_values = self.fc2(x)
        return q_values

class Dqn():

    def __init__(self, input_size, nb_action, gamma):
        self.gamma = gamma
        self.reward_window = []
        self.model = Network(input_size, nb_action)
        self.memory = ReplayMemory(100000)
        self.optimizer = optim.Adam(self.model.parameters(), lr = 0.001)
        self.last_state = torch.Tensor(input_size).unsqueeze(0)
        self.last_action = 0
        self.last_reward = 0

    def select_action(self, state):
        probs = F.softmax(self.model(Variable(state, volatile = True))*100) # &lt;-- The problem is here where the self.model object is CALLED with Parenthesis.
        action = probs.multinomial(10)
        return action.data[0,0]


In python, everything is an object. The functions you create and call are also objects. Anything in python that can be called is a callable object.

However, if you want a class object in python to be a callable object, the __call__ method must be defined inside the class.

When the object is called, the __call__(self, ...) method gets called.

Here is an example.

class Foo:
    def __init__(self, x=0):
        self.x = x

    def __str__(self):
        return f'{self.__class__.__name__}({self.x})'

    def __call__(self, *args):
        print(f'{self} has been called with {args} as arguments')


f1 = Foo(5)
f2 = Foo()

f1()  # f1.__call__()
f2()  # f2.__call__()
f1(1, 2, 3)  # f1.__call__(1, 2, 3)
f2(10, 20)  # f2.__call__(10, 20)


Output:

Foo(5) has been called with () as arguments
Foo(0) has been called with () as arguments
Foo(5) has been called with (1, 2, 3) as arguments
Foo(0) has been called with (10, 20) as arguments


This is how you can make a python object a callable object.
