Given a program X and compiler/interpreter Y. If there exists such input for X which doesn't influence an output of Y then Y is a compiler, otherwise it is an interpreter.
The problem here is to define what is an output of an interpreter... like all possible states during an evaluation of X maybe.
Also, compilers often have optimizations that are polynomial on size of input.
It seems to me that "compiling" is basically a special case of partial evaluators. You do some processing up front to get some speedup at runtime. And it isn't limited to constant factor speedup, since compilers are allowed to reduce O(n^2) code to O(1) runtime.
What seems to me a useful distinction is: "compiling" transforms a representation of a program into a different representation of a program. "interpreting" takes a representation of a program and produces actions/results.
Pretty much any program execution today is some mix of compiling and interpreting. Even running a "binary" can have a compilation step within the CPU to microcode.
Just, some ways of executing a program put more work into compiling before interpreting. And some types of compiling emit their output, rather than feeding it directly into an interpreter.
So, pragmatically, cpython is an "interpreter" because it reads source code and produces behavior, rather than a different representation of the source code. javac is a "compiler" because it transforms source code into another representation, which can then be read by the jvm interpreter.
Internally, jvm can have separate compilation and interpretation steps, but from a user's point of view, there's no way of getting a snapshot of its compilation output, so it's not usefully a compiler in that sense, even if it does use compiler techniques.
cpython can emit .pyc files, so it is a compiler in that sense, but it's still also an interpreter, and mostly used as an interpreter.
golang is just a compiler, it doesn't do any interpreting. The interpreting is done by the target architecture.