[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