<div dir="ltr"><br><br><div class="gmail_quote"><div dir="ltr">On Thu, 2 Mar 2017 at 03:07 Wolfgang Maier <<a href="mailto:wolfgang.maier@biologie.uni-freiburg.de">wolfgang.maier@biologie.uni-freiburg.de</a>> wrote:</div><div>[SNIP]</div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
As always though, reality can be expected to be quite a bit more<br class="gmail_msg">
complicated than theory so I decided to check the stdlib for real uses<br class="gmail_msg">
of break. This is quite a tedious task since break is used in many<br class="gmail_msg">
different ways and I couldn't come up with a good automated way of<br class="gmail_msg">
classifying them. So what I did is just go through stdlib code (in<br class="gmail_msg">
reverse alphabetical order) containing the break keyword and put it into<br class="gmail_msg">
categories manually. I only got up to socket.py before losing my<br class="gmail_msg">
enthusiasm, but here's what I found:<br class="gmail_msg">
<br class="gmail_msg">
- overall I looked at 114 code blocks that contain one or more breaks<br class="gmail_msg"></blockquote><div><br></div><div>I wanted to say thanks for taking the time to go through the stdlib and doing such a thorough analysis of the impact of your suggestion! It always helps to have real-world numbers to know whether an idea will be useful (or not).</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br class="gmail_msg">
- 84 of these are trivial use cases that simply break out of a while<br class="gmail_msg">
True block or terminate a while/for loop prematurely (no use for any<br class="gmail_msg">
follow-up clause there)<br class="gmail_msg">
<br class="gmail_msg">
- 8 more are causing a side-effect before a single break, and it would<br class="gmail_msg">
be pointless to put this into an except break clause<br class="gmail_msg">
<br class="gmail_msg">
- 3 more cause different, non-redundant side-effects before different<br class="gmail_msg">
breaks from the same loop and, obviously, an except break clause would<br class="gmail_msg">
not help them either<br class="gmail_msg">
<br class="gmail_msg">
=> So the vast majority of breaks does *not* need an except break *nor*<br class="gmail_msg">
an else clause, but that's just as expected.<br class="gmail_msg">
<br class="gmail_msg">
<br class="gmail_msg">
Of the remaining 19 non-trivial cases<br class="gmail_msg">
<br class="gmail_msg">
- 9 are variations of your classical search idiom above, i.e., there's<br class="gmail_msg">
an else clause there and nothing more is needed<br class="gmail_msg">
<br class="gmail_msg">
- 6 are variations of your "nested side-effects" form presented above<br class="gmail_msg">
with debatable (see above) benefit from except break<br class="gmail_msg">
<br class="gmail_msg">
- 2 do not use an else clause currently, but have multiple breaks that<br class="gmail_msg">
do partly redundant things that could be combined in a single except<br class="gmail_msg">
break clause<br class="gmail_msg">
<br class="gmail_msg">
- 1 is an example of breaking out of two loops; from sre_parse._parse_sub:<br class="gmail_msg">
<br class="gmail_msg">
[...]<br class="gmail_msg">
     # check if all items share a common prefix<br class="gmail_msg">
     while True:<br class="gmail_msg">
         prefix = None<br class="gmail_msg">
         for item in items:<br class="gmail_msg">
             if not item:<br class="gmail_msg">
                 break<br class="gmail_msg">
             if prefix is None:<br class="gmail_msg">
                 prefix = item[0]<br class="gmail_msg">
             elif item[0] != prefix:<br class="gmail_msg">
                 break<br class="gmail_msg">
         else:<br class="gmail_msg">
             # all subitems start with a common "prefix".<br class="gmail_msg">
             # move it out of the branch<br class="gmail_msg">
             for item in items:<br class="gmail_msg">
                 del item[0]<br class="gmail_msg">
             subpatternappend(prefix)<br class="gmail_msg">
             continue # check next one<br class="gmail_msg">
         break<br class="gmail_msg">
[...]<br class="gmail_msg">
<br class="gmail_msg">
This could have been written as:<br class="gmail_msg">
<br class="gmail_msg">
[...]<br class="gmail_msg">
     # check if all items share a common prefix<br class="gmail_msg">
     while True:<br class="gmail_msg">
         prefix = None<br class="gmail_msg">
         for item in items:<br class="gmail_msg">
             if not item:<br class="gmail_msg">
                 break<br class="gmail_msg">
             if prefix is None:<br class="gmail_msg">
                 prefix = item[0]<br class="gmail_msg">
             elif item[0] != prefix:<br class="gmail_msg">
                 break<br class="gmail_msg">
         except break:<br class="gmail_msg">
             break<br class="gmail_msg">
<br class="gmail_msg">
         # all subitems start with a common "prefix".<br class="gmail_msg">
         # move it out of the branch<br class="gmail_msg">
         for item in items:<br class="gmail_msg">
             del item[0]<br class="gmail_msg">
         subpatternappend(prefix)<br class="gmail_msg">
[...]<br class="gmail_msg">
<br class="gmail_msg">
<br class="gmail_msg">
- finally, 1 is a complicated break dance to achieve sth that clearly<br class="gmail_msg">
would have been easier with except break; from typing.py:<br class="gmail_msg">
<br class="gmail_msg">
[...]<br class="gmail_msg">
     def __subclasscheck__(self, cls):<br class="gmail_msg">
         if cls is Any:<br class="gmail_msg">
             return True<br class="gmail_msg">
         if isinstance(cls, GenericMeta):<br class="gmail_msg">
             # For a class C(Generic[T]) where T is co-variant,<br class="gmail_msg">
             # C[X] is a subclass of C[Y] iff X is a subclass of Y.<br class="gmail_msg">
             origin = self.__origin__<br class="gmail_msg">
             if origin is not None and origin is cls.__origin__:<br class="gmail_msg">
                 assert len(self.__args__) == len(origin.__parameters__)<br class="gmail_msg">
                 assert len(cls.__args__) == len(origin.__parameters__)<br class="gmail_msg">
                 for p_self, p_cls, p_origin in zip(self.__args__,<br class="gmail_msg">
                                                    cls.__args__,<br class="gmail_msg">
                                                    origin.__parameters__):<br class="gmail_msg">
                     if isinstance(p_origin, TypeVar):<br class="gmail_msg">
                         if p_origin.__covariant__:<br class="gmail_msg">
                             # Covariant -- p_cls must be a subclass of<br class="gmail_msg">
p_self.<br class="gmail_msg">
                             if not issubclass(p_cls, p_self):<br class="gmail_msg">
                                 break<br class="gmail_msg">
                         elif p_origin.__contravariant__:<br class="gmail_msg">
                             # Contravariant.  I think it's the<br class="gmail_msg">
opposite. :-)<br class="gmail_msg">
                             if not issubclass(p_self, p_cls):<br class="gmail_msg">
                                 break<br class="gmail_msg">
                         else:<br class="gmail_msg">
                             # Invariant -- p_cls and p_self must equal.<br class="gmail_msg">
                             if p_self != p_cls:<br class="gmail_msg">
                                 break<br class="gmail_msg">
                     else:<br class="gmail_msg">
                         # If the origin's parameter is not a typevar,<br class="gmail_msg">
                         # insist on invariance.<br class="gmail_msg">
                         if p_self != p_cls:<br class="gmail_msg">
                             break<br class="gmail_msg">
                 else:<br class="gmail_msg">
                     return True<br class="gmail_msg">
                 # If we break out of the loop, the superclass gets a<br class="gmail_msg">
chance.<br class="gmail_msg">
         if super().__subclasscheck__(cls):<br class="gmail_msg">
             return True<br class="gmail_msg">
         if self.__extra__ is None or isinstance(cls, GenericMeta):<br class="gmail_msg">
             return False<br class="gmail_msg">
         return issubclass(cls, self.__extra__)<br class="gmail_msg">
[...]<br class="gmail_msg">
<br class="gmail_msg">
which could be rewritten as:<br class="gmail_msg">
<br class="gmail_msg">
[...]<br class="gmail_msg">
     def __subclasscheck__(self, cls):<br class="gmail_msg">
         if cls is Any:<br class="gmail_msg">
             return True<br class="gmail_msg">
         if isinstance(cls, GenericMeta):<br class="gmail_msg">
             # For a class C(Generic[T]) where T is co-variant,<br class="gmail_msg">
             # C[X] is a subclass of C[Y] iff X is a subclass of Y.<br class="gmail_msg">
             origin = self.__origin__<br class="gmail_msg">
             if origin is not None and origin is cls.__origin__:<br class="gmail_msg">
                 assert len(self.__args__) == len(origin.__parameters__)<br class="gmail_msg">
                 assert len(cls.__args__) == len(origin.__parameters__)<br class="gmail_msg">
                 for p_self, p_cls, p_origin in zip(self.__args__,<br class="gmail_msg">
                                                    cls.__args__,<br class="gmail_msg">
                                                    origin.__parameters__):<br class="gmail_msg">
                     if isinstance(p_origin, TypeVar):<br class="gmail_msg">
                         if p_origin.__covariant__:<br class="gmail_msg">
                             # Covariant -- p_cls must be a subclass of<br class="gmail_msg">
p_self.<br class="gmail_msg">
                             if not issubclass(p_cls, p_self):<br class="gmail_msg">
                                 break<br class="gmail_msg">
                         elif p_origin.__contravariant__:<br class="gmail_msg">
                             # Contravariant.  I think it's the<br class="gmail_msg">
opposite. :-)<br class="gmail_msg">
                             if not issubclass(p_self, p_cls):<br class="gmail_msg">
                                 break<br class="gmail_msg">
                         else:<br class="gmail_msg">
                             # Invariant -- p_cls and p_self must equal.<br class="gmail_msg">
                             if p_self != p_cls:<br class="gmail_msg">
                                 break<br class="gmail_msg">
                     else:<br class="gmail_msg">
                         # If the origin's parameter is not a typevar,<br class="gmail_msg">
                         # insist on invariance.<br class="gmail_msg">
                         if p_self != p_cls:<br class="gmail_msg">
                             break<br class="gmail_msg">
                 except break:<br class="gmail_msg">
                     # If we break out of the loop, the superclass gets<br class="gmail_msg">
a chance.<br class="gmail_msg">
                     if super().__subclasscheck__(cls):<br class="gmail_msg">
                         return True<br class="gmail_msg">
                     if self.__extra__ is None or isinstance(cls,<br class="gmail_msg">
GenericMeta):<br class="gmail_msg">
                         return False<br class="gmail_msg">
                     return issubclass(cls, self.__extra__)<br class="gmail_msg">
<br class="gmail_msg">
                 return True<br class="gmail_msg">
[...]<br class="gmail_msg">
<br class="gmail_msg">
<br class="gmail_msg">
My summary: I do see use-cases for the except break clause, but,<br class="gmail_msg">
admittedly, they are relatively rare and may be not worth the hassle of<br class="gmail_msg">
introducing new syntax.<br class="gmail_msg"></blockquote><div><br></div><div>IOW out of 114 cases, 4 may benefit from an 'except' block? If I'm reading those numbers correctly then ~3.5% of cases would benefit which isn't high enough to add the syntax and related complexity IMO.</div></div></div>