How does the so-called shebang or (hash-bang) work

When the OS is asked to load a program with the exec() system call, the OS needs to know something about the program it's loading. Ordinarily it's a compiled executable (in the so-called ELF format) like what you get when you invoke the compiler.

However, Unix has also traditionally provided a method to specify instead a custom interpreter. If the first 2 bytes of an executable are #!, then Unix will interpret whatever follows this as the name of a program that it will invoke instead. The name of the program - really a command line - ends with a newline. To this command it adds the name of the program.

So if you say #!/usr/bin/env python3 at the beginning of your script script.py, you're really saying that if someone tries to exec() this executable (it must have the +x bit, or executable bit, for that), the OS should instead run /usr/bin/env python3 script.py.

Now you need to learn what /usr/bin/env does - it's a command that allows you to manipulate the environment and then starts another program. Here, we're not asking it to manipulate the environment at all (that would be env VARIABLE=value ...) - but just to start the python3 program.

The reason people recommend #!/usr/bin/env python3 over #!/usr/bin/python3 is that the former will use the execvp system call that respects the user's PATH. See also: How do environment variables work in Unix, and how does PATH work in particular?

I'm getting /usr/bin/env: ‘python3\r’: No such file or directory

If you add #!/usr/bin/env python3 but you created the file with Windows line endings (CRLF), then the first line of the file will be

/usr/bin/env python3<CR><LF>

where <CR> is typically displayed using its string escape \r. This is a telltale sign that the file was processed by a program that applied Windows line endings.

To fix, run dos2unix <file> on the file. To avoid, don't use programs that perform line ending conversions. Set any file transfer programs to binary mode, or better, don't copy files between Windows and Unix machines. Git also has an option to convert on checkout and checkin; turn it off. In your text editor, make sure it's not configured to save with Windows line endings. (In VSCode, it'll say LF in the bottom toolbar.)