derivation: sick Python trick of the week

has has.temp at virgin.net
Mon Feb 23 22:38:50 CET 2004


"Greg Ewing (using news.cis.dfn.de)" <wmwd2zz02 at sneakemail.com> wrote in message news:<c1bobu$1g1p9e$1 at ID-169208.news.uni-berlin.de>...

> Robert Brewer wrote:
>> In a fit of mad-scientist genius (get out the pitchforks and
torches), I
>> wondered if it might be faster to let Python do *all* the parsing,
and
>> just watch it work and take notes. That's what the code below does.
The
>> sick and twisted part is how one invokes this technique:
> 
> It's not really all that sick, or at least it's not a new
> idea. 

And a lot less sick that hacking the language compiler in my
opinion/experience... (I came to Python from a certain language whose
use of clever-clever compiler hackery makes it ludicrously tetchy
about how you structure your code. ("No, no! You must write it THIS
way!") Such contextual confusion and pain in the brain that you
wouldn't believe, the moment you try writing anything but the most
trivial code in the most trivial fashion.)


> I'm sure there's at least one DB module out there
> somewhere that uses this technique.

Can't speak for DB modules, but must confess to using a close
variation of this technique in my current project - a
MacPython-to-Apple Event Manager bridge - where it's used to assemble
AEM references and test expressions.

Main difference in my design to Robert's is that call capture objects
don't share mutable state. This means users can safely define new
references/test expressions by extending existing ones if they like,
rather than having to create a new one from scratch each time; the
original object won't be changed by this. eg:

    import sick_derive

    x = sick_derive.Expression()

    e1 = ((x.b > 1) | (x.b < -10))
    e2 = (e1 == 4)

    print sick_deriver.Deriver().derive(e2.expr)
    --> (((b > 1) or (b < -10)) == 4)

    print sick_derive.Deriver().derive(e1.expr)
    --> (((b > 1) or (b < -10)) == 4) # !!! e1 has also changed!


vs:

    from appscript import its

    e3 = its.name_extension.isin(['jpg', 'jpeg'])
    e4 = e3 .OR (its.file_type == 'JPEG')

    print e4
    # --> OR(its.name_extension.isin(['jpg', 'jpeg']), its.file_type
== 'JPEG')

    print e3
    # --> its.name_extension.isin(['jpg', 'jpeg'])




> In the case of database queries, you can make this seem
> much more natural by having 'x' represent some meaningful
> object such as a table.
> 
>     db = open_database("favourite_fruits")
>     fruits = db.fruits
>     query = (fruits.kind == "apple") && (fruits.tastiness >= 3)

Once you solve the mutating state problem, you can simply have your
module define a single (generic) 'root' object that users can use to
build any expression. (Appscript stores this object in its global
'its' variable, allowing it to be exported.)


>> I had to replace boolean 'and' and 'or' with binary calls in order
to
>> override them, and 'not' with 'neg'. That makes it even sicker.
> 
> Yes, that's the main nuisance with this technique. 

Yup. (Trouble with these sorts of exercises is sooner or later you
start knocking against the boundaries of what the language can do,
and'll just tie yourself in knots with outrageous acrobatics if you're
not careful.)


>I have
> a PEP idea floating around to make and/or/not overridable,
> for this very purpose. Hmmm... does that make me sick?-)

Raised this idea on the PythonMac SIG last week; it died a quick death
once someone pointed out that boolean operators also perform flow
control (lazy evaluation of operands). Implementing a suitable
override mechanism would be a non-trivial exercise (and seems unlikely
the BDFL would support it).



More information about the Python-list mailing list