Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

For-else deserves its own section in the tutorial #123946

Merged
merged 3 commits into from
Sep 25, 2024

Conversation

nedbat
Copy link
Member

@nedbat nedbat commented Sep 11, 2024

the ``for`` loop, **not** the ``if`` statement.)

One way to think of the else clause is to imagine it paired with the ``if``
inside the loop. If you conceptually unroll the loop, you have an if/if/if/else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted in Discourse, I think the concept of “loop unrolling” may sound too foreign to novices reading a tutorial. The paragraph may serve the purpose just as well without this sentence.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

As the loop executes, it will run a sequence like if/if/if/else. The if is inside the loop, encountered a number of times. If the condition is ever true, a break will happen. If the condition is never true, the else clause outside the loop will execute.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes that sounds to be in much plainer English to me. 👍

Copy link

@AsgerJon AsgerJon Sep 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recently wrote about this elsewhere and even included an example:

class Integer:

  __fallback_value__ = 0
  __inner_value__ = None

  def __init__(self, *args) -> None:
    """This constructor method accepts any number of positional arguments.
     It then implements the under-used and underappreciated 'for-loop'
     ending with an 'else' clause. The code in the 'else' block runs 
     after the loop completes. The point is that the 'break' keyword also 
     applies to this block. So if 'break' is encountered, the code in the 
     'else' block does not run. To translate to natural language: 
     
     'Go through each positional argument and when you find an integer, 
     assign it to the inner value. Use the fallback value if you have not found any integer 
     after looking through each positional argument.'  
     
     It is the opinion of this author that the 'else' clause in a loop is
     both underused and underappreciated."""
    for arg in args:
      if isinstance(arg, int):
        self.__inner_value__ = arg
        break
    else:
      self.__inner_value__ = self.__fallback_value__

Hope this helps in some small way!

Doc/tutorial/controlflow.rst Outdated Show resolved Hide resolved
the condition is ever true, a ``break`` will happen. If the condition is never
true, the ``else`` clause outside the loop will execute.

When used with a loop, the ``else`` clause has more in common with the ``else``
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this paragraph necessary? Many readers of the tutorial will not be familiar with try/else, so I'm not sure this helps many people.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't expect people to read purely linearly, and this could help cement the semantics in the reader's mind.

@nedbat nedbat merged commit ffdc80e into python:main Sep 25, 2024
25 checks passed
@nedbat nedbat added needs backport to 3.12 bug and security fixes needs backport to 3.13 bugs and security fixes labels Sep 25, 2024
@miss-islington-app
Copy link

Thanks @nedbat for the PR 🌮🎉.. I'm working now to backport this PR to: 3.12.
🐍🍒⛏🤖

@miss-islington-app
Copy link

Thanks @nedbat for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13.
🐍🍒⛏🤖

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Sep 25, 2024
* For-else deserves its own section in the tutorial

* remove mention of unrolling the loop

* Update Doc/tutorial/controlflow.rst

Co-authored-by: Jelle Zijlstra <[email protected]>

---------

(cherry picked from commit ffdc80e)

Co-authored-by: Ned Batchelder <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
@bedevere-app
Copy link

bedevere-app bot commented Sep 25, 2024

GH-124563 is a backport of this pull request to the 3.12 branch.

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Sep 25, 2024
* For-else deserves its own section in the tutorial

* remove mention of unrolling the loop

* Update Doc/tutorial/controlflow.rst

Co-authored-by: Jelle Zijlstra <[email protected]>

---------

(cherry picked from commit ffdc80e)

Co-authored-by: Ned Batchelder <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
@bedevere-app bedevere-app bot removed the needs backport to 3.12 bug and security fixes label Sep 25, 2024
@bedevere-app
Copy link

bedevere-app bot commented Sep 25, 2024

GH-124564 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.13 bugs and security fixes label Sep 25, 2024
emilyemorehouse added a commit to lysnikolaou/cpython that referenced this pull request Sep 26, 2024
* main: (69 commits)
  Add "annotate" SET_FUNCTION_ATTRIBUTE bit to dis. (python#124566)
  pythongh-124412: Add helpers for converting annotations to source format (python#124551)
  pythongh-119180: Disallow instantiation of ConstEvaluator objects (python#124561)
  For-else deserves its own section in the tutorial (python#123946)
  Add 3.13 as a version option to the crash issue template (python#124560)
  pythongh-123242: Note that type.__annotations__ may not exist (python#124557)
  pythongh-119180: Make FORWARDREF format look at __annotations__ first (python#124479)
  pythonGH-58058: Add quick reference for `ArgumentParser` to argparse docs (pythongh-124227)
  pythongh-41431: Add `datetime.time.strptime()` and `datetime.date.strptime()` (python#120752)
  pythongh-102450: Add ISO-8601 alternative for midnight to `fromisoformat()` calls. (python#105856)
  pythongh-124370: Add "howto" for free-threaded Python (python#124371)
  pythongh-121277: Allow `.. versionadded:: next` in docs (pythonGH-121278)
  pythongh-119400:  make_ssl_certs: update reference test data automatically, pass in expiration dates as parameters python#119400  (pythonGH-119401)
  pythongh-119180: Avoid going through AST and eval() when possible in annotationlib (python#124337)
  pythongh-124448: Update Windows builds to use Tcl/Tk 8.6.15 (pythonGH-124449)
  pythongh-123884 Tee of tee was not producing n independent iterators (pythongh-124490)
  pythongh-124378: Update test_ttk for Tcl/Tk 8.6.15 (pythonGH-124542)
  pythongh-124513: Check args in framelocalsproxy_new() (python#124515)
  pythongh-101100: Add a table of class attributes to the "Custom classes" section of the data model docs (python#124480)
  Doc: Use ``major.minor`` for documentation distribution archive filenames (python#124489)
  ...
JelleZijlstra added a commit that referenced this pull request Sep 26, 2024
…124563)


(cherry picked from commit ffdc80e)

Co-authored-by: Ned Batchelder <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
Yhg1s pushed a commit that referenced this pull request Sep 26, 2024
…124564)

For-else deserves its own section in the tutorial (GH-123946)

* For-else deserves its own section in the tutorial

* remove mention of unrolling the loop

* Update Doc/tutorial/controlflow.rst



---------

(cherry picked from commit ffdc80e)

Co-authored-by: Ned Batchelder <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
@JelleZijlstra
Copy link
Member

@nedbat minor feedback: it's good practice to clean up the commit message before merging so that the git log is more understandable. GitHub's default includes the titles of all the commits, which are often not very informative. In this case, I would have shortened the commit message to just:

For-else deserves its own section in the tutorial

Co-authored-by: Jelle Zijlstra [email protected]

@nedbat nedbat deleted the nedbat/for-else-tutorial branch September 26, 2024 00:14
@terryjreedy
Copy link
Member

I complete agree that break/continue should be separated from else. But I am sorry I did not see this earlier.

Existing lines 203 to 205 after the patch are incomplete. 'else' is paired with any loop exit.

In a :keyword:!for or :keyword:!while loop, any of :keyword:!break, :keyword:!return, and :keyword:!raise: may be paired with an :keyword:!else clause. (There ``raise`` may come from code executed elsewhere.) If the loop finishes without executing and of the 'exit' statements, the :keyword:!else` clause executes.

Ned, do you want to make another PR, or should I?

@nedbat
Copy link
Member Author

nedbat commented Sep 26, 2024

#124591

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation in the Doc dir skip issue skip news
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants