Activate pytorch network nodes without variable assignments?
In tutorials for PyTorch networks, we typically see an implementation such as:
from torch.nn.functional import hardtanh, sigmoid
import torch.nn as nn

class great_network(nn.Module):
    def __init__(self):
        super(great_network, self).__init__()  
        self.layer1 = nn.Conv2d(2, 2, 3)
        self.pool_1 = nn.MaxPool2d(1, 1)
        self.layer3 = nn.ConvTranspose2d(2, 2, 3)
        self.out_layer = nn.Conv2d(1, 1, 3)

    def forward(self, x):
        x = hardtanh(self.layer1(x))
        x = self.pool_1(x)
        x = hardtanh(self.layer3(x))
        x = sigmoid(self.out_layer(x))
        return x
    
net = great_network()
print(net)

great_network(
  (layer1): Conv2d(2, 2, kernel_size=(3, 3), stride=(1, 1))
  (pool_1): MaxPool2d(kernel_size=1, stride=1, padding=0, dilation=1, ceil_mode=False)
  (layer3): ConvTranspose2d(2, 2, kernel_size=(3, 3), stride=(1, 1))
  (out_layer): Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1))
)

If I were to dynamically change the size of this network to run multiple experiments, I'd have to emulate the above code (resembling a data clump code bloat) without multiple assignments.
Something like this might do:
from torch.nn.functional import hardtanh, sigmoid
import torch.nn as nn
import numpy as np

class not_so_great_network(nn.Module):
    def __init__(self, n):
        super(not_so_great_network, self).__init__()
        self.pre_layers = self.generate_pre_layers(n)
        self.post_layers = self.generate_post_layers(n)
        self.pool = nn.MaxPool2d(1, 1)
        self.out = nn.Conv2d(1, 1, 3)

    def generate_pre_layers(self, layer_num):
        layers = np.empty(layer_num, dtype = object)
        for lay in range(0, len(layers)):
            layers[lay] = nn.Conv2d(2, 2, 3)
        return layers

    def generate_post_layers(self, layer_num):
        layers = np.empty(layer_num, dtype = object)
        for lay in range(0, len(layers)):
            layers[lay] = nn.Conv2d(2, 2, 3)
        return layers

    def forward(self, x):
        for pre in self.pre_layers:
            x = hardtanh(pre(x))
            x = self.pool(x)
            
        for post in self.post_layers:
            x = hardtanh(post(x))

        x = sigmoid(self.out(x))
        
        return x

However, not all layers are there:
if __name__ == '__main__':
    layer_num = 5
    net = not_so_great_network(layer_num)
    print(net)

not_so_great_network(
  (pool): MaxPool2d(kernel_size=1, stride=1, padding=0, dilation=1, ceil_mode=False)
  (out): Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1))
)

I am not assigning the variables because this could be more powerful if I can generate networks of varying sizes without copying and pasting. How can I emulate the output so that I may activate nodes with activation functions later?

An alternative would be using ModuleList:
from torch import nn
from torch.nn.functional import hardtanh, sigmoid

class maybe_great_network(nn.Module):
    def __init__(self, n):
        super().__init__()
        self.pre_layers = self.generate_pre_layers(n)
        self.post_layers = self.generate_post_layers(n)
        self.pool = nn.MaxPool2d(1, 1)
        self.out = nn.Conv2d(1, 1, 3)

    def generate_pre_layers(self, layer_num):
        return nn.ModuleList([
            nn.Conv2d(2, 2, 3)
            for l in range(0, layer_num)
        ])

    def generate_post_layers(self, layer_num):
        return nn.ModuleList([
            nn.Conv2d(2, 2, 3)
            for l in range(0, layer_num)
        ])

    def forward(self, x):
        for pre in self.pre_layers:
            x = hardtanh(pre(x))
            x = self.pool(x)
        for post in self.post_layers:
            x = hardtanh(post(x))
        x = sigmoid(self.out(x))
        return x

Then:
&gt;&gt;&gt; m = maybe_great_network(3)
&gt;&gt;&gt; m
maybe_great_network(
  (pre_layers): ModuleList(
    (0): Conv2d(2, 2, kernel_size=(3, 3), stride=(1, 1))
    (1): Conv2d(2, 2, kernel_size=(3, 3), stride=(1, 1))
    (2): Conv2d(2, 2, kernel_size=(3, 3), stride=(1, 1))
  )
  (post_layers): ModuleList(
    (0): Conv2d(2, 2, kernel_size=(3, 3), stride=(1, 1))
    (1): Conv2d(2, 2, kernel_size=(3, 3), stride=(1, 1))
    (2): Conv2d(2, 2, kernel_size=(3, 3), stride=(1, 1))
  )
  (pool): MaxPool2d(kernel_size=1, stride=1, padding=0, dilation=1, ceil_mode=False)
  (out): Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1))
)

