[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