In CalcGui’s constructor, buttons are coded as lists of strings; each string represents a
row and each character in the string represents a button. Lambdas are used to save
extra callback data for each button. The callback functions retain the button’s character
and the linked text entry variable so that the character can be added to the end of the
entry widget’s current string on a press.
Notice how we must pass in the loop variable as a default argument to some lambdas
in this code. Recall from Chapter 7 how references within a lambda (or nested def) to
names in an enclosing scope are evaluated when the nested function is called, not when
it is created. When the generated function is called, enclosing scope references inside
the lambda reflect their latest setting in the enclosing scope, which is not necessarily
the values they held when the lambda expression ran. By contrast, defaults are evaluated
at function creation time instead and so can remember the current values of loop var-
iables. Without the defaults, each button would reflect the last iteration of the loop.
Lesson 4: Embedding Beats Parsers
The calculator uses eval and exec to call Python’s parser and interpreter at runtime
instead of analyzing and evaluating expressions manually. In effect, the calculator runs
embedded Python code from a Python program. This works because Python’s devel-
opment environment (the parser and bytecode compiler) is always a part of systems
that use Python. Because there is no difference between the development and the de-
livery environments, Python’s parser can be used by Python programs.
The net effect here is that the entire expression evaluator has been replaced with a single
call to eval or exec. In broader terms, this is a powerful technique to remember: the
Python language itself can replace many small, custom languages. Besides saving de-
velopment time, clients have to learn just one language, one that’s potentially simple
enough for end-user coding.
Furthermore, Python can take on the flavor of any application. If a language interface
requires application-specific extensions, just add Python classes, or export an API for
use in embedded Python code as a C extension. By evaluating Python code that uses
application-specific extensions, custom parsers become almost completely
unnecessary.
There’s also a critical added benefit to this approach: embedded Python code has access
to all the tools and features of a powerful, full-blown programming language. It can
use lists, functions, classes, external modules, and even larger Python tools like tkinter
GUIs, shelve storage, multiple threads, network sockets, and web page fetches. You’d
probably spend years trying to provide similar functionality in a custom language
parser. Just ask Guido.
Running code strings
This module implements a GUI calculator in some 45 lines of code (counting comments
and blank lines). But truthfully, it “cheats.” Expression evaluation is delegated entirely
to Python. In fact, the built-in eval and exec tools do most of the work here:
1460 | Chapter 19: Text and Language