There is no zero_grad between errD_real.backward() errD_fake.backward(), which means the gradients of these two are accumulated (sumed). If you look closer, the computation of these two is the same with back propagating errD (= errD_real + errD_fake). So the first two backwards(errD_real.backward & errD_fake.backward) are equivalent to errD.backward().
However, when optimizing generator, the gradient for the discriminator parameters is not zeroed! It still has the gradient for optimizing the discriminator.
If you plot a computation graph for the discriminator, you will notice that the gradient of theta(D) does not affect the generator. It’s true that theta(D).grad is been inaccurately accumulated. But what will be passed back to the generator is theta(D) and theta(D) will not be affected by theta(D).grad since we are not updating it.