Checking for the empty (or zero) gradients was just used in this test to show that the behavior is indeed as I want it to be. I.e. the first
loss_2.backward(inputs=list(network.second_part.parameters()), retain_graph=True)

call would populate the gradients of second_part's parameters and leave the others untouched.
You should be fine using optimizer.zero_grad() at the beginning.