Поначалу это может показаться неочевидным, но до тех пор, пока вы не установите b.obj
в качестве объекта nn.Module
, вы определяете нормальный атрибут; но как только вы установите b.obj
как nn.Module
объект, вы сможете «только» заменить b.obj
другим nn.Module
, потому что вы зарегистрировали его как _modules
. Позвольте мне пройтись по коду, и вы получите его.
nn.Module()
можно найти реализацию __setattr__
здесь .
Сначала вы определили новый nn.Module
:
b = A() # btw, why not a = A() :)
Затем вы устанавливаете (я пропущу ненужные шаги для воспроизведения поведения):
b.obj = [1, 2, 3]
В этом случае, потому что
Затем будет выполнена эта строка:
object.__setattr__(self, name, value)
, которая является не чем иным, как обычным набором атрибутов, , который вызывает ваш установщик .
Теперь, когда вы установите:
b.obj = B()
Тогда, поскольку B()
является nn.Module
, следующий блок * Вместо этого будет выполнено 1066 *:
modules = self.__dict__.get('_modules')
if isinstance(value, Module):
if modules is None:
raise AttributeError(
"cannot assign module before Module.__init__() call")
remove_from(self.__dict__, self._parameters, self._buffers)
modules[name] = value
Итак, теперь вы фактически регистрируете nn.Module
в self.__dict__.get('_modules')
(распечатайте его раньше и после, и вы увидите ... сделайте это до и после установки [1,2,3]
также).
После этой точки, если вы не устанавливаете nn.Parameter
, и вы пытаетесь установить .obj
снова , затем он попадет в этот блок :
elif modules is not None and name in modules:
if value is not None:
raise TypeError("cannot assign '{}' as child module '{}' "
"(torch.nn.Module or None expected)"
.format(torch.typename(value), name))
modules[name] = value
То есть: у вас уже установлено modules['obj']
на что-то, и теперь вам нужно укажите другой nn.Module
или None
, если вы хотите установить его снова. И, как вы можете видеть, поскольку вы предоставляете list
, если вы попытаетесь установить b.obj = [1,2,3]
снова, вы получите сообщение об ошибке в блоке выше, и это то, что вы получите.
Если вы действительно хотите установить его на что-то другое, то вы должны удалить его раньше:
b.obj = B()
del b.obj
b.obj = [1,2,3]