lazy (?) evaluation, request for advice

Tim Peters tim_one at email.msn.com
Wed Jan 5 20:30:23 EST 2000


[Alex Martelli]
> I'm planning to prototype in Python a bunch of bridge-
> related functionality, including selective dealing.  In
> selective dealing, the program must repeatedly generate
> random deals, and apply on each generated deal a user-
> supplied function, which can accept or reject the deal ...
> ... I'd much rather let the user write the deal-selection
> function in Python, than in either Tcl, or any custom,
> ad-hoc "little language".

Against that, you invented ad-hoc semantics as you went along; the only
connection to Python appeared to be syntactic.  Let the user write real
Python!  Then as the program gets fancier they'll have the full language to
exploit; with "spades" etc as magical global names, what chance is there to
extend the scheme to a user-defined function that e.g. wants to examine
*two* hands at a time?  (I know! "spades1" and "spades2" <wink>)

> ...
> Further, since the typical user will not be a programmer,
> I would like to make the user's life as easy as possible.
> I.e., despite the important mantra about explicit being
> better than implicit, I think the user would much rather
> be able to write, say:
>     (spades>=6 or hearts>=6) and (3.0<losers<4.0)
> rather than have to mention "current_hand.spades" or
> other such explicit notation.

Here you fall head first into the "ad hoc little language" trap, as if to
say "my users are so stupid there's no chance of getting them to learn
*anything* -- so I'll give them something so flat there's no chance either
it can be extended cleanly later".

def userfunc(x):  # they get to make up the argument name
    ... (x.spades >= 6 or x.hearts >= 6) ... etc

Provided your users can pass a mirror test (i.e., you hold a mirror under
their nose, and they pass if it fogs up <wink>), they can learn this.  After
which, playing __getattr__ tricks with the x object gives you the "lazy"
behavior you want.

> ...
> I've thought about doing the eval repeatedly, within
> a try/except block to catch the NameError's, inserting
> the missing names in the dictionary one by one, but
> I'm suspicious about the performance characteristics
> of such an approach.

You should be more suspicious of the sheer convolution <0.5 wink>.

Anyway, what's expensive there isn't so much the try/except, but the
repeated eval *if* the user's expression is carried around as a string (eval
will recompile it to byte code each time).  If you do go this route, see the
builtin "compile" function for a way to do the compilation to byte code just
once (eval accepts compiled code objects too).  Note also that if your users
truly are morons, there's nothing to stop *you* from marching over their
string and replacing e.g. "spades" with "current_hand.spades" before
compilation.

treat-users-like-grownups-and-they-won't-demand-you-give-
    them-an-allowance<wink>-ly y'rs  - tim






More information about the Python-list mailing list