bc utility are
interpreters. The good old bash shell you know and love is an
interpreter. The program python3 is the python
interpreter we will be using.
Note: Java is an interesting special case.
The Java Compiler (javac) compiles your source code,
but it compiles it to bytecode, not CPU
instructions. Bytecode is a simple language, and the
program java is an interpreter that interprets
the Java bytecode language. So if someone asks: "Is Java
compiled or interpreted?", you answer "yes".
Important! The interpreter keeps reading
input until it hits end-of-file. So in interactive mode, use
ctrl-d to simulate end of file and, thus, exit
the interpreter.
Note: some things, like x = 2 don't have a result, so nothing
is printed. Also, a variable all on its own evaluates to its value, which
allows you in interactive mode to inspect the variable's value.
$ python3 ←running python3 with no arguments we get interactive mode >>> 3 + 5 8 >>> x = 1.455 >>> x 1.455 >>> 2*x + 0.2 3.1100000000000003When python is launched with standard input tied to a terminal, it automatically goes into interactive mode (like in the previous example). If standard input is coming from somewhere else, or you've directed python to read its code from a file (which we'll see later), it automatically chooses non-interactive mode. In non-interactive mode we don't get a REPL (Read-Eval_Print-Loop). Instead, while the source code is indeed read in, no prompt is printed out. And while after being read the source code is indeed evaluated, the results are not printed (though you can call the
print(·) function explicitly to print
things).
$ python3 Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> 3*4 12 >>> |
Run this way we get interactive mode because standard in is tied to the terminal. |
$ echo "3*4" | python3
$ |
Run this way we get non-interactive mode because standard
in is coming from a pipe, not a terminal. 3*4 gets evaluated, but because we are in non-interactive mode, the result is not printed. |
$ echo "3*4" | python3 -i
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 12
>>> |
The -i option forces the interpreter into
interactive mode.3*4 gets evaluated and printed because we are in interactive mode. And ... with the -i option input
moves over to the terminal once stdin is exhausted, which
is why the interpreter doesn't automatically exit.
|
print(·) function, which
writes its argument to standard out.
$ echo "print(3*4)" | python3
12
The advantage to using print() to produce output rather than
relying on the interactive shell to print results is that we can
take the code we used in our interpreter session and put it in a file
that we can run over and over.
| file: ex01.py | run python with file arg |
|---|---|
rate = 5.85 bal = 10500.00 interest = bal * rate/100 print(interest) |
$ python3 ex01.py
614.2499999999999 |
| file: ex02.py | run python with file arg | NOTE |
|---|---|---|
print("starting ...")
rate = 5.85
bal = 10500.00
interest = bal * rate/100
print("... calculations complete!")
print(3/asdf) |
$ python3 ex04.py
starting ...
... calculations complete!
Traceback (most recent call last):
File "/home/scs/wcbrown/courses/F24SI342/pythonPlay/ex04.py", line 6, in <module>
print(3/asdf)
NameError: name 'asdf' is not defined |
There is one way in which non-interactive mode is different
from interactive mode without the prompt and printing outputs.
It turns out that the interpreter tries to read and
parse the whole input at once before executing. So if there
is a syntax error, it will
be detected and the interpreter will exit because of it
before evaluating anything. Note: an undefined variable error is *not* a syntax error in Python, even though it is a syntax error in C/C++/Java. Also: note that the interpreter exits on error when in non-interactive mode. |
One last thing!
You can use the -i option along with a file argument so that, after executing the
code in the file, you drop back into interactive mode, and any
functions or variables defined in the file are available for you to use.
$ python3 -i ex01.py i = 614.2499999999999 >>> rate 5.85 >>>This is really nice if you are defining functions and you want to test your function definitions as you go. That, in turn, makes bottom up programming really easy. Top-down programming with the technique of "stubbing out" functions (see IC211 notes!) is also easier because of this.
| file: ex03.py | run |
|---|---|
def sqr(x):
return x*x
|
$ python3 -i ex05.py >>> sqr(7) 49 >>> |
The purpose of this activity is to get you thinking about how
easy it is to do bottom up programming in an interpreted
language like python, because you can easily test each
function as you write it by running python3 -i yourcode.py
and calling the function on different inputs from within the
interpreter. [Important! the interpreter
has the up-arrow-for-past-commands feature, where the past
command history goes back over past times you've launched the
interpreter. This is helpful!]
Your ultimate goal is to define a function "grade" that takes an array G of assignment grades (0-100) and array W of weights, where len(W) = len(G), and returns an overall letter grade from the "normalized weighted sum" of the assignment grades.
| A | B | C | D | F |
|---|---|---|---|---|
| $g \geq 90$ | $90 \gt g \geq 78$ | $78 \gt g \geq 66$ | $66 \gt g \geq 50$ | $50 \gt g$ |
sol.py and add code to it, following
the below plan as you go. When you test, run as
python3 -i sol.py so you can easily test your
functions. Notice how the interpreter makes it easy to follow
our gold standard approach to programming: write a very small
amount of code, then stop to test and debug!
sum(W) that takes an
array/list of numbers and returns the sum of those numbers.
>>> sum([10.5,6,4.25,5])
25.75
wsum(G,W) that takes an
array/list of grades and of weights and returns the weighted
sum of those numbers.
>>> wsum([100,50,50,80],[.5,1,1,.25])
170.0
nwsum(G,W) that takes an
array/list of grades and of weights and returns the
normalized weighted
sum of those numbers.
>>> nwsum([100,50,50,80],[.5,1,1,.25])
61.81818181818182
grademap(x) that takes
number $x$ in the range $[0,100]$ and returns the letter
grade (as a string) as described above.
>>> grademap(95) 'A' >>> grademap(78.5) 'B' >>> grademap(66.0) 'C'
grade(G,W) that takes an
array/list of grades and of weights and returns the overall letter
grade as described above.
>>> grade([100,50,50,80],[.5,1,1,.25]) 'D' >>> grade([100,50,50,80],[3,2,2,1]) 'C' >>> grade([100,50,50,80],[3,1,1,2]) 'B' >>> grade([100,50,50,80],[10,1,1,1]) 'A' >>> grade([100,50,50,80],[-1,10,10,1]) 'F'
python3 -i foo.py or even as python3 foo.py. Instead we
use a trick you should know already from systems programming.
In unix, if a file foo starts with line
#!<path>and you chmod +x it, then the shell command
./foo will cause the
shell to call <path> with ./foo as a command line argument.
Note: the #! line is often called a "shebang".
This is the same mechanism you saw in Systems Programming for
making an executable script out of a file of bash commands.
Bash is, as I hope you now see, and interpreted language. The
bash shell is the interpreter, and we use it interactively
(as a read-eval-print loop) all the time! But, sometimes we
also want to store sequences of commands in scripts. Just like Python.
#!/usr/bin/env python3The program "env" uses the PATH environment variable to find the python3 executable (or whatever its argument is) and executes that with the current file as its argument. Just an FYI, because you may see it.
| file: ex04.py | running |
|---|---|
#!/usr/bin/python3
def sqr(x):
return x*x
print(sqr(42)) |
$ ./ex04.py
1764 |
python3 ex04.py
import command.
How import works is actually a big topic, and the
main reason is that we have to wade into namespaces.
To finish off this class, just be aware that if you have a
file foo.py you would load that code into the
interpret with the command
import foo... which executes the code in foo.py (i.e. you leave off the .py part in the import statement). The catch is that if a name
bar is defined in foo.py, perhaps
as a variable or function or class, that binding inside the
interpreter will go by the name foo.bar. In other
words, all the names defined in foo.py belong to
the namespace foo not to the default namespace.
Hence, if you use our code from the activity as an example and
assume it is in a file dbsol.py:
$ python3 Python 3.10.12 (main, Jul 29 2024, 16:56:48) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import dbsol >>> grademap(78.5) Traceback (most recent call last): File "<stdin>", line 1, in >module> NameError: name 'grademap' is not defined >>> dbsol.grademap(78.5) 'B' >>>If you want names defined in the file you are importing to be loaded into the same namespace you are in, you can load the file and have a specific name defined in your current namespace:
from dbsol import grademap
\___/ \______/
file dbsol.py name you want
... or just "load all the names" with
from dbsol import *
\___/ |
file dbsol.py I want everything
So we end with this example:
$ python3 Python 3.10.12 (main, Jul 29 2024, 16:56:48) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from dbsol import * >>> grademap(78.5) 'B' >>>There will be more to say later on
import.