Any submodule that isn’t declared as a direct attribute of the class, will not be registered as a submodule of the model and its parameters won’t show up in model.parameters() so it won’t get trained.
For example, if you do this…
self.convs = []
self.convs.append(conv)

then your model doesn’t know that it has conv as a submodule.
I know of three possible fixes…

use settattr() as you pointed out.
self.add_module(f'conv{i}', conv)
replace self.convs = [] with self.convs = nn.ModuleList(). Then treat self.convs like an ordinary python list and anything you append to it will be properly registered.

The third solution is the most elegant in my opinion.