【问题标题】:How the parenthesis are used right after the object's name?如何在对象名称之后使用括号?
【发布时间】:2019-12-09 07:49:32
【问题描述】:

我想知道对象名称后面的括号。

我正在学习 AI 并构建 AI 模型,现在在教程的代码中,作者写了一行,其中包含对象名称后面的括号:self.model(...)

其中 self.model 是 Network 类的对象。 对象如何将括号作为对象而不是函数? 现在我想知道对象名称后面的括号。

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]

【问题讨论】:

  • 括号中调用的是Network的构造函数(__init__)。
  • 类定义class Dqn() 中类名后面的括号允许包含“父”对象,其方法将由子对象继承。正如@iz_ 所暗示的,当您实例化对象时(例如dq = Dqn(x,y,z)),括号用于将参数(除了隐含的self)传递给类的__init__ 方法。
  • @iz_ 不,从技术上讲,它正在调用type.__call__(Network, input_size, nb_action),它调用Network.__new__(Network, input_size, nb_action),如果Network.__new__ 确实返回了Network 的实例,then 调用Network.__init__ 在那个实例上。
  • 但是,在代码中,一个 Object self.model 已经用 Network 类初始化了: self.model = Network(size,action) ,但是在调用 init之后> 关于初始化,我们如何将括号放在对象本身之后?喜欢:self.model() ....?请简化...我不是那么好...@Deepstop

标签: python artificial-intelligence pytorch


【解决方案1】:

任何类都可以定义一个__call__ 方法来定义它的一个实例被调用的含义,就好像它是一个函数一样。在这种情况下,

self.model(...)

之所以有效,是因为它是self.model.__call__(...) 的缩写,并且Network 通过其基类继承了__call__ 的定义。


事实上,这就是函数本身是可调用的原因:

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

(即使foo.__call__.__call__() 返回3!尽量不要想太多为什么__call__ 本身可以被调用。在某些时候,Python 实现本身打破了看似无限的隐式调用方法链。)

【讨论】:

    【解决方案2】:

    在python中,一切都是对象。您创建和调用的函数也是对象。 python 中可以调用的任何东西都是可调用对象。

    但是,如果你想让python中的类对象成为可调用对象,则必须在类内部定义__call__方法。

    当对象被调用时,__call__(self, ...) 方法被调用。

    这是一个例子。

    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)
    

    输出:

    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
    

    这就是如何使 python 对象成为可调用对象。

    【讨论】:

      猜你喜欢
      • 2015-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-14
      • 1970-01-01
      相关资源
      最近更新 更多