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)
'print' is a global function that can be overridden by user code. The most obvious way to do this would be to define a new function named 'print', but it could also be overridden through less direct means, like from an 'eval' call, manually manipulating the global variables mapping ('globals()'), or by manipulating the '__builtins__' mapping. Maybe this happens in another thread!
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.