[Python-ideas] for/except/else
Brett Cannon
brett at python.org
Thu Mar 2 13:05:21 EST 2017
On Thu, 2 Mar 2017 at 03:07 Wolfgang Maier <
wolfgang.maier at biologie.uni-freiburg.de> wrote:
[SNIP]
> As always though, reality can be expected to be quite a bit more
> complicated than theory so I decided to check the stdlib for real uses
> of break. This is quite a tedious task since break is used in many
> different ways and I couldn't come up with a good automated way of
> classifying them. So what I did is just go through stdlib code (in
> reverse alphabetical order) containing the break keyword and put it into
> categories manually. I only got up to socket.py before losing my
> enthusiasm, but here's what I found:
>
> - overall I looked at 114 code blocks that contain one or more breaks
>
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).
>
> - 84 of these are trivial use cases that simply break out of a while
> True block or terminate a while/for loop prematurely (no use for any
> follow-up clause there)
>
> - 8 more are causing a side-effect before a single break, and it would
> be pointless to put this into an except break clause
>
> - 3 more cause different, non-redundant side-effects before different
> breaks from the same loop and, obviously, an except break clause would
> not help them either
>
> => So the vast majority of breaks does *not* need an except break *nor*
> an else clause, but that's just as expected.
>
>
> Of the remaining 19 non-trivial cases
>
> - 9 are variations of your classical search idiom above, i.e., there's
> an else clause there and nothing more is needed
>
> - 6 are variations of your "nested side-effects" form presented above
> with debatable (see above) benefit from except break
>
> - 2 do not use an else clause currently, but have multiple breaks that
> do partly redundant things that could be combined in a single except
> break clause
>
> - 1 is an example of breaking out of two loops; from sre_parse._parse_sub:
>
> [...]
> # check if all items share a common prefix
> while True:
> prefix = None
> for item in items:
> if not item:
> break
> if prefix is None:
> prefix = item[0]
> elif item[0] != prefix:
> break
> else:
> # all subitems start with a common "prefix".
> # move it out of the branch
> for item in items:
> del item[0]
> subpatternappend(prefix)
> continue # check next one
> break
> [...]
>
> This could have been written as:
>
> [...]
> # check if all items share a common prefix
> while True:
> prefix = None
> for item in items:
> if not item:
> break
> if prefix is None:
> prefix = item[0]
> elif item[0] != prefix:
> break
> except break:
> break
>
> # all subitems start with a common "prefix".
> # move it out of the branch
> for item in items:
> del item[0]
> subpatternappend(prefix)
> [...]
>
>
> - finally, 1 is a complicated break dance to achieve sth that clearly
> would have been easier with except break; from typing.py:
>
> [...]
> def __subclasscheck__(self, cls):
> if cls is Any:
> return True
> if isinstance(cls, GenericMeta):
> # For a class C(Generic[T]) where T is co-variant,
> # C[X] is a subclass of C[Y] iff X is a subclass of Y.
> origin = self.__origin__
> if origin is not None and origin is cls.__origin__:
> assert len(self.__args__) == len(origin.__parameters__)
> assert len(cls.__args__) == len(origin.__parameters__)
> for p_self, p_cls, p_origin in zip(self.__args__,
> cls.__args__,
> origin.__parameters__):
> if isinstance(p_origin, TypeVar):
> if p_origin.__covariant__:
> # Covariant -- p_cls must be a subclass of
> p_self.
> if not issubclass(p_cls, p_self):
> break
> elif p_origin.__contravariant__:
> # Contravariant. I think it's the
> opposite. :-)
> if not issubclass(p_self, p_cls):
> break
> else:
> # Invariant -- p_cls and p_self must equal.
> if p_self != p_cls:
> break
> else:
> # If the origin's parameter is not a typevar,
> # insist on invariance.
> if p_self != p_cls:
> break
> else:
> return True
> # If we break out of the loop, the superclass gets a
> chance.
> if super().__subclasscheck__(cls):
> return True
> if self.__extra__ is None or isinstance(cls, GenericMeta):
> return False
> return issubclass(cls, self.__extra__)
> [...]
>
> which could be rewritten as:
>
> [...]
> def __subclasscheck__(self, cls):
> if cls is Any:
> return True
> if isinstance(cls, GenericMeta):
> # For a class C(Generic[T]) where T is co-variant,
> # C[X] is a subclass of C[Y] iff X is a subclass of Y.
> origin = self.__origin__
> if origin is not None and origin is cls.__origin__:
> assert len(self.__args__) == len(origin.__parameters__)
> assert len(cls.__args__) == len(origin.__parameters__)
> for p_self, p_cls, p_origin in zip(self.__args__,
> cls.__args__,
> origin.__parameters__):
> if isinstance(p_origin, TypeVar):
> if p_origin.__covariant__:
> # Covariant -- p_cls must be a subclass of
> p_self.
> if not issubclass(p_cls, p_self):
> break
> elif p_origin.__contravariant__:
> # Contravariant. I think it's the
> opposite. :-)
> if not issubclass(p_self, p_cls):
> break
> else:
> # Invariant -- p_cls and p_self must equal.
> if p_self != p_cls:
> break
> else:
> # If the origin's parameter is not a typevar,
> # insist on invariance.
> if p_self != p_cls:
> break
> except break:
> # If we break out of the loop, the superclass gets
> a chance.
> if super().__subclasscheck__(cls):
> return True
> if self.__extra__ is None or isinstance(cls,
> GenericMeta):
> return False
> return issubclass(cls, self.__extra__)
>
> return True
> [...]
>
>
> My summary: I do see use-cases for the except break clause, but,
> admittedly, they are relatively rare and may be not worth the hassle of
> introducing new syntax.
>
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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20170302/1ce16145/attachment-0001.html>
More information about the Python-ideas
mailing list