Print doesn't have to be re resolved on every access... Not sure about python but many interpreters do a resolution pass that matches declarations and usages (and decides where data lives, stack, heap, virtual register, whatever)
Statically reasoning about all this (as would be required for optimization) is difficult. Not totally impossible, but not something that the canonical CPython implementation tries to do.