<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>