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