Thanks for the idea and prior research. I'm not convinced that this warrants new syntax. Most of what you propose (skipping, counting, exposing a length if available, tracking if completed) could be solved already by creating your own wrapper around an iterable:
elements_loop = ForLoopIterationObject(elements) for element in elements_loop: ... Some of your proposal simply can't be done perfectly (e.g. length) since iterables can have infinite length. There is `__length_hint__` IIRC which might (or might not) give a rough estimate. You also couldn't do `elements_loop.continue()` or `elements_loop.break()` in a python-based wrapper without special interpreter magic (at least now as far as I can imagine...). I realize that is the point of this proposal as it would allow breaking/continuing nested loops, however, that proposal was already rejected because Guido didn't think that the use-cases were compelling enough to add new syntax/complexity to the python language and most of the time you can address that case by adding a function here or there. In this proposal, you're adding significantly _more_ complexity than was proposed in PEP-3136. Also, you're creating an object for every loop -- and that object needs to do stuff when iterating (e.g. increment a counter). It's very unlikely that this could be anything but a performance drain compared to existing solutions (only use `enumerate` when you need it). I suppose that the interpreter could be smart enough to only create a ForLoopIterationObject when it actually needs it (e.g. when there is an `as` statement), but that shifts a lot more complexity onto the language implementors -- again for perhaps little benefit. On Fri, Mar 3, 2017 at 1:14 AM, Brice PARENT <cont...@brice.xyz> wrote: > Object: Creation of a ForLoopIterationObject object (name open to > suggestions) > using a "For/in/as" syntax. > > Disclaimer: > I've read PEP-3136 which was refused. The present proposal includes a > solution > to what it tried to solve (multi-level breaking out of loops), but is a > more > general proposal and it offers many unrelated advantages and > simplifications. > It also gives another clean way of making a kind of for/except/else syntax > of > another active conversation. > > What it tries to solve: > During the iteration process, we often need to create temporary variables > (like > counters or states) or tests that don't correspond to the logic of the > algo, > but are required to make it work. This proposal aims at removing these > unnecessary and hard to understand parts and help write simpler, cleaner, > more > maintainable and more logical code. > So, more PEP-8 compliant code. > > How does it solve it: > By instanciating a new object when starting a loop, which contains some > data > about the iteration process and some methods to act on it. The list of > available properties and methods are yet to be defined, but here are a > subset > of what could be really helpful. > > 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). > > 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) > > 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) > > print("That's a total of {} dogs !".format(dogs_loop.length)) > > # -> cleaner, and probably faster as it won't have to call len() or > enumerate() > #(but I could easily be wrong on that) > > > ################################################## > # forloop.length when we broke out of the list > > small_and_medium_dogs_count = 0 > for dog in generate_dogs_list_by_size(): > if dog.size >= medium_dog_size: > break > > small_and_medium_dogs_count += 1 > > print("That's a total of {} small to medium dogs !".format( > small_and_medium_dogs_count)) > > # would be equivalent to > > for dog in generate_dogs_list_by_size() as dogs_loop: > if dog.size >= medium_dog_size: > break > > print("That's a total of {} small to medium dogs !".format( > dogs_loop.length - 1)) > > # -> easier to read, less temporary variables > > ################################################## > # forloop.break(), to break out of nested loops (or explicitly out of > current > #loop) - a little like pep-3136's first proposal > > has_dog_named_rex = False > for owner in owners: > for dog in dogs: > if dog.name == "Rex": > has_dog_named_rex = True > break > > if has_dog_named_rex: > break > > # would be equivalent to > > for owner in owners as owners_loop: > for dog in dogs: # syntax without "as" is off course still supported > if dog.name == "Rex": > owners_loop.break() > > # -> way easier to read and understand, less temporary variables > > ################################################## > # forloop.continue(), to call "continue" from any nested loops (or > explicitly > #in active loop) > > has_dog_named_rex = False > for owner in owners: > for dog in owner.dogs: > if dog.name == "Rex": > has_dog_named_rex = True > break > > if has_dog_named_rex: > continue > > # do something > > # would be equivalent to > > for owner in owners as owners_loop: > for dog in owner.dogs: > if dog.name == "Rex": > owners_loop.continue() > > # do something > > # -> way easier to read and understand, less temporary variables > > ################################################## > # forloop.completed, to know if the list was entirely consumed or "break" > was > #called (or exception caught) - might help with the for/except/elseproposal > > broken_out = False > for dog in dogs: > if dog.name == "Rex": > broken_out = True > break > > if broken_out: > print("We didn't consume all the dogs list") > > # would be equivalent to > > for dog in dogs as dogs_loop: > if dog.name == "Rex": > break > > if not dogs_loop.completed: > print("We didn't consume all the dogs list") > > # -> less temporary variables, every line of code is relevant, easy to > #understand > > ################################################## > # forloop.skip > # In the example, we want to skip 2 elements starting from item #2 > > skip = 0 > for num, dog in enumerate(dogs): > if skip: > skip -= 1 > continue > > if num == 2: > skip = 2 > > # would be equivalent to > > for dog in dogs as dogs_loop: > if dogs_loop.counter == 2: > dogs_loop.skip(2) > > # -> way easier to read and understand, less temporary variables > > # Notes : > # - Does a call to forloop.skip() implies a forloop.continue() call or > does > # the code continue its execution until the end of the loop, which > will > # then be skipped? Implying the .continue() call seems less ambiguous > to > # me. Or the method should be called skip_next_iteration, or something > # like that. > # - Does a call to forloop.skip(2) adds 2 to forloop.length or not? > # -> kwargs may be added to allow both behaviours for both questions. > > # We could allow the argument to be a function that accepts a single > argument > #and return a boolean, like > dogs_loop.skip(lambda k: k % 3 == 0) # Execute the code on multiples of 3 > only > > > ################################################## > > Thoughts : > - It would allow to pass forloop.break and forloop.continue as callback to > other functions. Not sure yet if it's a good or a bad thing (readability > against what it could offer). > - I haven't yet used much the asynchronous functionalities, so I couldn't > yet > think about the implications of such a new syntax to this (and what > about a > lazy keyword in here?) > - I suppose it's a huge work to create such a syntax. And I have no idea > how > complicated it could be to create methods (like break() and continue()) > doing > what keywords were doing until now. > - I'm not sure if that would make sense in list comprehensions, despite > being > confusing. > - Would enable to support callback events like forloop.on_break. But would > there be a need for that? > - Would this have a major impact on the loops execution times? > - would a "while condition as condition_loop:" be of any use too? > > Sorry for the very long message, I hope it will get your interest. And I > also > hope my English was clear enough. > > Brice Parent > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ > -- [image: pattern-sig.png] Matt Gilson // SOFTWARE ENGINEER E: m...@getpattern.com // P: 603.892.7736 We’re looking for beta testers. Go here <https://www.getpattern.com/meetpattern> to sign up!
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/