Может быть, я нашел что-то странное на pytorch, в результате чего не работает установщик свойств - PullRequest
1 голос
/ 09 апреля 2020

Может быть, я нашел что-то странное на pytorch, в результате чего не работает установщик свойств. Ниже приведен минимальный пример, демонстрирующий это:

import torch.nn as nn

class A(nn.Module):
    def __init__(self):
        super(A, self).__init__()
        self.aa = 1
        self.oobj = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    @property
    def obj(self):
        print('get attr [obj]: {0}'.format(self.oobj))
        return self.oobj
    @obj.setter
    def obj(self, val):
        print('set attr [obj] to {0}'.format(val))
        self.oobj = val

class B(nn.Module):
    def get_attr(self):
        print('no any attr.')

class C:
    def get_attr(self):
        print('no any attr.')

b = A()             # set obj, and prints my setter message

b.obj               # get obj using my getter

# respectively run the following 3 lines, only the last line not call the setter I defined explicitly.
b.obj = C()         # set obj, and prints my setter message

# b.obj = [1, 2, 3]   # set obj, and prints my setter message

# b.obj = B()         # set obj, but it doesn't print my setter message

В последней строке вызывается не метод определения свойств, определенный мной для класса A, а метод вызова torch.nn.Module. Поскольку A рассматривает B как nn.Module, вызовите установщик на nn.Module, чтобы установить attr [obj] как Модуль, но все же странно, почему бы не вызвать установщик, который я явно определил для класса A?

И мой проект должен установить атрибут nn.Module через сеттер, который я определил явно, что вызывает ошибку (потому что это не удалось). Теперь я изменил свой код, решил ошибку, но все еще ломаю голову над проблемой.

1 Ответ

0 голосов
/ 09 апреля 2020

Поначалу это может показаться неочевидным, но до тех пор, пока вы не установите 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]
...