[Python-ideas] For/in/as syntax

Matt Gilson matt at getpattern.com
Fri Mar 3 11:32:34 EST 2017


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 <contact at 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 at 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: matt at getpattern.com // P: 603.892.7736

We’re looking for beta testers.  Go here
<https://www.getpattern.com/meetpattern> to sign up!
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20170303/17f0d71a/attachment-0001.html>


More information about the Python-ideas mailing list