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

_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to