1

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) # <-- The problem is here where the self.model object is CALLED with Parenthesis.
        action = probs.multinomial(10)
        return action.data[0,0]
AAYUSH SHAH
  • 131
  • 2
  • 6
  • The parentheses are calling the constructor (`__init__`) of `Network`. – iz_ Jul 31 '19 at 18:16
  • 1
    Possible duplicate of https://stackoverflow.com/questions/32129064/what-are-the-parentheses-for-at-the-end-of-python-method-names – tripleee Jul 31 '19 at 18:17
  • The parentheses after the class name in the class definition `class Dqn()` allow for the inclusion of 'parent' objects whose methods which will be inherited by the child object. As @iz_ alluded to, when you instantiate the object (e.g. `dq = Dqn(x,y,z)`), the parentheses are for passing arguments (except for self, which is implied), to the class' `__init__` method. – Deepstop Jul 31 '19 at 18:20
  • @iz_ No, techinically it is calling `type.__call__(Network, input_size, nb_action)`, which calls `Network.__new__(Network, input_size, nb_action)` and, if `Network.__new__` does indeed return an instance of `Network`, *then* calls `Network.__init__` on that instance. – chepner Jul 31 '19 at 18:29
  • But , in the code an Object self.model was already Initialized with Network class on the line : self.model = Network(size,action) , but after already called __init__ on the initilization , how can we put the parenthesis after the object itself ? LIKE : self.model() .... ? Please simplify ... I'm not that good...@Deepstop – AAYUSH SHAH Jul 31 '19 at 18:29
  • @chepner Thanks for the correction; I was trying to make it easy to understand for OP. – iz_ Jul 31 '19 at 18:30
  • @AAYUSHSHAH My guess is that `Network`'s base class (`nn.Module`) is callable, i.e. it defines `__call__`. – iz_ Jul 31 '19 at 18:31
  • Can you simplify your answer for me please ? @chepner – AAYUSH SHAH Jul 31 '19 at 18:31
  • I believe @iz_ simplified chepners comment earlier – kluvin Jul 31 '19 at 18:33
  • @AAYUSHSHAH *All* types are callable; `type.__call__` is defined as it is so that you can get an instance of a class without having to call the class's `__new__` method manually. – chepner Jul 31 '19 at 18:34

2 Answers2

1

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.

Diptangsu Goswami
  • 5,554
  • 3
  • 25
  • 36
0

Any class can define a __call__ method to define what it means for one of its instances to be called as if it were a function. In this case,

self.model(...)

works because it is short for self.model.__call__(...), and Network inherits, via its base class, a definition of __call__.


In fact, this is why functions themselves are callable:

>>> def foo(): return 3
...
>>> type(foo)
<class 'function'>
>>> type(foo).__call__
<slot wrapper '__call__' of 'function' objects>
>>> foo()
3
>>> foo.__call__()
3

(Even foo.__call__.__call__() returns 3! Try not to think too hard about why __call__ itself can be called. At some point, the Python implementation itself breaks the seemingly infinite chain of implicitly-invoked methods.)

chepner
  • 497,756
  • 71
  • 530
  • 681