Iterator addition

Bengt Richter bokr at oz.net
Sun Nov 13 16:44:43 EST 2005


On Sun, 13 Nov 2005 17:31:32 +0100, Reinhold Birkenfeld <reinhold-birkenfeld-nospam at wolke7.net> wrote:

>bearophileHUGS at lycos.com wrote:
>> Tom Anderson:
>>> And we're halfway to looking like perl already! Perhaps a more pythonic
>>> thing would be to define a "then" operator:
>>> all_lines = file1 then file2 then file3
>> 
>> Or a "chain" one:
>>> all_lines = file1 chain file2 chain file3
>
>That's certainly not better than the chain() function. Introducing new operators
>for just one application is not pythonic.
Agreed. But here's a thought:

What if
    expr1 expr2

were as legal as
    expr1, expr2

and had well defined meaning? The meaning I have in mind is
that expr1 and expr2 (etc if more) should be evaluated just as
they are for building a tuple. Then, instead of taking the values
on the stack and building the tuple, the whole sequence is evaluated
by looking for three possible methods on the objects. First, a __unaryop__
method is sought. If present, that is sufficient to evaluate
    expr1 expr2
as
    expr1.__unaryop__(expr2)

Now that effectively becomes the first element replacing the first two
elements of the sequence. If it is the only element, that is the value
of the whole. If there are more elements, we start as if with a new sequence,
so
    expr1 expr2 expr2

is equivalent to
    (expr1 expr2 expr3)

is equivalent to
    ((expr1 expr2) expr3)

so we evaluate
    (expr1 expr2).__unaryop__(expr3)

if that __unaryop__ exists. If not, we look for a postfix unary op method on expr3
and if present, we get

    expr3.__pfunaryop__((expr1 expr2))

as the value of the whole. Or to write it out fully,

    expr3.__pfunaryop__(expr1.__unaryop__(expr2))

Now if expr1 had no op method, we would look for __pfunaryop__ on expr2
and if found the value would obviously be
    expr2.__pfunaryop__(expr1)

If there is no __pfunaryop__ on expr2, we look for a __binaryop__ method,
(BTW meaning unary ops are given precedence). If we have expr2.__binaryop__,
then we need an expr3, or it is an error. If present, we get a value for

    expr1 expr2 expr3

that is

    expr2.__binaryop__(expr1, expr3)

Now if e.g. a bare-name expression MINUS is bound to an object that has
both a __unaryop__ and a __binaryop__ method, parens can as usual control
the evaluation. E.g.,

     MINUS a MINUS b
evaluates to
     MINUS.__binaryop__(MINUS.__unaryop__(a), b)
whereas
     MINUS (a MINUS b)
evaluates to
     MINUS.__unaryop__(MINUS.__binaryop__(a, b))

Presumably the byte codes for the above would look something like
(faked!! minor revision of tuple-building code ;-)

 >>> dis.dis(compile('MINUS a MINUS b','','eval'))
   0           0 LOAD_NAME                0 (MINUS)
               3 LOAD_NAME                1 (a)
               6 LOAD_NAME                0 (MINUS)
               9 LOAD_NAME                2 (b)
              12 EVAL_EXPR_SEQ            4
              15 RETURN_VALUE
 >>> dis.dis(compile('MINUS (a MINUS b)','','eval'))
   0           0 LOAD_NAME                0 (MINUS)
               3 LOAD_NAME                1 (a)
               6 LOAD_NAME                0 (MINUS)
               9 LOAD_NAME                2 (b)
              12 EVAL_EXPR_SEQ            3
              15 EVAL_EXPR_SEQ            2
              18 RETURN_VALUE

I think this might have some interesting possibilities ;-)

I'm sure there can be some interesting ambiguities in the
syntax of adjacent blank-separated expressions, but nothing
parens couldn't overcome, IWT. Built-in operators with
symbols such as +, -, *, / etc. would of course not be
treated as as objects in the above sense. I.e.,
even if expr1 had a __unaryop__ method,
    expr1 - expr2
could not become
    expr1.__unaryop__(-expr2)
unless you forced the issue with
    expr1 (-expr2)

Ok, this should be fun ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list