Transition from Java to Python
TL;DR Use the Python module dis to show students Python's bytecode alongside Java's bytecode. Avoid the "compiled v. interpreted" rabbit hole to start (because it may lead to more confusion than clarity) and instead show students what's actually happening underneath the hood.



Begin by defining "bytecode" in the context of Python. Here is the Python 3 documentation:


  Python source code is compiled into bytecode, the internal
  representation of a Python program in the CPython interpreter. The
  bytecode is also cached in .pyc files so that executing the same file
  is faster the second time (recompilation from source to bytecode can
  be avoided). This “intermediate language” is said to run on a virtual
  machine that executes the machine code corresponding to each bytecode. [emphasies added]
  Do note that bytecodes are not expected to work between different
  Python virtual machines, nor to be stable between Python releases.


That might sound to students not too dissimilar from their experience with Java and its use of a virtual machine. Also, take a close look at the documentation for the dis module.

Let's consider this very short Python program test.py:

print("Hello, CSEducators.SE!")


If I run the command python -m dis test.py, I see the following Python bytecode:

  1           0 LOAD_NAME                0 (print)
              3 LOAD_CONST               0 ('Hello, CSEducators.SE!')
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              9 POP_TOP
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE


Now, let's write Test.java:

public class Test {
    public static void main(String args[]) {
        System.out.println("Hello, CSEducators.SE!");
    }
}


I run javac Test.java then javap -v Test.class. Here's a small part of what I see:

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello, CSEducators.SE!
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return        
      LineNumberTable:
        line 3: 0
        line 4: 8


This is only a small part of it; the full output leaves a lot to explore. Nonetheless, at this point, students will see fewer differences than similarities (hopefully).

Additionally, consider this SO question: "Compiled vs. Interpreted Languages". It's not quite so black-and-white, and forcing it into a pure dichotomy may do more harm than good.

Let it be a bit messy. That's what learning is all about.