[Python-ideas] For/in/as syntax
Brice PARENT
contact at brice.xyz
Fri Mar 3 13:00:54 EST 2017
Thanks Matthias for taking the time to give your opinion about it.
Just to set the focus where I may have failed to point it:
the main purpose of this proposal is the creation of the object itself,
an object representing the loop. What we can do with it is still a
sub-level of this proposal, as the presence of this object is what
allows all these simplifications, and offers a lot of possibilities.
Le 03/03/17 à 17:21, Matthias Bussonnier a écrit :
>
>> A word about compatibility and understandability before:
>> "as" is already a keyword, so it is already reserved and easy to parse. It
>> couldn't be taken for its other uses (context managers, import statements
>> and
>> exceptions) as they all start with a specific and different keyword. It
>> would
>> have a really similar meaning, so be easy to understand. It doesn't imply
>> any
>> incompatibility with previous code, as current syntax would of course still
>> be
>> supported, and it doesn't change anything anywhere else (no change in
>> indentation levels for example, no deprecation).
> That still would make it usable only on Python 3.7+ It seem to me you ca already
> do that now with helper object/function:
Yes, what I meant is that it wouldn't break any code from previous
versions. But as with any new keyword or syntax evolution, newer code
wouldn't get well in older versions.
> In [1]: class Loop:
> ...:
> ...: def __init__(self, iterable):
> ...: self._it = iter(iterable)
> ...: self._break = False
> ...:
> ...: def __next__(self):
> ...: if self._break:
> ...: raise StopIteration
> ...: return (self, next(self._it))
> ...:
> ...: def __iter__(self):
> ...: return self
> ...:
> ...: def brk(self):
> ...: self._break = True
> ...:
> ...:
>
> In [2]: for loop,i in Loop(range(10)):
> ...: if i==5:
> ...: loop.brk()
> ...: print(i)
> 1
> 2
> 3
> 4
> 5<break>
A 2 level breaking would have to be used this way :
for outerloop, i in Loop(range(4)):
for innerloop, j in Loop(range(3)):
if i==2 and j==1:
outerloop.brk()
break # this
print(i, j)
if outerloop.broken:
break # or continue. For the code following this line not to
be executed
break() and continue() methods (which I named this way to reflect the
behaviour of the statements, but I'm not sure whether it could or should
be kept this way) are only an improvement on the keywords in nested
loops or complex situations where explicitly exiting a certain loop
helps readability by removing a bunch of conditions and assignments.
It's also allowing to pass this function as a callback to something
else, but I'm not sure it would be that useful.
>
>> Syntax:
>> for element in elements as elements_loop:
>> assert type(elements_loop) is ForLoopIterationObject
>>
>> Properties and methods (non exhaustive list, really open to discussion and
>> suggestions):
>> for element in elements as elements_loop:
>> elements_loop.length: int # len ? count ?
>> elements_loop.counter: int # Add a counter0, as in Django? a counter1 ?
>> elements_loop.completed: bool
>> elements_loop.break()
>> elements_loop.continue()
>> elements_loop.skip(count=1)
> I did not implement these, but they are as feasible. (break is keyword
> though, so you need to rename it)
Length, counter and completed (and .skip() in some ways) are just
helping the readability while allowing a more concise code, as they
don't force to create temporary variables. But they belong here as a
part of the whole thing. If the object had to be integrated, for
.break(), .continue(), .skip() and any other useful method, those little
improvements would be nice to have and help to keep your code look more
like the design in your head.
.break() and .continue() help as they modify the flow. They roughly do
this :
LOOP1
LOOP2
if something:
LOOP1.break() # similar to calling break repeatedly once
for every loop until LOOP1 included, so going straight to
"something_after_every_loop"
something_else
yet_another_thing
something_after_every_loop
The same goes for .continue(), calling break in every inner loop, and
continue in the one the method is called from.
>
>> Examples of every presented element (I didn't execute the code, it's just to
>> explain the proposal):
>>
>> ##################################################
>> # forloop.counter and forloop.length
>>
>> for num, dog in enumerate(dogs):
>> print(num, dog)
>>
>> print("That's a total of {} dogs !".format(len(dogs)))
>>
>> # would be equivalent to
>>
>> for dog in dogs as dogs_loop:
>> print(dogs_loop.counter, dog)
> I find it less readable than enumerate.
Well, I find both readable, but the second only requires to focus on the
loop itself, not on how enumerate and unpacking work.
The second syntax however allows the exact same syntax when you work
with dicts, lists or tuples. Here, dogs may be of any type of iterable,
while in first version, you'd need to implement a counter to have a
unified loop syntax (I'm not saying we should always be able to replace
those types, just that in that case it makes sense to me).
Brice
More information about the Python-ideas
mailing list