diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index ed8f4a432bc..234b07e766c 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -6,4 +6,4 @@ updates:
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
- interval: 'daily'
+ interval: 'monthly'
diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml
index 01ddab0cac5..c2a5519234d 100644
--- a/.github/workflows/ci-workflow.yml
+++ b/.github/workflows/ci-workflow.yml
@@ -6,20 +6,19 @@ name: Exercises check
on:
push:
branches:
- - master
- main
pull_request:
jobs:
housekeeping:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
- python-version: 3.11.2
+ python-version: 3.13.5
- name: Download & Install dependencies
run: |
@@ -49,24 +48,20 @@ jobs:
./bin/template_status.py -v -p .problem-specifications
canonical_sync:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
needs: housekeeping
strategy:
matrix:
- python-version: [3.7, 3.8, 3.9, 3.10.6, 3.11.2]
+ python-version: [3.10.6, 3.11.2, 3.12, 3.13.5]
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- - uses: actions/setup-python@v4
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with:
python-version: ${{ matrix.python-version }}
- - name: Install dataclasses package
- if: ${{ matrix.python-version == '3.6' }}
- run: pip install dataclasses
-
- name: Install pytest
- run: pip install pytest~=7.2.2
+ run: pip install pytest~=8.4.0
- name: Check exercises
run: |
diff --git a/.github/workflows/issue-commenter.yml b/.github/workflows/issue-commenter.yml
index 7bddcd077c0..ca615dd43a0 100644
--- a/.github/workflows/issue-commenter.yml
+++ b/.github/workflows/issue-commenter.yml
@@ -5,20 +5,20 @@ on:
jobs:
comment-on-new-issue:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
name: Comments for every NEW issue.
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Read issue-comment.md
id: issue-comment
- uses: juliangruber/read-file-action@v1
+ uses: juliangruber/read-file-action@b549046febe0fe86f8cb4f93c24e284433f9ab58
with:
path: .github/issue-comment.md
- name: Base comment
- uses: jd-0001/gh-action-comment-on-new-issue@v2.0.3
+ uses: jd-0001/gh-action-comment-on-new-issue@c443e1151cc69b146fd6918cc983ec1bd27ab254
with:
message: "${{ steps.issue-comment.outputs.content }}"
ignore-label: ":anger: prickle"
diff --git a/.github/workflows/no-important-files-changed.yml b/.github/workflows/no-important-files-changed.yml
new file mode 100644
index 00000000000..812e9129668
--- /dev/null
+++ b/.github/workflows/no-important-files-changed.yml
@@ -0,0 +1,23 @@
+name: No important files changed
+
+on:
+ pull_request_target:
+ types: [opened]
+ branches: [main]
+ paths:
+ - "exercises/concept/**"
+ - "exercises/practice/**"
+ - "!exercises/*/*/.approaches/**"
+ - "!exercises/*/*/.articles/**"
+ - "!exercises/*/*/.docs/**"
+ - "!exercises/*/*/.meta/**"
+
+permissions:
+ pull-requests: write
+
+jobs:
+ check:
+ uses: exercism/github-actions/.github/workflows/check-no-important-files-changed.yml@main
+ with:
+ repository: ${{ github.event.pull_request.head.repo.owner.login }}/${{ github.event.pull_request.head.repo.name }}
+ ref: ${{ github.head_ref }}
diff --git a/.github/workflows/ping-cross-track-maintainers-team.yml b/.github/workflows/ping-cross-track-maintainers-team.yml
new file mode 100644
index 00000000000..b6ec9c5662f
--- /dev/null
+++ b/.github/workflows/ping-cross-track-maintainers-team.yml
@@ -0,0 +1,16 @@
+name: Ping cross-track maintainers team
+
+on:
+ pull_request_target:
+ types:
+ - opened
+
+permissions:
+ pull-requests: write
+
+jobs:
+ ping:
+ if: github.repository_owner == 'exercism' # Stops this job from running on forks
+ uses: exercism/github-actions/.github/workflows/ping-cross-track-maintainers-team.yml@main
+ secrets:
+ github_membership_token: ${{ secrets.COMMUNITY_CONTRIBUTIONS_WORKFLOW_TOKEN }}
diff --git a/.github/workflows/pr-commenter.yml b/.github/workflows/pr-commenter.yml
index 13c0b62d44c..f12714aec38 100644
--- a/.github/workflows/pr-commenter.yml
+++ b/.github/workflows/pr-commenter.yml
@@ -4,9 +4,9 @@ on:
jobs:
pr-comment:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- - uses: exercism/pr-commenter-action@v1.4.0
+ - uses: exercism/pr-commenter-action@085ef62d2a541a112c3ade1d24deea83665ea186
with:
github-token: "${{ github.token }}"
config-file: ".github/pr-commenter.yml"
\ No newline at end of file
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 41156435d82..29b31a0401e 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -6,9 +6,9 @@ on:
jobs:
stale:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/stale@v8
+ - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 21
diff --git a/.github/workflows/test-runner.yml b/.github/workflows/test-runner.yml
index ecfc2a10fc8..c9b0d21a713 100644
--- a/.github/workflows/test-runner.yml
+++ b/.github/workflows/test-runner.yml
@@ -8,8 +8,8 @@ on:
jobs:
test-runner:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Run test-runner
- run: docker-compose run test-runner
+ run: docker compose run test-runner
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index df8e36761c1..3f7813de10a 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -90,4 +90,4 @@ This policy was initially adopted from the Front-end London Slack community and
A version history can be seen on [GitHub](https://github.com/exercism/website-copy/edit/main/pages/code_of_conduct.md).
_This policy is a "living" document, and subject to refinement and expansion in the future.
-This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Slack, Twitter, email) and any other Exercism entity or event._
+This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Discord, Forum, Twitter, email) and any other Exercism entity or event._
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0bac23e0af3..6ff557f7087 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,33 +4,37 @@
Contributing
[](https://forum.exercism.org)
- [](https://exercism.org)
+ [](https://exercism.org)
[](https://exercism.org/blog/freeing-our-maintainers)
[](https://github.com/exercism/python/actions?query=workflow%3A%22Exercises+check%22)
-
-
-Hi. ππ½ π **We are happy you are here.** π π
-
-
-
-
+> [!IMPORTANT]
+> We are not accepting community contributions at this time.
+>
+>
+>
+>
+> We love our community. We're grateful you are interested in improving the Python track.
+> But our maintainers are **not accepting community contributions at this time.**
+> If you would like to discuss possible future changes, please open a [thread on the forum](https://forum.exercism.org/).
+>
+> This [community blog post](https://exercism.org/blog/freeing-our-maintainers) contains more details.
+>
+>
+>
-We π π our community.
-**`But our maintainers are not accepting community contributions at this time.`**
-Please read this [community blog post](https://exercism.org/blog/freeing-our-maintainers) for details.
-
-
+Hi. ππ½ π **We are happy you are here.** π π
+
**`exercism/Python`** is one of many programming language tracks on [exercism(dot)org][exercism-website].
This repo holds all the instructions, tests, code, & support files for Python _exercises_ currently under development or implemented & available for students.
-π Track exercises support Python `3.7` - `3.11.2`.
+π Track exercises support Python `3.10` - `3.13.5`.
Exceptions to this support are noted where they occur.
-π Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.11.2`.
+π Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.13.5`.
Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree π΄ .
Concept exercises are constrained to a small set of language or syntax features.
@@ -43,34 +47,38 @@ Practice exercises are open-ended, and can be used to practice concepts learned,
It is not uncommon to discover typos, confusing directions, or incorrect implementations of certain tests or code examples. Or you might have a great suggestion for a hint to aid students ( π ), see optimizations for exemplar or test code, find missing test cases to add, or want to correct factual and/or logical errors. Or maybe you have a great idea π‘ for an exercise or feature ( π ).
_Our track is always a work in progress!_ ππ
-While contributions are paused, we ask that you [`open a thread in our community forum`](https://forum.exercism.org) to let us know what you have found/suggest.
+While contributions are paused, we ask that you [**open a thread in our community forum**](https://forum.exercism.org) to let us know what you have found/suggest.
## π§ **Did you write a patch that fixes a bug?**
-**`Our maintainers are not accepting community contributions at this time.`**
-Please read this [community blog post](https://exercism.org/blog/freeing-our-maintainers) for details.
+Our maintainers are not accepting community contributions at this time.
+
+Until the pause on contributions ends, all PRs from the larger community will be **automatically closed** with a note.
+We ask that you [**open a thread in our community forum**](https://forum.exercism.org) to discuss any potential changes. Changes may or may not be approved, depending on the forum discussion.
-Once the pause ends, we will **happily** consider your PR.
-Until that time, all PRs from the larger community will be **automatically closed** with a note.
+Please read this [community blog post](https://exercism.org/blog/freeing-our-maintainers) for additional details.
+
-We're leaving the general contributing docs below for our long-term collaborators and maintainers.
+We're leaving the track contributing docs below for our long-term collaborators and maintainers.
+
+
+ Python Track Contributing Docs
In General
-- Maintainers are happy to review your work and help troubleshoot with you. π π
+- Maintainers are happy to review your work and help troubleshoot with you. π π If you need help, comment in the Pull Request/issue. ππ½ββοΈ
+ - **Please wait at least 72 hours before pinging or `@`ing reviewers directly.**
- Requests are reviewed as soon as is practical/possible.
- - (β ) Reviewers may be in a different timezone β , or tied up π§Ά with other tasks.
- - **Please wait at least 72 hours before pinging.**
-- If you need help, comment in the Pull Request/issue. ππ½ββοΈ
+ - (β ) Keep in mind that reviewers may be in a different timezone β , or tied up π§Ά with other tasks.
- If you would like in-progress feedback/discussion, please mark your Pull Request as a **`[draft]`**
- Pull Requests should be focused around a single exercise, issue, or change.
- Pull Request titles and descriptions should make clear **what** has changed and **why**.
- - Please link π to any related issues the PR addresses.
+ - Please link π to any related forum discussions or issues the PR addresses.
- π [ Open an issue ][open-an-issue]π and discuss it with π§° maintainers _**before**_:
- creating a Pull Request making significant or breaking changes.
- for changes across multiple exercises, even if they are typos or small.
@@ -170,7 +178,7 @@ Our documents use [Markdown][markdown-language], with certain [alterations][exer
- Favor `f-strings` for dynamic failure messages. Please make your error messages as relevant and human-readable as possible.
- We relate test cases to **task number** via a custom [PyTest Marker][pytestmark].
- These take the form of `@pytest.mark.task(taskno=)`. See [Guido's Gorgeous Lasagna][guidos-gorgeous-lasagna-testfile] for an example.
-- We prefer **test data files** when test inputs/ouputs are verbose.
+- We prefer **test data files** when test inputs/outputs are verbose.
- These should be named with `_data` or `_test_data` at the end of the filename, and saved alongside the test case file.
- See the [Cater-Waiter][cater-waiter] exercise directory for an example of this setup.
- **Test data files** need to be added under an `editor` key within [`config.json "files"`][exercise-config-json].
@@ -195,13 +203,13 @@ _We know it, and trust us, we are working on fixing it._ But if you see
-This track officially supports Python `3.7 - 3.11.2` for students completing exercises.
-The track `test runner`, `analyzer`, and `representer` run in docker on `python:3.11.2-slim`.
+This track officially supports Python `3.10 - 3.13.5` for students completing exercises.
+The track `test runner`, `analyzer`, and `representer` run in docker on `python:3.13.5-alpine3.22`.
Although the majority of test cases are written using `unittest.TestCase`,
-- All exercises should be written for compatibility with Python `3.7` - `3.11.2`.
-- Version backward _incompatibility_ (_e.g_ an exercise using features introduced in `3.8`, `3.9`, or `3.10`) should be clearly noted in any exercise hints, links, introductions or other notes.
+- All exercises should be written for compatibility with Python `3.10` - `3.13.5`.
+- Version backward _incompatibility_ (_e.g_ an exercise using features introduced in Python `3.10`+ that would not work in Python `3.10`) should be clearly noted in any exercise hints, links, introductions or other notes.
- Here is an example of how the Python documentation handles [version-tagged π· ][version-tagged-language-features] feature introduction.
@@ -222,7 +230,7 @@ Although the majority of test cases are written using `unittest.TestCase`,
- For specifications, refer to [Concept Exercise Anatomy][concept-exercise-anatomy], or [Practice Exercise Anatomy][practice-exercise-anatomy] depending on which type of exercise you are contributing to.
-- **Practice exercise**, descriptions and instructions come from a centralized, cross-track [problem specifications][problem-specifications] repository.
+- **Practice exercise** descriptions and instructions come from a centralized, cross-track [problem specifications][problem-specifications] repository.
- Any updates or changes need to be proposed/approved in `problem-specifications` first.
- If Python-specific changes become necessary, they need to be appended to the canonical instructions by creating a `instructions.append.md` file in this (`exercism/Python`) repository.
@@ -270,9 +278,9 @@ Although the majority of test cases are written using `unittest.TestCase`,
- [ ] `.meta/config.json` (**required**)
- [ ] `.meta/example.py` (**required**)
- [ ] `.meta/design.md` (_optional_)
- - [ ] `.meta/template.j2` (_template for generating tests from cannonical data_)
- - [ ] `.meta/tests.toml` (_tests configuration from cannonical data_)
- - [ ] `_test.py` (_**auto-generated from cannonical data**_)
+ - [ ] `.meta/template.j2` (_template for generating tests from canonical data_)
+ - [ ] `.meta/tests.toml` (_tests configuration from canonical data_)
+ - [ ] `_test.py` (_**auto-generated from canonical data**_)
- [ ] `.py` (**required**)
@@ -369,45 +377,32 @@ configlet generate --spec-path path/to/problem/specifications --
configlet generate --spec-path path/to/problem/specifications
```
+
+
[.flake8]: https://github.com/exercism/python/blob/main/.flake8
[.style.yapf]: https://github.com/exercism/python/blob/main/.style.yapf
[american-english]: https://github.com/exercism/docs/blob/main/building/markdown/style-guide.md
-[being-a-good-community-member]: https://github.com/exercism/docs/tree/main/community/good-member
[card-games-testfile]: https://github.com/exercism/python/blob/main/exercises/concept/card-games/lists_test.py
[cater-waiter]: https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter
[concept-exercise-anatomy]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md
-[concept-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md
[config-json]: https://github.com/exercism/javascript/blob/main/config.json
-[config-json]: https://github.com/exercism/python/blob/main/config.json
-[configlet-general]: https://github.com/exercism/configlet
[configlet-lint]: https://github.com/exercism/configlet#configlet-lint
[configlet]: https://github.com/exercism/docs/blob/main/building/configlet/generating-documents.md
[distinguishing-test-iterations]: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests
[enumerate]: https://docs.python.org/3/library/functions.html#enumerate
[eol]: https://en.wikipedia.org/wiki/Newline
[exercise-config-json]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md#full-example
-[exercise-presentation]: https://github.com/exercism/docs/blob/main/building/tracks/presentation.md
-[exercism-admins]: https://github.com/exercism/docs/blob/main/community/administrators.md
-[exercism-code-of-conduct]: https://exercism.org/docs/using/legal/code-of-conduct
-[exercism-concepts]: https://github.com/exercism/docs/blob/main/building/tracks/concepts.md
-[exercism-contributors]: https://github.com/exercism/docs/blob/main/community/contributors.md
[exercism-internal-linking]: https://github.com/exercism/docs/blob/main/building/markdown/internal-linking.md
[exercism-markdown-specification]: https://github.com/exercism/docs/blob/main/building/markdown/markdown.md
[exercism-markdown-widgets]: https://github.com/exercism/docs/blob/main/building/markdown/widgets.md
-[exercism-mentors]: https://github.com/exercism/docs/tree/main/mentoring
-[exercism-tasks]: https://exercism.org/docs/building/product/tasks
-[exercism-track-maintainers]: https://github.com/exercism/docs/blob/main/community/maintainers.md
-[exercism-track-structure]: https://github.com/exercism/docs/tree/main/building/tracks
[exercism-website]: https://exercism.org/
-[exercism-writing-style]: https://github.com/exercism/docs/blob/main/building/markdown/style-guide.md
[flake8-noqa]: https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html#in-line-ignoring-errors
[flake8]: http://flake8.pycqa.org/
[google-coding-style]: https://google.github.io/styleguide/pyguide.html
[guidos-gorgeous-lasagna-testfile]: https://github.com/exercism/python/blob/main/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
-[help-wanted]: https://github.com/exercism/python/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22
[implicit-line-joining]: https://google.github.io/styleguide/pyguide.html#32-line-length
[markdown-language]: https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf
[open-an-issue]: https://github.com/exercism/python/issues/new/choose
@@ -429,5 +424,4 @@ configlet generate --spec-path path/to/problem/specifications
[the-words-that-we-use]: https://github.com/exercism/docs/blob/main/community/good-member/words.md
[unittest]: https://docs.python.org/3/library/unittest.html#unittest.TestCase
[version-tagged-language-features]: https://docs.python.org/3/library/stdtypes.html#dict.popitem
-[website-contributing-section]: https://exercism.org/docs/building
[yapf]: https://github.com/google/yapf
diff --git a/README.md b/README.md
index 1c82f33b9d0..fd9be64f715 100644
--- a/README.md
+++ b/README.md
@@ -10,16 +10,33 @@
-Hi. ππ½ π **We are happy you are here.** π π
+> [!IMPORTANT]
+> We are not accepting community contributions at this time.
+>
+>
+>
+>
+> We love our community. We're grateful you are interested in improving the Python track.
+> But our maintainers are **not accepting community contributions at this time.**
+> If you would like to suggest a change / discuss an issue, please open a [thread on the forum](https://forum.exercism.org/).
+>
+> This [community blog post](https://exercism.org/blog/freeing-our-maintainers) contains more details.
+>
+>
+>
+Hi. ππ½ π **We are happy you are here.** π π
+
+
+
**`exercism/Python`** is one of many programming language tracks on [exercism(dot)org][exercism-website].
This repo holds all the instructions, tests, code, & support files for Python _exercises_ currently under development or implemented & available for students.
-π Track exercises support Python `3.7` - `3.11.2`.
+π Track exercises support Python `3.10` - `3.13.5`.
Exceptions to this support are noted where they occur.
-π Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.11.2`.
+π Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.13.5`.
Exercises are grouped into **concept** exercises which teach the [Python syllabus][python-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree π΄ .
Concept exercises are constrained to a small set of language or syntax features.
@@ -43,17 +60,17 @@ It might also be helpful to look at [Being a Good Community Member][being-a-good
-We π π our community.
-**`But our maintainers are not accepting community contributions at this time.`**
+We π π our community.
+**But our maintainers are not accepting community contributions at this time.**
Please read this [community blog post][freeing-maintainers] for details.
Here to suggest a new feature or new exercise?? **Hooray!** π
-We'd love if you did that via our [Exercism Community Forum](https://forum.exercism.org/).
+We'd love if you did that via our [Community Forum](https://forum.exercism.org/).
Please read [Suggesting Exercise Improvements][suggesting-improvements] & [Chesterton's Fence][chestertons-fence].
-_Thoughtful suggestions will likely result faster & more enthusiastic responses from volunteers._
+_Thoughtful suggestions will likely result in faster & more enthusiastic responses from volunteers._
@@ -67,7 +84,7 @@ _Thoughtful suggestions will likely result faster & more enthusiastic responses
## Python Software and Documentation
-**Copyright Β© 2001-2022 Python Software Foundation. All rights reserved.**
+**Copyright Β© 2001-2026 Python Software Foundation. All rights reserved.**
Python software and documentation are licensed under the [PSF License Agreement][psf-license].
@@ -99,7 +116,6 @@ This repository uses the [MIT License](/LICENSE).
[exercism-writing-style]: https://github.com/exercism/docs/blob/main/building/markdown/style-guide.md
[freeing-maintainers]: https://exercism.org/blog/freeing-our-maintainers
[practice-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/practice-exercises.md
-[prs]: https://github.com/exercism/docs/blob/main/community/good-member/pull-requests.md
[psf-license]: https://docs.python.org/3/license.html#psf-license
[python-syllabus]: https://exercism.org/tracks/python/concepts
[suggesting-improvements]: https://github.com/exercism/docs/blob/main/community/good-member/suggesting-exercise-improvements.md
diff --git a/bin/fetch-configlet b/bin/fetch-configlet
index 4800e150849..6bef43ab722 100755
--- a/bin/fetch-configlet
+++ b/bin/fetch-configlet
@@ -24,10 +24,11 @@ get_download_url() {
local latest='https://api.github.com/repos/exercism/configlet/releases/latest'
local arch
case "$(uname -m)" in
- x86_64) arch='x86-64' ;;
- *686*) arch='i386' ;;
- *386*) arch='i386' ;;
- *) arch='x86-64' ;;
+ aarch64|arm64) arch='arm64' ;;
+ x86_64) arch='x86-64' ;;
+ *686*) arch='i386' ;;
+ *386*) arch='i386' ;;
+ *) arch='x86-64' ;;
esac
local suffix="${os}_${arch}.${ext}"
curl "${curlopts[@]}" --header 'Accept: application/vnd.github.v3+json' "${latest}" |
@@ -47,7 +48,7 @@ main() {
fi
local os
- case "$(uname)" in
+ case "$(uname -s)" in
Darwin*) os='macos' ;;
Linux*) os='linux' ;;
Windows*) os='windows' ;;
@@ -58,8 +59,8 @@ main() {
local ext
case "${os}" in
- windows*) ext='zip' ;;
- *) ext='tar.gz' ;;
+ windows) ext='zip' ;;
+ *) ext='tar.gz' ;;
esac
echo "Fetching configlet..." >&2
@@ -69,16 +70,16 @@ main() {
curl "${curlopts[@]}" --output "${output_path}" "${download_url}"
case "${ext}" in
- *zip) unzip "${output_path}" -d "${output_dir}" ;;
- *) tar xzf "${output_path}" -C "${output_dir}" ;;
+ zip) unzip "${output_path}" -d "${output_dir}" ;;
+ *) tar xzf "${output_path}" -C "${output_dir}" ;;
esac
rm -f "${output_path}"
local executable_ext
case "${os}" in
- windows*) executable_ext='.exe' ;;
- *) executable_ext='' ;;
+ windows) executable_ext='.exe' ;;
+ *) executable_ext='' ;;
esac
local configlet_path="${output_dir}/configlet${executable_ext}"
diff --git a/bin/generate_tests.py b/bin/generate_tests.py
index 6c9b1c38b98..2ad23a9b5f1 100755
--- a/bin/generate_tests.py
+++ b/bin/generate_tests.py
@@ -138,7 +138,7 @@ def parse_datetime(string: str, strip_module: bool = False) -> datetime:
]| # OR
o(?:[0-8]{1,3}) # an octal value
| # OR
- x(?:[0-9A-Fa-f]{2}) # a hexidecimal value
+ x(?:[0-9A-Fa-f]{2}) # a hexadecimal value
| # OR
N # a unicode char name composed of
\{ # an opening brace
@@ -204,6 +204,8 @@ def regex_find(s: str, find: str) -> List[Any]:
def regex_split(s: str, find: str) -> List[str]:
return re.split(find, s)
+def join_test_inputs(test_inputs: list) -> str:
+ return "\n".join(test_inputs)
def filter_test_cases(cases: List[TypeJSON], opts: TestsTOML) -> List[TypeJSON]:
"""
@@ -409,6 +411,7 @@ def generate(
env.filters["regex_replace"] = regex_replace
env.filters["regex_find"] = regex_find
env.filters["regex_split"] = regex_split
+ env.filters["join_test_inputs"] = join_test_inputs
env.filters["zip"] = zip
env.filters["parse_datetime"] = parse_datetime
env.filters["escape_invalid_escapes"] = escape_invalid_escapes
diff --git a/concepts/basics/about.md b/concepts/basics/about.md
index 30ea083022f..71f30524c4d 100644
--- a/concepts/basics/about.md
+++ b/concepts/basics/about.md
@@ -6,7 +6,8 @@ Imperative, declarative (e.g., functional), and object-oriented programming _sty
Python puts a strong emphasis on code readability and (_similar to Haskell_) uses [significant indentation][significant indentation] to denote function, method, and class definitions.
-Python was created by Guido van Rossum and first released in 1991. The [Python Software Foundation][psf] manages and directs resources for Python and CPython development and receives proposals for changes to the language from [members][psf membership] of the community via [Python Enhancement Proposals or PEPs][peps].
+Python was created by Guido van Rossum and first released in 1991.
+The [Python Software Foundation][psf] manages and directs resources for Python and CPython development and receives proposals for changes to the language from [members][psf membership] of the community via [Python Enhancement Proposals or PEPs][peps].
Complete documentation for the current release can be found at [docs.python.org][python docs].
@@ -18,8 +19,14 @@ Complete documentation for the current release can be found at [docs.python.org]
- [Python FAQs][python faqs]
- [Python Glossary of Terms][python glossary of terms]
+
+
+This first concept introduces 4 major Python language features:
+1. Name Assignment (_variables and constants_),
+2. Functions (_the `def` keyword and the `return` keyword_),
+3. Comments, and
+4. Docstrings.
-This concept introduces 4 major Python language features: Name Assignment (_variables and constants_), Functions (_and the return keyword_), Comments, and Docstrings.
~~~~exercism/note
@@ -32,9 +39,9 @@ On the Python track, [variables][variables] are always written in [`snake_case`]
[snake case]: https://en.wikipedia.org/wiki/Snake_case
+[the zen of python]: https://www.python.org/dev/peps/pep-0020/
[variables]: https://realpython.com/python-variables/
[what is pythonic]: https://blog.startifact.com/posts/older/what-is-pythonic.html
-[the zen of python]: https://www.python.org/dev/peps/pep-0020/
~~~~
@@ -61,8 +68,9 @@ For example, `my_first_variable` can be re-assigned many times using `=`, and ca
>>> print(type(my_first_variable))
+>>> my_first_variable = 'You can call me "str".' # Strings can be declared using single or double quote marks.
>>> print(my_first_variable)
-"Now, I'm a string." # Strings can be declared using single or double quote marks.
+You can call me "str".
import collections
>>> my_first_variable = collections.Counter([1,1,2,3,3,3,4,5,6,7]) # Now my_first_variable has been re-bound to a Counter object.
@@ -70,7 +78,7 @@ import collections
>>> print(my_first_variable)
->>> Counter({3: 3, 1: 2, 2: 1, 4: 1, 5: 1, 6: 1, 7: 1})
+Counter({3: 3, 1: 2, 2: 1, 4: 1, 5: 1, 6: 1, 7: 1})
```
@@ -101,8 +109,8 @@ When functions are bound to a [class][classes] name, they're referred to as [met
Related functions and classes (_with their methods_) can be grouped together in the same file or module, and imported in part or in whole for use in other programs.
The `def` keyword begins a [function definition][function definition].
-Each function can have zero or more formal [parameters][parameters] in `()` parenthesis, followed by a `:` colon.
-Statements for the _body_ of the function begin on the line following `def` and must be _indented in a block_.
+Each function can have zero or more formal [parameters][parameters] in `()` parentheses, followed by a `:` colon.
+Statements for the _body_ of the function begin on the line following `def` and must be _indented in a block_:
```python
@@ -127,24 +135,69 @@ def add_two_numbers(number_one, number_two):
IndentationError: unindent does not match any outer indentation level
```
-Functions explicitly return a value or object via the [`return`][return] keyword.
-Functions that do not have an explicit `return` expression will _implicitly_ return [`None`][none].
+
+Functions _explicitly_ return a value or object via the [`return`][return] keyword:
+
```python
-# Function definition on first line.
+# Function definition on first line, explicit return used on final line.
def add_two_numbers(number_one, number_two):
- result = number_one + number_two
- return result # Returns the sum of the numbers.
+ return number_one + number_two
+
+# Calling the function in the Python shell returns the sum of the numbers.
>>> add_two_numbers(3, 4)
7
-# This function will return None.
+# Assigning the function call to a variable and printing
+# the variable will also return the value.
+>>> sum_with_return = add_two_numbers(5, 6)
+>>> print(sum_with_return)
+11
+```
+
+Functions that do not have an _explicit_ expression following a `return` will _implicitly_ return the [`None`][none] object.
+The details of `None` will be covered in a later exercise.
+For the purposes of this exercise and explanation, `None` is a placeholder that represents nothing, or null:
+
+
+```python
+
+# This function will return `None`
+def square_a_number(number):
+ square = number * number
+ return # <-- note that this return is not followed by an expression
+
+# Calling the function in the Python shell appears
+# to not return anything at all.
+>>> square_a_number(2)
+>>>
+
+
+# Using print() with the function call shows that
+# the function is actually returning the **None** object.
+>>> print(square_a_number(2))
+None
+```
+
+Functions that omit `return` will also _implicitly_ return the [`None`][none] object.
+This means that if you do not use `return` in a function, Python will return the `None` object for you.
+
+```python
+
+# This function omits a return keyword altogether
def add_two_numbers(number_one, number_two):
result = number_one + number_two
+>>> add_two_numbers(5, 7)
>>> print(add_two_numbers(5, 7))
None
+
+# Assigning the function call to a variable and printing
+# the variable will also show None.
+>>> sum_without_return = add_two_numbers(5, 6)
+>>> print(sum_without_return)
+None
```
@@ -154,32 +207,42 @@ Functions are [_called_][calls] or invoked using their name followed by `()`.
Dot (`.`) notation is used for calling functions defined inside a class or module.
```python
->>> def number_to_the_power_of(number_one, number_two):
- return number_one ** number_two
+>>> def raise_to_power(number, power):
+ return number ** power
...
->>> number_to_the_power_of(3,3) # Invoking the function with the arguments 3 and 3.
+>>> raise_to_power(3,3) # Invoking the function with the arguments 3 and 3.
27
# A mis-match between the number of parameters and the number of arguments will raise an error.
->>> number_to_the_power_of(4,)
+>>> raise_to_power(4,)
...
Traceback (most recent call last):
File "", line 1, in
-TypeError: number_to_the_power_of() missing 1 required positional argument: 'number_two'
+TypeError: raise_to_power() missing 1 required positional argument: 'power'
# Calling methods or functions in classes and modules.
>>> start_text = "my silly sentence for examples."
->>> str.upper(start_text) # Calling the upper() method for the built-in str class.
-"MY SILLY SENTENCE FOR EXAMPLES."
+>>> str.upper(start_text) # Calling the upper() method from the built-in str class on start_text.
+'MY SILLY SENTENCE FOR EXAMPLES.'
+
+# Because a string is an instance of the str class, methods can also be called on them "directly".
+>>> start_text = "my silly sentence for examples."
+>>> start_text.upper() # Calling the upper() method on start_text directly.
+'MY SILLY SENTENCE FOR EXAMPLES.'
+
+# Alternatively, we can skip the variable assignment (although this gets messy quick).
+>>> "my silly sentence for examples.".upper()
+'MY SILLY SENTENCE FOR EXAMPLES.'
+
# Importing the math module
import math
>>> math.pow(2,4) # Calling the pow() function from the math module
->>> 16.0
+16.0
```
@@ -235,32 +298,32 @@ Testing and `doctest` will be covered in a later concept.
```python
# An example on a user-defined function.
->>> def number_to_the_power_of(number_one, number_two):
+>>> def raise_to_power(number, power):
"""Raise a number to an arbitrary power.
- :param number_one: int the base number.
- :param number_two: int the power to raise the base number to.
- :return: int - number raised to power of second number
+ :param number: int the base number.
+ :param power: int the power to raise the base number to.
+ :return: int - number raised to the specified power.
- Takes number_one and raises it to the power of number_two, returning the result.
+ Takes a number and raises it to the specified power, returning the result.
"""
- return number_one ** number_two
+ return number ** power
...
# Calling the .__doc__ attribute of the function and printing the result.
->>> print(number_to_the_power_of.__doc__)
+>>> print(raise_to_power.__doc__)
Raise a number to an arbitrary power.
- :param number_one: int the base number.
- :param number_two: int the power to raise the base number to.
- :return: int - number raised to power of second number
+ :param number: int the base number.
+ :param power: int the power to raise the base number to.
+ :return: int - number raised to the specified power.
- Takes number_one and raises it to the power of number_two, returning the result.
+ Takes a number and raises it to the specified power, returning the result.
-# Printing the __doc__ attribute for the built-in type: str.
+# Printing the __doc__ attribute of the built-in type: str.
>>> print(str.__doc__)
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str
diff --git a/concepts/basics/introduction.md b/concepts/basics/introduction.md
index 34e2a8804d7..d44e6c9fd07 100644
--- a/concepts/basics/introduction.md
+++ b/concepts/basics/introduction.md
@@ -2,18 +2,26 @@
Python is a [dynamic and strongly typed][dynamic typing in python] programming language.
It employs both [duck typing][duck typing] and [gradual typing][gradual typing], via [type hints][type hints].
+Python puts a strong emphasis on code readability and (_similar to Haskell_) uses [significant indentation][significant indentation] to denote function, method, and class definitions.
+
+Python was created by Guido van Rossum and first released in 1991.
Imperative, declarative (e.g., functional), and object-oriented programming _styles_ are all supported, but internally **[everything in Python is an object][everythings an object]**.
-Python puts a strong emphasis on code readability and (_similar to Haskell_) uses [significant indentation][significant indentation] to denote function, method, and class definitions.
+We'll dig more into what all of that means as we continue through the Python track concepts.
-Python was created by Guido van Rossum and first released in 1991.
+This first concept (`basics`) introduces 4 major Python language features:
+1. Name Assignment (_variables and constants_),
+2. Functions (_the `def` keyword and the `return` keyword_),
+3. Comments, and
+4. Docstrings.
+
## Name Assignment (Variables & Constants)
Programmers can bind [_names_][facts-and-myths-about-python-names] (also called _variables_) to any type of object using the assignment `=` operator: ` = `.
-A name can be reassigned (or re-bound) to different values (different object types) over its lifetime.
+A name can be reassigned (or re-bound) to different values (different object types) over its lifetime:
```python
@@ -30,27 +38,29 @@ A name can be reassigned (or re-bound) to different values (different object typ
>>> print(type(my_first_variable))
+>>> my_first_variable = 'You can call me "str".' #<-- Strings can be declared using single or double quote marks.
>>> print(my_first_variable)
-"Now, I'm a string." # Strings can be declared using single or double quote marks.
+You can call me "str".
```
### Constants
-Constants are names meant to be assigned only once in a program.
-They should be defined at a [module][module] (file) level, and are typically visible to all functions and classes in the program.
-Using `SCREAMING_SNAKE_CASE` signals that the name should not be re-assigned, or its value mutated.
+Constants are names meant to be assigned only once in a program β although Python will not prevent re-assignment.
+Using `SCREAMING_SNAKE_CASE` signals to anyone reading the code that the name should **not** be re-assigned, or its value mutated.
+Constants should be defined at a [module][module] (file) level, and are typically visible to all functions and classes in a program.
+
## Functions
The `def` keyword begins a [function definition][function definition].
-Each function can have zero or more formal [parameters][parameters] in `()` parenthesis, followed by a `:` colon.
+Each function can have zero or more formal [parameters][parameters] in `()` parentheses, followed by a `:` colon.
Statements for the _body_ of the function begin on the line following `def` and must be _indented in a block_.
```python
-# The body of a function is indented by 2 spaces, & prints the sum of the numbers.
+# The body of this function is indented by 2 spaces,& prints the sum of the numbers.
def add_two_numbers(number_one, number_two):
total = number_one + number_two
print(total)
@@ -71,24 +81,70 @@ def add_two_numbers(number_one, number_two):
IndentationError: unindent does not match any outer indentation level
```
-Functions explicitly return a value or object via the [`return`][return] keyword.
-Functions that do not have an explicit `return` expression will _implicitly_ return [`None`][none].
+
+Functions _explicitly_ return a value or object via the [`return`][return] keyword:
+
```python
-# Function definition on first line.
+# Function definition on first line, explicit return used on final line.
def add_two_numbers(number_one, number_two):
- result = number_one + number_two
- return result # Returns the sum of the numbers.
+ return number_one + number_two
+
+# Calling the function in the Python shell returns the sum of the numbers.
>>> add_two_numbers(3, 4)
7
-# This function will return None.
+# Assigning the function call to a variable and printing
+# the variable will also return the value.
+>>> sum_with_return = add_two_numbers(5, 6)
+>>> print(sum_with_return)
+11
+```
+
+
+Functions that do not have an _explicit_ expression following a `return` will _implicitly_ return the [`None`][none] object.
+The details of `None` will be covered in a later exercise.
+For the purposes of this exercise and explanation, `None` is a placeholder that represents nothing, or null:
+
+
+```python
+
+# This function will return `None`
+def square_a_number(number):
+ square = number * number
+ return # <-- note that this return is not followed by an expression
+
+# Calling the function in the Python shell appears
+# to not return anything at all.
+>>> square_a_number(2)
+>>>
+
+
+# Using print() with the function call shows that
+# the function is actually returning the **None** object.
+>>> print(square_a_number(2))
+None
+```
+
+Functions that omit `return` will also _implicitly_ return the [`None`][none] object.
+This means that if you do not use `return` in a function, Python will return the `None` object for you.
+
+```python
+
+# This function omits a return keyword altogether
def add_two_numbers(number_one, number_two):
result = number_one + number_two
+>>> add_two_numbers(5, 7)
>>> print(add_two_numbers(5, 7))
None
+
+# Assigning the function call to a variable and printing
+# the variable will also show None.
+>>> sum_without_return = add_two_numbers(5, 6)
+>>> print(sum_without_return)
+None
```
diff --git a/concepts/basics/links.json b/concepts/basics/links.json
index 3e5561228ee..1d1d640c9e7 100644
--- a/concepts/basics/links.json
+++ b/concepts/basics/links.json
@@ -1,5 +1,6 @@
[
- {"url": "https://lerner.co.il/2019/06/18/understanding-python-assignment/",
+ {
+ "url": "https://lerner.co.il/2019/06/18/understanding-python-assignment/",
"description": "Reuven Lerner: Understanding Python Assignment"
},
{
@@ -14,6 +15,10 @@
"url": "https://www.pythonmorsels.com/everything-is-an-object/",
"description": "Python Morsels: Everything is an Object"
},
+ {
+ "url": "https://eli.thegreenplace.net/2012/03/23/python-internals-how-callables-work/",
+ "description": "Eli Bendersky: Python internals: how callables work"
+ },
{
"url": "https://stackoverflow.com/questions/11328920/is-python-strongly-typed",
"description": "dynamic typing and strong typing"
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
index 667f3eb6cd7..a7fca3714e3 100644
--- a/concepts/binary-octal-hexadecimal/about.md
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -190,8 +190,8 @@ As with binary and octal, Python will automatically convert hexadecimal literals
291
```
-As with binary and octal - hexidecimal literals **are ints**, and you can perform all integer operations.
-Prefixing a non-hexidecimal number with `0x` will raise a `SyntaxError`.
+As with binary and octal - hexadecimal literals **are ints**, and you can perform all integer operations.
+Prefixing a non-hexadecimal number with `0x` will raise a `SyntaxError`.
### Converting to and from Hexadecimal Representation
@@ -216,9 +216,6 @@ As with binary and octal, giving the wrong base will raise a `ValueError`.
[binary]: https://en.wikipedia.org/wiki/Binary_number
[bit_count]: https://docs.python.org/3/library/stdtypes.html#int.bit_count
[bit_length]: https://docs.python.org/3/library/stdtypes.html#int.bit_length
-[bit_count]: https://docs.python.org/3/library/stdtypes.html#int.bit_count
-[bit_length]: https://docs.python.org/3/library/stdtypes.html#int.bit_length
[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal
-[methods-int]: https://docs.python.org/3/library/stdtypes.html#additional-methods-on-integer-types
[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
[octal]: https://en.wikipedia.org/wiki/Octal
diff --git a/concepts/binary-octal-hexadecimal/introduction.md b/concepts/binary-octal-hexadecimal/introduction.md
index 820aac33ee7..a06ac922faf 100644
--- a/concepts/binary-octal-hexadecimal/introduction.md
+++ b/concepts/binary-octal-hexadecimal/introduction.md
@@ -5,7 +5,7 @@ Binary is base 2, octal is base 8, and hexadecimal is base 16.
Normal integers are base 10 in python.
Binary, octal, and hexadecimal literals are all considered `int` subtypes and Python automatically converts between them.
This means that they can only represent zero, positive, and negative numbers that do not have a fractional or decimal part.
-Binary, octal, and hexidecimal numbers support all integer operations.
+Binary, octal, and hexadecimal numbers support all integer operations.
However, division (_as with ints_) will return a `float`.
[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
diff --git a/concepts/bitwise-operators/.meta/config.json b/concepts/bitwise-operators/.meta/config.json
index 9b9e8da5a9b..7767ff5d740 100644
--- a/concepts/bitwise-operators/.meta/config.json
+++ b/concepts/bitwise-operators/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "TODO: add blurb for this concept",
- "authors": ["bethanyg", "cmccandless"],
+ "blurb": "Python supports bitwise operations such as left/right shift, and, or, xor, and not.",
+ "authors": ["BethanyG", "colinleach"],
"contributors": []
}
diff --git a/concepts/bitwise-operators/about.md b/concepts/bitwise-operators/about.md
index c628150d565..a68e5378f12 100644
--- a/concepts/bitwise-operators/about.md
+++ b/concepts/bitwise-operators/about.md
@@ -1,2 +1,197 @@
-#TODO: Add about for this concept.
+# About
+Down at the hardware level, transistors can only be on or off: two states that we traditionally represent with `1` and `0`.
+These are the [`binary digits`][binary-digits], abbreviated as [`bits`][bits].
+Awareness of `bits` and `binary` is particularly important for systems programmers working in low-level languages.
+
+However, for most of the history of computing the programming priority has been to find increasingly sophisticated ways to _abstract away_ this binary reality.
+In Python (and many other [high-level programming languages][high-level-language]), we work with `int`, `float`, `string` and other defined _types_, up to and including audio and video formats.
+We let the Python internals take care of (eventually) translating everything to bits.
+
+Nevertheless, using [bitwise-operators][python-bitwise-operators] and [bitwise operations][python-bitwise-operations] can sometimes have significant advantages in speed and memory efficiency, even in a high-level language like Python.
+
+
+## Entering and Displaying Binary Numbers
+
+Unsurprisingly, Python interacts with the user using decimal numbers, but a programmer can override this default.
+In fact, Python will readily accept an `int` in `binary`, `hexadecimal`, or `octal` format, and will happily perform mathematical operations between them.
+For more details, you can review the [concept:python/binary-octal-hexadecimal]() concept.
+
+Binary numbers are entered with a `0b` prefix, just as `0x` can be used for hexadecimal (_hex numbers are a concise way to represent groups of 4 bits_), and `oct` can be used for octal numbers.
+
+There are multiple ways to convert integers to binary strings, varying in whether they include the `0b` prefix and whether they support left-padding with zeros:
+
+
+```python
+# Binary entry.
+>>> 0b10111
+23
+
+# Converting an int display to binary string, with prefix.
+>>> bin(23)
+'0b10111'
+
+>>> number = 23
+
+# Binary without prefix, padded to 8 digits.
+>>> format(number, '08b')
+'00010111'
+
+# Same format, but using an f-string.
+>>> f"{number} in decimal is {number:08b} in binary and {number:x} in hex"
+'23 in decimal is 00010111 in binary and 17 in hex'
+```
+
+
+## [`Bitwise Logic`][python-bitwise-operations]
+
+In the [concept:python/bools]() concept, we discussed the _logical operators_ `and`, `or` and `not` used with Boolean (_`True` and `False`_) values.
+The same logic rules apply when working with bits.
+
+However, the bitwise equivalents of the logical operators `&` (_and_), `|` (_or_), `~` (_not_), and `^` (_[XOR][xor]_), are applied to each _bit_ in a binary representation, treating `1` as `True` ("on") and `0` as `False` ("off").
+An example with the bitwise `&` might make this clearer:
+
+
+```python
+>>> x = 0b01100110
+>>> y = 0b00101010
+
+>>> format(x & y, '08b')
+'00100010'
+```
+
+Only positions with a `1` in _**both**_ the input numbers are set to `1` in the output.
+
+Bitwise `&` is commonly used as a way to isolate single bits in a compacted set of `True`/`False` values, such as user-configurable settings in an app.
+This enables the value of individual bits to control program logic:
+
+
+```python
+>>> number = 0b0110
+>>> number & 0b0001 > 0
+False
+
+>>> number & 0b0010 > 0
+True
+```
+
+
+For a bitwise `|` (or), a `1` is set in the output if there is a `1` in _**either**_ of the inputs:
+
+
+```python
+>>> x = 0b01100110
+>>> y = 0b00101010
+
+>>> format(x | y, '08b')
+'01101110'
+```
+
+
+With the `^` operator for bitwise e**x**clusive **or** (xor), a `1` is set if it appears in _**either**_ of the inputs _**but not both**_ inputs.
+This symbol might seem familiar from the [concept:python/sets]() concept, where it is used for `set` _symmetric difference_, which is the same as [xor applied to sets][symmetric-difference].
+If xor `^` seems strange, be aware that this is by far the [most common operation in cryptography][xor-cipher].
+
+
+```python
+>>> x = 0b01100110
+>>> y = 0b00101010
+
+>>> format(x ^ y, '08b')
+'01001100'
+```
+
+
+Finally, there is the `~` operator (_the [tilde][tilde] character_), which is a bitwise `not` that takes a single input and _**inverts all the bits**_, which might not be the result you were expecting!
+Each `1` in the representation changes to `0`, and vice versa.
+See the section below for details.
+
+
+## Negative Numbers and Binary Representation
+
+In decimal representation, we distinguish positive and negative numbers by using a `+` or `-` sign to the left of the digits.
+Using these symbols at a binary level proved inefficient for digital computing and raised the problem that `+0` is not the same as `-0`.
+
+Rather than using `-` and `+`, all modern computers use a [`twos-complement`][twos-complement] representation for negative numbers, right down to the silicon chip level.
+This means that all bits are inverted and a number is _**interpreted as negative**_ if the left-most bit (also termed the "most significant bit", or MSB) is a `1`.
+Positive numbers have an MSB of `0`.
+This representation has the advantage of only having one version of zero, so that the programmer doesn't have to manage `-0` and `+0`.
+
+This way of representing negative and positive numbers adds a complication for Python: there are no finite-integer concepts like `int32` or `int64` internally in the core language.
+In 'modern' Python, `int`s are of unlimited size (_limited only by hardware capacity_), and a negative or bit-inverted number has a (_theoretically_) infinite number of `1`'s to the left, just as a positive number has unlimited `0`'s.
+
+This makes it difficult to give a useful example of `bitwise not`:
+
+```python
+>>> x = 0b01100110
+>>> format(x, '08b')
+'01100110'
+
+# This is a negative binary (not twos-complement display).
+>>> format(~x, '08b')
+'-1100111'
+
+ # Decimal representation.
+>>> x
+102
+
+# Using the Bitwise not, with an unintuitive result.
+>>> ~x
+-103
+```
+
+This is **not** the `0b10011001` we would see in languages with fixed-size integers.
+
+The `~` operator only works as expected with _**unsigned**_ byte or integer types, or with fixed-sized integer types.
+These numeric types are supported in third-party packages such as [`NumPy`][numpy], [`pandas`][pandas], and [`sympy`][sympy] but not in core Python.
+
+In practice, Python programmers quite often use the shift operators described below and `& | ^` with positive numbers only.
+Bitwise operations with negative numbers are much less common.
+One technique is to add [`2**32 (or 1 << 32)`][unsigned-int-python] to a negative value to make an `int` unsigned, but this gets difficult to manage.
+Another strategy is to work with the [`ctypes`][ctypes-module] module, and use c-style integer types, but this is equally unwieldy.
+
+
+## [`Shift operators`][bitwise-shift-operators]
+
+The left-shift operator `x << y` simply moves all the bits in `x` by `y` places to the left, filling the new gaps with zeros.
+Note that this is arithmetically identical to multiplying a number by `2**y`.
+
+The right-shift operator `x >> y` does the opposite.
+This is arithmetically identical to integer division `x // 2**y`.
+
+Keep in mind the previous section on negative numbers and their pitfalls when shifting.
+
+
+```python
+>>> x = 8
+>>> format(x, '08b')
+'00001000'
+
+# A left bit shift.
+>>> x << 2
+32
+
+>>> format(x << 2, '08b')
+'00100000'
+
+# A right bit shift.
+>>> format(x >> 2, '08b')
+'00000010'
+```
+
+[binary-digits]: https://www.khanacademy.org/computing/computers-and-internet/xcae6f4a7ff015e7d:digital-information/xcae6f4a7ff015e7d:binary-numbers/v/the-binary-number-system
+[bits]: https://en.wikipedia.org/wiki/Bit
+[bitwise-shift-operators]: https://docs.python.org/3/reference/expressions.html#shifting-operations
+[ctypes-module]: https://docs.python.org/3/library/ctypes.html#module-ctypes
+[high-level-language]: https://en.wikipedia.org/wiki/High-level_programming_language
+[numpy]: https://numpy.org/doc/stable/user/basics.types.html
+[pandas]: https://pandas.pydata.org/docs/reference/arrays.html#nullable-integer
+[python-bitwise-operations]: https://docs.python.org/3/reference/expressions.html#binary-bitwise-operations
+[python-bitwise-operators]: https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
+[symmetric-difference]: https://math.stackexchange.com/questions/84184/relation-between-xor-and-symmetric-difference#:~:text=It%20is%20the%20same%20thing,they%20are%20indeed%20the%20same.
+[sympy]: https://docs.sympy.org/latest/modules/codegen.html#predefined-types
+[tilde]: https://en.wikipedia.org/wiki/Tilde
+[twos-complement]: https://en.wikipedia.org/wiki/Two%27s_complement#:~:text=Two's%20complement%20is%20the%20most,number%20is%20positive%20or%20negative.
+[unsigned-int-python]: https://stackoverflow.com/a/20768199
+[xor-cipher]: https://en.wikipedia.org/wiki/XOR_cipher
+[xor]: https://stackoverflow.com/a/2451393
diff --git a/concepts/bitwise-operators/introduction.md b/concepts/bitwise-operators/introduction.md
index bbe12ffd5e9..88aba3a6a7b 100644
--- a/concepts/bitwise-operators/introduction.md
+++ b/concepts/bitwise-operators/introduction.md
@@ -1 +1,20 @@
-#TODO: Add introduction for this concept.
+# Introduction
+
+Down at the hardware level, transistors can only be on or off: two states that we traditionally represent with `1` and `0`.
+These are the [`binary digits`][binary-digits], abbreviated as [`bits`][bits].
+Awareness of `bits` and `binary` is particularly important for systems programmers working in low-level languages.
+
+However, for most of the history of computing the programming priority has been to find increasingly sophisticated ways to _abstract away_ this binary reality.
+
+
+In Python (and many other [high-level programming languages][high-level-language]), we work with `int`, `float`, `string` and other defined _types_, up to and including audio and video formats.
+We let the Python internals take care of (eventually) translating everything to bits.
+
+
+Nevertheless, using [bitwise-operators][python-bitwise-operators] and [bitwise operations][python-bitwise-operations] can sometimes have significant advantages in speed and memory efficiency, even in a high-level language like Python.
+
+[high-level-language]: https://en.wikipedia.org/wiki/High-level_programming_language
+[binary-digits]: https://www.khanacademy.org/computing/computers-and-internet/xcae6f4a7ff015e7d:digital-information/xcae6f4a7ff015e7d:binary-numbers/v/the-binary-number-system
+[bits]: https://en.wikipedia.org/wiki/Bit
+[python-bitwise-operations]: https://docs.python.org/3/reference/expressions.html#binary-bitwise-operations
+[python-bitwise-operators]: https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
diff --git a/concepts/bitwise-operators/links.json b/concepts/bitwise-operators/links.json
index eb5fb7c38a5..7c103c84630 100644
--- a/concepts/bitwise-operators/links.json
+++ b/concepts/bitwise-operators/links.json
@@ -1,18 +1,22 @@
[
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://wiki.python.org/moin/BitwiseOperators/",
+ "description": "BitwiseOperators on the Python wiki."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://realpython.com/python-bitwise-operators",
+ "description": "Real Python: Bitwise Operators in Python."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://stackoverflow.com/a/20768199",
+ "description": "Stack Overflow: Convert a Python int to an unsigned int."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://www.khanacademy.org/computing/computer-science/cryptography/ciphers/a/xor-bitwise-operation",
+ "description": "Khan Academy: The Ultimate Shift Cipher."
+ },
+ {
+ "url": "https://en.wikipedia.org/wiki/XOR_cipher",
+ "description": "The XOR Cipher"
}
]
diff --git a/concepts/bools/about.md b/concepts/bools/about.md
index 4b697270659..0b8354750a3 100644
--- a/concepts/bools/about.md
+++ b/concepts/bools/about.md
@@ -1,6 +1,6 @@
# About
-Python represents True and False values with the [bool][bool] type.
+Python represents true and false values with the [`bool`][bools] type, which is a subclass of `int`.
There are only two Boolean values in this type: `True` and `False`.
These values can be assigned to a variable and combined with the [Boolean operators][boolean-operators] (`and`, `or`, `not`):
@@ -134,8 +134,8 @@ It is considered a [Python anti-pattern][comparing to true in the wrong way] to
```
-[bool-function]: https://docs.python.org/3/library/functions.html#bool
-[bool]: https://docs.python.org/3/library/stdtypes.html#truth
[Boolean-operators]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
+[bool-function]: https://docs.python.org/3/library/functions.html#bool
+[bools]: https://docs.python.org/3/library/stdtypes.html#typebool
[comparing to true in the wrong way]: https://docs.quantifiedcode.com/python-anti-patterns/readability/comparison_to_true.html
[comparisons]: https://docs.python.org/3/library/stdtypes.html#comparisons
diff --git a/concepts/bools/introduction.md b/concepts/bools/introduction.md
index 535f90be07c..85eb032df25 100644
--- a/concepts/bools/introduction.md
+++ b/concepts/bools/introduction.md
@@ -1,6 +1,6 @@
# Introduction
-Python represents true and false values with the `bool` type.
+Python represents true and false values with the [`bool`][bools] type, which is a subclass of `int`.
There are only two values under that type: `True` and `False`.
These values can be bound to a variable:
@@ -21,3 +21,5 @@ We can evaluate Boolean expressions using the `and`, `or`, and `not` operators.
>>> true_variable = not False
>>> false_variable = not True
```
+
+[bools]: https://docs.python.org/3/library/stdtypes.html#typebool
diff --git a/concepts/class-inheritance/about.md b/concepts/class-inheritance/about.md
index 5db7909e2c7..9f1bdf30cd9 100644
--- a/concepts/class-inheritance/about.md
+++ b/concepts/class-inheritance/about.md
@@ -7,7 +7,7 @@ In situations where only a small amount of functionality needs to be customized
`Inheritance` describes `is a kind of` relationship between two or more classes, abstracting common details into super (_base_ or _parent_) class and storing specific ones in the subclass (_derived class_ or _child class_).
-To create a child class, specify the parent class name inside the pair of parenthesis, followed by it's name.
+To create a child class, specify the parent class name inside the pair of parenthesis, followed by its name.
Example
```python
class Child(Parent):
diff --git a/concepts/classes/about.md b/concepts/classes/about.md
index f50af7321d3..11b03643543 100644
--- a/concepts/classes/about.md
+++ b/concepts/classes/about.md
@@ -118,7 +118,7 @@ class MyClass:
def __init__(self, location):
# This is an instance or object property, attribute, or variable.
- # Note that we are unpacking the tuple argument into two seperate instance variables.
+ # Note that we are unpacking the tuple argument into two separate instance variables.
self.location_x = location[0]
self.location_y = location[1]
@@ -314,12 +314,12 @@ class MyClass:
# This will compile and run without error, but has no current functionality.
def pending_functionality(self):
- # Stubbing or placholding the body of this method.
+ # Stubbing or place-holding the body of this method.
pass
```
[class method]: https://stackoverflow.com/questions/17134653/difference-between-class-and-instance-methods
-[dunder]: https://www.dataindependent.com/python/python-glossary/python-dunder/
+[dunder]: https://mathspp.com/blog/pydonts/dunder-methods
[oop]: https://www.educative.io/blog/object-oriented-programming
[dot notation]: https://stackoverflow.com/questions/45179186/understanding-the-dot-notation-in-python
[shadowing]: https://oznetnerd.com/2017/07/17/python-shadowing/
diff --git a/concepts/classes/links.json b/concepts/classes/links.json
index 5687b92a3d1..8cc9ba5926e 100644
--- a/concepts/classes/links.json
+++ b/concepts/classes/links.json
@@ -17,7 +17,7 @@
},
{
"url": "https://dbader.org/blog/6-things-youre-missing-out-on-by-never-using-classes-in-your-python-code",
- "description": "6 Things Youre Missing out on by never using classes in your Python code."
+ "description": "6 Things You are Missing out on by never using classes in your Python code."
},
{
"url": "http://python-history.blogspot.com/2010/06/inside-story-on-new-style-classes.html",
diff --git a/concepts/comparisons/about.md b/concepts/comparisons/about.md
index c2f5faaad91..1d2c677d22a 100644
--- a/concepts/comparisons/about.md
+++ b/concepts/comparisons/about.md
@@ -29,7 +29,7 @@ Numeric types are (mostly) an exception to this type matching rule.
An `integer` **can** be considered equal to a `float` (_or an [`octal`][octal] equal to a [`hexadecimal`][hex]_), as long as the types can be implicitly converted for comparison.
For the other numeric types ([complex][complex numbers], [decimal][decimal numbers], [fractions][rational numbers]), comparison operators are defined where they "make sense" (_where implicit conversion does not change the outcome_), but throw a `TypeError` if the underlying objects cannot be accurately converted for comparison.
-For more information on the rules that python uses for numeric conversion, see [arithmetic conversions][arithmetic conversions] in the Python documentation.
+For more information on the rules that Python uses for numeric conversion, see [arithmetic conversions][arithmetic conversions] in the Python documentation.
```python
>>> import fractions
@@ -47,7 +47,8 @@ True
>>> 6/3 == 0b10
True
-# An int can be converted to a complex number with a 0 imaginary part.
+# An int can be converted to a complex
+# number with a 0 imaginary part.
>>> 17 == complex(17)
True
@@ -60,8 +61,8 @@ True
```
Any ordered comparison of a number to a `NaN` (_not a number_) type is `False`.
-A confusing side-effect of Python's `NaN` definition is that `NaN` never compares equal to `NaN`.
-If you are curious as to why `Nan` was defined this way in Python, this [Stack Overflow Post on NaN][so nan post] around the setting of the international standard is an interesting read.
+A confusing side effect of Python's `NaN` definition is that `NaN` never compares equal to `NaN`.
+If you are curious as to why `NaN` was defined this way in Python, this [Stack Overflow Post on NaN][so nan post] around the setting of the international standard is an interesting read.
```python
>>> x = float('NaN')
@@ -188,7 +189,7 @@ See the Python reference docs on [value comparisons][value comparisons none] and
>>> my_fav_numbers is your_fav_numbers
True
-# The returned id will differ by system and python version.
+# The returned id will differ by system and Python version.
>>> id(my_fav_numbers)
4517478208
diff --git a/concepts/comparisons/links.json b/concepts/comparisons/links.json
index ed61054b722..f16869e2b92 100644
--- a/concepts/comparisons/links.json
+++ b/concepts/comparisons/links.json
@@ -1,27 +1,19 @@
[
- {
- "url": "https://docs.python.org/3/reference/expressions.html#comparisons",
- "description": "Comparisons in Python (Python language reference)"
- },
{
"url": "https://www.tutorialspoint.com/python/python_basic_operators.htm",
"description": "Python basic operators on Tutorials Point"
},
{
- "url": "https://data-flair.training/blogs/python-comparison-operators/",
- "description": "Python comparison operators on Data Flair"
- },
- {
- "url": "https://www.python.org/dev/peps/pep-0207/",
- "description": "PEP 207 to allow Operator Overloading for Comparison"
+ "url": "https://docs.python.org/3/reference/expressions.html#comparisons",
+ "description": "Comparisons in Python (Python language reference)"
},
{
"url": "https://docs.python.org/3/reference/expressions.html#is-not",
"description": "Identity comparisons in Python (Python language reference)"
},
{
- "url": "https://docs.python.org/3/library/operator.html",
- "description": "Operators (Python Docs)"
+ "url": "https://docs.python.org/3/reference/expressions.html#value-comparisons",
+ "description": "Value comparisons in Python (Python language reference)"
},
{
"url": "https://docs.python.org/3/library/stdtypes.html#typesnumeric",
@@ -44,11 +36,11 @@
"description": "Python Object Model (Python docs)"
},
{
- "url": "https://docs.python.org/3/reference/datamodel.html#customization",
- "description": "Basic Customization (Python language reference)"
+ "url": "https://www.python.org/dev/peps/pep-0207/",
+ "description": "PEP 207 to allow Operator Overloading for Comparison"
},
{
- "url": "https://docs.python.org/3/reference/expressions.html#value-comparisons",
- "description": "Value comparisons in Python (Python language reference)"
+ "url": "https://docs.python.org/3/reference/datamodel.html#customization",
+ "description": "Basic Customization (Python language reference)"
}
]
diff --git a/concepts/complex-numbers/.meta/config.json b/concepts/complex-numbers/.meta/config.json
index 9b9e8da5a9b..ca6ccc8811d 100644
--- a/concepts/complex-numbers/.meta/config.json
+++ b/concepts/complex-numbers/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "TODO: add blurb for this concept",
- "authors": ["bethanyg", "cmccandless"],
+ "blurb": "Complex numbers are a fundamental data type in Python, along with int and float. Further support is added with the cmath module, which is part of the Python standard library.",
+ "authors": ["BethanyG", "colinleach"],
"contributors": []
}
diff --git a/concepts/complex-numbers/about.md b/concepts/complex-numbers/about.md
index c628150d565..dfe067be4ee 100644
--- a/concepts/complex-numbers/about.md
+++ b/concepts/complex-numbers/about.md
@@ -1,2 +1,260 @@
-#TODO: Add about for this concept.
+# About
+
+`Complex numbers` are not complicated.
+They just need a less alarming name.
+
+They are so useful, especially in engineering and science, that Python includes [`complex`][complex] as a standard numeric type alongside integers ([`int`s][ints]) and floating-point numbers ([`float`s][floats]).
+
+
+## Basics
+
+A `complex` value in Python is essentially a pair of floating-point numbers.
+These are called the "real" and "imaginary" parts, for unfortunate historical reasons.
+Again, it is best to focus on the underlying simplicity and not the strange names.
+
+There are two common ways to create complex numbers.
+
+1) The [`complex(real, imag)`][complex] constructor takes two `float` parameters:
+
+```python
+>>> z1 = complex(1.5, 2.0)
+>>> z1
+(1.5+2j)
+```
+
+The constructor can also parse string input.
+This has the odd limitation that it fails if the string contains spaces.
+
+```python
+>>> complex('4+2j')
+(4+2j)
+
+>>> complex('4 + 2j')
+Traceback (most recent call last):
+ File "", line 1, in
+ValueError: complex() arg is a malformed string
+```
+
+
+2) The complex number can be specified as ` + j` literal, or just `j` if the real part is zero:
+
+
+```python
+>>> z2 = 2.0 + 1.5j
+>>> z2
+(2+1.5j)
+```
+The end result is identical to using the `complex()` constructor.
+
+
+There are two rules for that imaginary part of the complex number:
+
+
+- It is designated with `j` (not `i` as you may see in math textbooks).
+
+- The `j` must immediately follow a number, to prevent Python seeing it as a variable name. If necessary, use `1j`.
+
+```python
+>>> j
+Traceback (most recent call last):
+ File "", line 1, in
+NameError: name 'j' is not defined
+
+>>> 1j
+1j
+
+>>> type(1j)
+
+```
+
+Most engineers are happy with `j`.
+Most scientists and mathematicians prefer the mathematical notation `i` for _imaginary_, but that notation conflicts with the use of `i` to mean _current_ in Electrical Engineering.
+So in designing Python, the Electrical Engineers won.
+
+
+To access the parts of a complex number individually:
+
+```python
+>>> z2.real
+2.0
+>>> z2.imag
+1.5
+```
+
+Either part can be zero and mathematicians may then talk of the number being "wholly real" or "wholly imaginary".
+However, it is still a complex number in Python:
+
+
+```python
+>>> complex(0, 1)
+1j
+>>> type(complex(0, 1))
+
+
+>>> complex(1, 0)
+(1+0j)
+```
+
+You may have heard that "`i` (or `j`) is the square root of -1".
+
+For now, all this means is that the imaginary part _by definition_ satisfies the equality
+```python
+1j * 1j == -1 # => True
+```
+
+This is a simple idea, but it leads to interesting consequences.
+
+## Arithmetic
+
+Most of the [`operators`][operators] used with floats and ints also work with complex numbers:
+
+
+```python
+>>> z1 = (1.5+2j)
+>>> z2 = (2+1.5j)
+
+
+>>> z1 + z2 # addition
+(3.5+3.5j)
+
+>>> z1 - z2 # subtraction
+(-0.5+0.5j)
+
+>>> z1 * z2 # multiplication
+6.25j
+
+>>> z1 / z2 # division
+(0.96+0.28j)
+
+>>> z1 ** 2 # exponentiation
+(-1.75+6j)
+
+>>> 2 ** z1 # another exponentiation
+(0.5188946835878313+2.7804223253571183j)
+
+>>> 1j ** 2 # j * j == -1
+(-1+0j)
+```
+
+Explaining the rules for complex number multiplication and division is out of scope for this concept (_and you are unlikely to have to perform those operations "by hand" very often_).
+
+Any [mathematical][math-complex] or [electrical engineering][engineering-complex] introduction to complex numbers will cover this, should you want to dig into the topic.
+
+Alternatively, Exercism has a `Complex Numbers` practice exercise where you can implement a complex number class with these operations from first principles.
+
+
+Integer division is ___not___ possible on complex numbers, so the `//` and `%` operators and `divmod()` functions will fail for the complex number type.
+
+
+There are two functions implemented for numeric types that are very useful when working with complex numbers:
+
+- `.conjugate()` simply flips the sign of the imaginary part of a complex number (_from + to - or vice-versa_).
+ - Because of the way complex multiplication works, this is more useful than you might think.
+- `abs()` is guaranteed to return a real number with no imaginary part.
+
+
+```python
+>>> z1
+(1.5+2j)
+
+>>> z1.conjugate() # flip the z1.imag sign
+(1.5-2j)
+
+>>> abs(z1) # sqrt(z1.real ** 2 + z1.imag ** 2)
+2.5
+```
+
+## The `cmath` module
+
+The Python standard library has a [`math`][math-module] module full of useful functionality for working with real numbers.
+
+It also has an equivalent [`cmath`][cmath] module for working with complex numbers.
+
+
+We encourage you to read through the module and experiment, but the main categories are:
+
+- Conversion between Cartesian and polar coordinates,
+- Exponential and log functions,
+- Trigonometric functions,
+- Hyperbolic functions,
+- Classification functions, and
+- Useful constants.
+
+Here is an example using some constants:
+
+```python
+>>> import cmath
+
+>>> euler = cmath.exp(1j * cmath.pi) # Euler's equation
+
+>>> euler.real
+-1.0
+>>> round(euler.imag, 15) # round to 15 decimal places
+0.0
+```
+
+So a simple expression with three of the most important constants in nature `e`, `i` (or `j`) and `pi` gives the result `-1`.
+Some people believe this is the most beautiful result in all of mathematics.
+It dates back to around 1740.
+
+-----
+
+## Optional section: a Complex Numbers FAQ
+
+This part can be skipped, unless you are interested.
+
+### Isn't this some strange new piece of pure mathematics?
+
+It was strange and new in the 16th century.
+
+500 years later, it is central to most of engineering and the physical sciences.
+
+### Why would anyone use these?
+
+It turns out that complex numbers are the simplest way to describe anything that rotates or anything with a wave-like property.
+So they are used widely in electrical engineering, audio processing, physics, computer gaming, and navigation - to name only a few applications.
+
+You can see things rotate.
+Complex numbers may not make the world go round, but they are great for explaining _what happens_ as a result of the world going round: look at any satellite image of a major storm.
+
+
+Less obviously, sound is wave-like, light is wave-like, radio signals are wave-like, and even the economy of your home country is at least partly wave-like.
+
+
+A lot of this wave processing can be done with trig functions (`sin()` and `cos()`) but that gets messy quite quickly.
+
+Complex exponentials are ___much___ easier to work with.
+
+### But I don't need complex numbers!
+
+
+Only true if you are living in a cave and foraging for your food.
+
+If you are reading this on any sort of screen, you are utterly dependent on some useful 20th-Century advances made through the use of complex numbers.
+
+
+1. __Semiconductor chips__.
+ - These make no sense in classical physics and can only be explained (and designed) by quantum mechanics (QM).
+ - In QM, everything is complex-valued by definition. (_its waveforms all the way down_)
+
+2. __The Fast Fourier Transform algorithm__.
+ - FFT is an application of complex numbers, and it is in _everything_ connected to sound transmission, audio processing, photos, and video.
+
+ -MP3 and other audio formats use FFT for compression, ensuring more audio can fit within a smaller storage space.
+ - JPEG compression and MP4 video, among many other image and video formats also use FTT for compression.
+
+ - FFT is also deployed in the digital filters that allow cellphone towers to separate your personal cell signal from everyone else's.
+
+
+So, you are probably using technology that relies on complex number calculations thousands of times per second.
+
+
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[cmath]: https://docs.python.org/3/library/cmath.html
+[operators]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
+[math-module]: https://docs.python.org/3/library/math.html
+[math-complex]: https://www.nagwa.com/en/videos/143121736364/
+[engineering-complex]: https://www.khanacademy.org/science/electrical-engineering/ee-circuit-analysis-topic/ee-ac-analysis/v/ee-complex-numbers
+[ints]: https://docs.python.org/3/library/functions.html#int
+[floats]: https://docs.python.org/3/library/functions.html#float
diff --git a/concepts/complex-numbers/introduction.md b/concepts/complex-numbers/introduction.md
index fcde74642ca..a82f47cb6cb 100644
--- a/concepts/complex-numbers/introduction.md
+++ b/concepts/complex-numbers/introduction.md
@@ -1,2 +1,93 @@
-#TODO: Add introduction for this concept.
+# Introduction
+`Complex numbers` are not complicated.
+They just need a less alarming name.
+
+They are so useful, especially in engineering and science (_everything from JPEG compression to quantum mechanics_), that Python includes [`complex`][complex] as a standard numeric type alongside integers ([`int`s][ints]) and floating-point numbers ([`float`s][floats]).
+
+A `complex` value in Python is essentially a pair of floating-point numbers:
+
+```python
+>>> my_complex = 5.443+6.77j
+(5.443+6.77j)
+```
+
+These are called the "real" and "imaginary" parts.
+You may have heard that "`i` (or `j`) is the square root of -1".
+For now, all this means is that the imaginary part _by definition_ satisfies the equality `1j * 1j == -1`.
+This is a simple idea, but it leads to interesting mathematical consequences.
+
+In Python, the "imaginary" part is designated with `j` (_not `i` as you would see in math textbooks_), and
+the `j` must immediately follow a number, to prevent Python seeing it as a variable name:
+
+
+```python
+>>> j
+Traceback (most recent call last):
+ File "", line 1, in
+NameError: name 'j' is not defined
+
+>>> 1j
+1j
+
+>>> type(1j)
+
+```
+
+
+There are two common ways to create complex numbers.
+
+1) The [`complex(real, imag)`][complex] constructor takes two `float` parameters:
+
+ ```python
+ >>> z1 = complex(1.5, 2.0)
+ >>> z1
+ (1.5+2j)
+ ```
+
+ The constructor can also parse string input.
+ This has the odd limitation that it fails if the string contains spaces.
+
+ ```python
+ >>> complex('4+2j')
+ (4+2j)
+
+ >>> complex('4 + 2j')
+ Traceback (most recent call last):
+ File "", line 1, in
+ ValueError: complex() arg is a malformed string
+ ```
+
+
+2) The complex number can be specified as ` + j` literal, or just `j` if the real part is zero:
+
+
+ ```python
+ >>> z2 = 2.0 + 1.5j
+ >>> z2
+ (2+1.5j)
+ ```
+ The end result is identical to using the `complex()` constructor.
+
+
+## Arithmetic
+
+Most of the [`operators`][operators] used with floats and ints also work with complex numbers.
+
+Integer division is _**not**_ possible on complex numbers, so the `//` and `%` operators and `divmod()` functions will fail for the complex number type.
+
+Explaining the rules for complex number multiplication and division is out of scope for this concept (_and you are unlikely to have to perform those operations "by hand" very often_).
+
+Any [mathematical][math-complex] or [electrical engineering][engineering-complex] introduction to complex numbers will cover these scenarios, should you want to dig into the topic.
+
+The Python standard library has a [`math`][math-module] module full of useful functionality for working with real numbers and the [`cmath`][cmath] module is its equivalent for working with complex numbers.
+
+
+[cmath]: https://docs.python.org/3/library/cmath.html
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[engineering-complex]: https://www.khanacademy.org/science/electrical-engineering/ee-circuit-analysis-topic/ee-ac-analysis/v/ee-complex-numbers
+[floats]: https://docs.python.org/3/library/functions.html#float
+[ints]: https://docs.python.org/3/library/functions.html#int
+[math-complex]: https://www.nagwa.com/en/videos/143121736364/
+[math-module]: https://docs.python.org/3/library/math.html
+[operators]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
diff --git a/concepts/complex-numbers/links.json b/concepts/complex-numbers/links.json
index eb5fb7c38a5..759ef1689ff 100644
--- a/concepts/complex-numbers/links.json
+++ b/concepts/complex-numbers/links.json
@@ -1,18 +1,18 @@
[
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex/",
+ "description": "Operations on numeric types."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://docs.python.org/3/library/functions.html#complex/",
+ "description": "The complex class."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://docs.python.org/3/library/cmath.html/",
+ "description": "Module documentation for cmath."
},
{
- "url": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "url": "https://docs.python.org/3/library/math.html/",
+ "description": "Module documentation for math."
}
]
diff --git a/concepts/conditionals/about.md b/concepts/conditionals/about.md
index 3b3d5b1ba1d..2060905b335 100644
--- a/concepts/conditionals/about.md
+++ b/concepts/conditionals/about.md
@@ -8,7 +8,6 @@ Python 3.10 introduces a variant case-switch statement called `pattern matching`
Conditional statements use expressions that must resolve to `True` or `False` -- either by returning a `bool` directly, or by evaluating ["truthy" or "falsy"][truth value testing].
-
```python
x = 10
y = 5
@@ -55,19 +54,21 @@ else:
>>> z is greater than x and y
```
-[Boolen operations][boolean operations] and [comparisons][comparisons] can be combined with conditionals for more complex testing:
+[Boolean operations][boolean operations] and [comparisons][comparisons] can be combined with conditionals for more complex testing:
```python
>>> def classic_fizzbuzz(number):
if number % 3 == 0 and number % 5 == 0:
- return 'FizzBuzz!'
+ say = 'FizzBuzz!'
elif number % 5 == 0:
- return 'Buzz!'
+ say = 'Buzz!'
elif number % 3 == 0:
- return 'Fizz!'
+ say = 'Fizz!'
else:
- return str(number)
+ say = str(number)
+
+ return say
>>> classic_fizzbuzz(15)
'FizzBuzz!'
@@ -76,29 +77,54 @@ else:
'13'
```
+As an alternative, the example above can be re-written to only use `if` statements with `returns`.
+However, re-writing in this way might obscure that the conditions are intended to be [_mutually exclusive_][mutually-exclusive] and could lead to future bugs or maintenance issues.
+
+
+```python
+>>> def classic_fizzbuzz(number):
+ if number % 3 == 0 and number % 5 == 0:
+ return 'FizzBuzz!'
+ if number % 5 == 0:
+ return 'Buzz!'
+ if number % 3 == 0:
+ return 'Fizz!'
+
+ return str(number)
+
+>>> classic_fizzbuzz(15)
+'FizzBuzz!'
+
+>>> classic_fizzbuzz(13)
+'13'
+```
+
+
Conditionals can also be nested.
```python
>>> def driving_status(driver_age, test_score):
if test_score >= 80:
if 18 > driver_age >= 16:
- return "Student driver, needs supervision."
+ status = "Student driver, needs supervision."
elif driver_age == 18:
- return "Permitted driver, on probation."
+ status = "Permitted driver, on probation."
elif driver_age > 18:
- return "Fully licensed driver."
+ status = "Fully licensed driver."
else:
- return "Unlicensed!"
+ status = "Unlicensed!"
+
+ return status
>>> driving_status(63, 78)
-'Unlicsensed!'
+'Unlicensed!'
>>> driving_status(16, 81)
'Student driver, needs supervision.'
>>> driving_status(23, 80)
-'Fully licsensed driver.'
+'Fully licensed driver.'
```
## Conditional expressions or "ternary operators"
@@ -115,8 +141,8 @@ def just_the_buzz(number):
>>> just_the_buzz(15)
'Buzz!'
->>> just_the_buzz(10)
-'10'
+>>> just_the_buzz(7)
+'7'
```
## Truthy and Falsy
@@ -151,8 +177,9 @@ This is Truthy.
Nope. It's Falsey.
```
-[if statement]: https://docs.python.org/3/reference/compound_stmts.html#the-if-statement
-[control flow tools]: https://docs.python.org/3/tutorial/controlflow.html#more-control-flow-tools
-[truth value testing]: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
[boolean operations]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
[comparisons]: https://docs.python.org/3/library/stdtypes.html#comparisons
+[control flow tools]: https://docs.python.org/3/tutorial/controlflow.html#more-control-flow-tools
+[if statement]: https://docs.python.org/3/reference/compound_stmts.html#the-if-statement
+[mutually-exclusive]: https://stackoverflow.com/a/22783232
+[truth value testing]: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
diff --git a/concepts/conditionals/introduction.md b/concepts/conditionals/introduction.md
index 29e3e635975..ee1d4336207 100644
--- a/concepts/conditionals/introduction.md
+++ b/concepts/conditionals/introduction.md
@@ -45,10 +45,8 @@ z = 20
# The elif statement allows for the checking of more conditions.
if x > y > z:
-
print("x is greater than y and z")
elif y > x > z:
-
print("y is greater than x and z")
else:
print("z is greater than x and y")
@@ -56,19 +54,20 @@ else:
>>> z is greater than x and y
```
-[Boolen operations][boolean operations] and [comparisons][comparisons] can be combined with conditionals for more complex testing:
+[Boolean operations][boolean operations] and [comparisons][comparisons] can be combined with conditionals for more complex testing:
```python
-
>>> def classic_fizzbuzz(number):
if number % 3 == 0 and number % 5 == 0:
- return 'FizzBuzz!'
+ say = 'FizzBuzz!'
elif number % 5 == 0:
- return 'Buzz!'
+ say = 'Buzz!'
elif number % 3 == 0:
- return 'Fizz!'
+ say = 'Fizz!'
else:
- return str(number)
+ say = str(number)
+
+ return say
>>> classic_fizzbuzz(15)
'FizzBuzz!'
@@ -77,8 +76,8 @@ else:
'13'
```
-[if statement]: https://docs.python.org/3/reference/compound_stmts.html#the-if-statement
-[control flow tools]: https://docs.python.org/3/tutorial/controlflow.html#more-control-flow-tools
-[truth value testing]: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
[boolean operations]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
[comparisons]: https://docs.python.org/3/library/stdtypes.html#comparisons
+[control flow tools]: https://docs.python.org/3/tutorial/controlflow.html#more-control-flow-tools
+[if statement]: https://docs.python.org/3/reference/compound_stmts.html#the-if-statement
+[truth value testing]: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
diff --git a/concepts/dict-methods/about.md b/concepts/dict-methods/about.md
index d910d3e9168..6dcf9b4ae7a 100644
--- a/concepts/dict-methods/about.md
+++ b/concepts/dict-methods/about.md
@@ -214,7 +214,7 @@ When both dictionaries share keys, `dict_two` values take precedence.
'Purple baseline': '#161748'}
```
-`dict_one |= other` behaves similar to `.update()`, but in this case, `other` can be either a `dict` or an iterable of (`key`, `value`) pairs:
+`dict_one |= other` behaves similar to `.update()`, but in this case, `other` can be either a `dict` or an iterable of (`key`, `value`) pairs:
```python
>>> palette_III = {'Grassy Green': (155, 196, 0),
diff --git a/concepts/dicts/about.md b/concepts/dicts/about.md
index c34160b2ef6..1f83fe6e30a 100644
--- a/concepts/dicts/about.md
+++ b/concepts/dicts/about.md
@@ -108,7 +108,7 @@ animals = {
## Accessing Values in a `dict`
You can access a `value` in a dictionary using a _key_ in square brackets.
-If a key does not exist, a `KeyError` is thrown:
+If a key does not exist in the dictionary, a `KeyError` is thrown:
```python
>>> bear["speed"]
@@ -172,7 +172,7 @@ You can change an entry `value` by assigning to its _key_:
New `key`:`value` pairs can be _added_ in the same fashion:
```python
-# Adding an new "color" key with a new "tawney" value.
+# Adding a new "color" key with a new "tawney" value.
>>> bear["color"] = 'tawney'
{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True, 'color': 'tawney'}
diff --git a/concepts/fractions/.meta/config.json b/concepts/fractions/.meta/config.json
new file mode 100644
index 00000000000..621a3766d84
--- /dev/null
+++ b/concepts/fractions/.meta/config.json
@@ -0,0 +1,5 @@
+{
+ "blurb": "The fractions module enables working with rational numbers, which preserve exact values and avoid the rounding errors common with floats.",
+ "authors": ["BethanyG", "colinleach"],
+ "contributors": []
+}
diff --git a/concepts/fractions/about.md b/concepts/fractions/about.md
new file mode 100644
index 00000000000..d41124c39c4
--- /dev/null
+++ b/concepts/fractions/about.md
@@ -0,0 +1,122 @@
+# About
+
+The [`Fractions`][fractions] module allows us to create and work with [`rational numbers`][rational]: fractions with an integer numerator divided by an integer denominator.
+
+For example, we can store `2/3` as an exact fraction instead of the approximate `float` value `0.6666...`
+
+## Creating Fractions
+
+
+Unlike `int`, `float`, and `complex` numbers, fractions do not have a literal form.
+However, the fractions constructor is quite flexible.
+
+Most obviously, it can take take two integers.
+Common factors are automatically removed, converting the fraction to its "lowest form": the smallest integers that accurately represent the fraction.
+
+
+```python
+>>> from fractions import Fraction
+
+>>> f1 = Fraction(2, 3) # 2/3
+>>> f1
+Fraction(2, 3)
+
+>>> f2 = Fraction(6, 9)
+>>> f2
+Fraction(2, 3) # automatically simplified
+
+>>> f1 == f2
+True
+```
+
+The fractions constructor can also parse a string representation:
+
+
+```python
+>>> f3 = Fraction('2/3')
+>>> f3
+Fraction(2, 3)
+```
+
+It can also work with `float` parameters, but this may run into problems with the approximate nature of representing the decimal value internally as binary.
+For more on this representation issue, see the [0.30000000000000004][0.30000000000000004] website, and [Floating Point Arithmetic: Issues and Limitations ][fp-issues] in the Python documentation.
+
+For a more reliable result when using floats with fractions, there is the `.limit_denominator()` method.
+
+
+[`.limit_denominator()`][limit_denominator] can take an integer parameter if you have specific requirements, but even the default (`max_denominator=1000000`) can work well and give an acceptable, simple approximation.
+
+```python
+>>> Fraction(1.2)
+Fraction(5404319552844595, 4503599627370496)
+
+>>> Fraction(1.2).limit_denominator()
+Fraction(6, 5)
+```
+
+## Arithmetic with Fractions
+
+
+The usual [`arithmetic operators`][operators] `+ - * / **` work with fractions, as with other numeric types.
+
+Integers and other `Fraction`s can be included and give a `Fraction` result.
+Including a `float` in the expression results in `float` output, with a consequent (possible) loss in precision.
+
+
+```python
+>>> Fraction(2, 3) + Fraction(1, 4) # addition
+Fraction(11, 12)
+
+>>> Fraction(2, 3) * Fraction(6, 5) # multiply fractions
+Fraction(4, 5)
+
+>>> Fraction(2, 3) * 6 / 5 # fraction with integers
+Fraction(4, 5)
+
+>>> Fraction(2, 3) * 1.2 # fraction with float -> float
+0.7999999999999999
+
+>>> Fraction(2, 3) ** 2 # exponentiation with integer
+Fraction(4, 9)
+```
+
+## Conversions to and from Fractions
+
+
+Fractions are great for preserving precision during intermediate calculations, but may not be what you want for the final output.
+
+It is possible to get the numerator and denominator individually or as a tuple ([`tuples`][tuple] will be discussed in a later Concept):
+
+```python
+>>> Fraction(2, 3).numerator
+2
+>>> Fraction(2, 3).denominator
+3
+>>> Fraction(2, 3).as_integer_ratio()
+(2, 3)
+```
+
+Various standard Python numeric functions also give the result you might expect from working with `int` and `float` types:
+
+```python
+>>> round(Fraction(11, 3))
+4
+
+>>> from math import floor, ceil
+>>> floor(Fraction(11, 3))
+3
+>>> ceil(Fraction(11, 3))
+4
+
+>>> float(Fraction(11, 3))
+3.6666666666666665
+```
+
+[fractions]: https://docs.python.org/3/library/fractions.html
+[0.30000000000000004]: https://0.30000000000000004.com/
+[fp-issues]: https://docs.python.org/3/tutorial/floatingpoint.html#tut-fp-issues
+[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
+
+[operators]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
+[rational]: https://en.wikipedia.org/wiki/Rational_number
+[limit_denominator]: https://docs.python.org/3/library/fractions.html
diff --git a/concepts/fractions/introduction.md b/concepts/fractions/introduction.md
new file mode 100644
index 00000000000..437ccbbeb07
--- /dev/null
+++ b/concepts/fractions/introduction.md
@@ -0,0 +1,85 @@
+# Introduction
+
+The [`Fractions`][fractions] module allows us to create and work with [`rational numbers`][rational]: fractions with an integer numerator divided by an integer denominator.
+For example, we can store `2/3` as an exact fraction instead of the approximate `float` value `0.6666...`.
+
+Unlike `int`, `float`, and `complex` numbers, fractions do not have a literal form.
+However, the fractions constructor is quite flexible.
+
+Most obviously, it can take take two integers as arguments.
+Common factors are automatically removed, converting the fraction to its "lowest form": the smallest integers that accurately represent the fraction:
+
+```python
+>>> from fractions import Fraction
+
+>>> f1 = Fraction(2, 3) # 2/3
+>>> f1
+Fraction(2, 3)
+
+>>> f2 = Fraction(6, 9)
+>>> f2
+Fraction(2, 3) # automatically simplified
+
+>>> f1 == f2
+True
+```
+
+The fractions constructor can also parse a string representation:
+
+```python
+>>> f3 = Fraction('2/3')
+>>> f3
+Fraction(2, 3)
+```
+
+Fractions can also work with `float` parameters, but this may run into problems with the approximate nature of representing the decimal value internally as binary.
+For more on this representation issue, see the [0.30000000000000004][0.30000000000000004] website, and [Floating Point Arithmetic: Issues and Limitations ][fp-issues] in the Python documentation.
+
+For a more reliable result when using floats with fractions, there is the `.limit_denominator()` method.
+
+
+## Arithmetic with Fractions
+
+The usual [`arithmetic operators`][operators] `+ - * / **` will work with fractions, as with other numeric types.
+
+Integers and other `Fraction`s can be included in the equation and give a `Fraction` result.
+Including a `float` in the expression results in `float` output, with a consequent (possible) loss in precision:
+
+```python
+>>> Fraction(2, 3) + Fraction(1, 4) # addition
+Fraction(11, 12)
+
+>>> Fraction(2, 3) * Fraction(6, 5) # multiply fractions
+Fraction(4, 5)
+
+>>> Fraction(2, 3) * 6 / 5 # fraction with integers
+Fraction(4, 5)
+
+>>> Fraction(2, 3) * 1.2 # fraction with float -> float
+0.7999999999999999
+
+>>> Fraction(2, 3) ** 2 # exponentiation with integer
+Fraction(4, 9)
+```
+
+Various standard Python numeric functions also give the result you might expect from working with `int` and `float` types:
+
+```python
+>>> round(Fraction(11, 3))
+4
+
+>>> from math import floor, ceil
+>>> floor(Fraction(11, 3))
+3
+>>> ceil(Fraction(11, 3))
+4
+
+>>> float(Fraction(11, 3))
+3.6666666666666665
+```
+
+[0.30000000000000004]: https://0.30000000000000004.com/
+[fp-issues]: https://docs.python.org/3/tutorial/floatingpoint.html#tut-fp-issues
+[fractions]: https://docs.python.org/3/library/fractions.html
+[operators]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
+[rational]: https://en.wikipedia.org/wiki/Rational_number
diff --git a/concepts/fractions/links.json b/concepts/fractions/links.json
new file mode 100644
index 00000000000..78d349bcfc3
--- /dev/null
+++ b/concepts/fractions/links.json
@@ -0,0 +1,18 @@
+[
+ {
+ "url": "https://docs.python.org/3/library/fractions.html/",
+ "description": "Documentation for the Fractions module."
+ },
+ {
+ "url": "https://docs.python.org/3/tutorial/floatingpoint.html#tut-fp-issues",
+ "description": "Limitations of Floating Point Arithmetic."
+ },
+ {
+ "url": "https://leancrew.com/all-this/2023/08/decimal-to-fraction/",
+ "description": "And now it's all this: Decimal to fraction."
+ },
+ {
+ "url": "https://nrich.maths.org/2515",
+ "description": "History of Fractions."
+ }
+]
diff --git a/concepts/function-arguments/about.md b/concepts/function-arguments/about.md
index 09b01b10862..0f2ab5dddda 100644
--- a/concepts/function-arguments/about.md
+++ b/concepts/function-arguments/about.md
@@ -4,7 +4,7 @@ For the basics on function arguments, please see the [function concept][function
## Parameter Names
-Paramater names, like variable names, must start with a letter or underscore and may contain letters, underscores, or numbers.
+Parameter names, like variable names, must start with a letter or underscore and may contain letters, underscores, or numbers.
Parameter names should not contain spaces or punctuation.
## Positional Arguments
@@ -182,7 +182,7 @@ For instance, `*` is used for multiplication, it is used for unpacking, and it i
Since a tuple can be iterated, `args` can be passed to any other function which takes an iterable.
Although `*args` is commonly juxtaposed with `**kwargs`, it doesn't have to be.
-Following is an example of an arbitrary amount of values being passed to a function:
+Following is an example of an arbitrary number of values being passed to a function:
```python
@@ -196,7 +196,7 @@ Following is an example of an arbitrary amount of values being passed to a funct
If `*args` follows one or more positional arguments, then `*args` will be what is left over after the positional arguments.
-Following is an example of an arbitrary amount of values being passed to a function after a positional argument:
+Following is an example of an arbitrary number of values being passed to a function after a positional argument:
```python
@@ -210,7 +210,7 @@ Following is an example of an arbitrary amount of values being passed to a funct
If one or more default arguments are defined after `*args` they are separate from the `*args` values.
-To put it all together is an example of an arbitrary amount of values being passed to a function that also has a positional argument and a default argument:
+To put it all together is an example of an arbitrary number of values being passed to a function that also has a positional argument and a default argument:
```python
@@ -228,7 +228,7 @@ To put it all together is an example of an arbitrary amount of values being pass
```
-Note that when an argument is already in an iterable, such as a tuple or list, it needs to be unpacked before being passed to a function that takes an arbitrary amount of separate arguments.
+Note that when an argument is already in an iterable, such as a tuple or list, it needs to be unpacked before being passed to a function that takes an arbitrary number of separate arguments.
This is accomplished by using `*`, which is the [unpacking operator][unpacking operator].
`*` in this context _unpacks_ the container into its separate elements which are then transformed by `*args` into a tuple.
@@ -257,7 +257,7 @@ The `**` transforms the group of named arguments into a [`dictionary`][dictionar
Since a dictionary can be iterated, `kwargs` can be passed to any other function which takes an iterable.
Although `**kwargs` is commonly juxtaposed with `*args`, it doesn't have to be.
-Following is an example of an arbitrary amount of key-value pairs being passed to a function:
+Following is an example of an arbitrary number of key-value pairs being passed to a function:
```python
>>> def add(**kwargs):
@@ -271,7 +271,7 @@ Note that the `dict.values()` method is called to iterate through the `kwargs` d
When iterating a dictionary the default is to iterate the keys.
-Following is an example of an arbitrary amount of key-value pairs being passed to a function that then iterates over `kwargs.keys()`:
+Following is an example of an arbitrary number of key-value pairs being passed to a function that then iterates over `kwargs.keys()`:
```python
>>> def concat(**kwargs):
diff --git a/concepts/function-arguments/introduction.md b/concepts/function-arguments/introduction.md
index 171675ce3c4..07b885f332e 100644
--- a/concepts/function-arguments/introduction.md
+++ b/concepts/function-arguments/introduction.md
@@ -4,7 +4,7 @@ For the basics on function arguments, please see the [function concept][function
## Parameter Names
-Paramater names, like variable names, must start with a letter or underscore and may contain letters, underscores, or numbers.
+Parameter names, like variable names, must start with a letter or underscore and may contain letters, underscores, or numbers.
Parameter names should not contain spaces or punctuation.
## Positional Arguments
diff --git a/concepts/functions/about.md b/concepts/functions/about.md
index 9d8fddfa956..f3630af763c 100644
--- a/concepts/functions/about.md
+++ b/concepts/functions/about.md
@@ -2,11 +2,11 @@
A [`function`][function] is a block of organized, reusable code that is used to perform a specific task.
`Functions` provide better modularity for your application and a high degree of code reuse.
-Python, like other programming languages, has [_built-in functions_][build-in functions] ([`print`][print], [`map`][map], and so on) that are readily available.
+Python, like other programming languages, has [_built-in functions_][built-in functions] ([`print`][print], [`map`][map], and so on) that are readily available.
You can also define your own functions. Those are called [`user-defined functions`][user defined functions].
Functions can run something as simple as _printing a message to the console_ or they can be quite complex.
-To execute the code inside a function, you need to call the function, which is done by using the function name followed by parenthesese [`()`].
+To execute the code inside a function, you need to call the function, which is done by using the function name followed by parentheses [`()`].
Data, known as [`arguments`][arguments], can be passed to the function by placing them inside the parenthesese.
A function definition may include zero or more [`parameters`][parameters].
Parameters define what argument(s) the function accepts.
@@ -376,7 +376,7 @@ The full list of function attributes can be found at [Python DataModel][attribut
[LEGB Rule]: https://realpython.com/python-scope-legb-rule/
[arguments]: https://www.w3schools.com/python/gloss_python_function_arguments.asp
[attributes]: https://docs.python.org/3/reference/datamodel.html#index-33
-[build-in functions]: https://docs.python.org/3/library/functions.html
+[built-in functions]: https://docs.python.org/3/library/functions.html
[def]: https://www.geeksforgeeks.org/python-def-keyword/
[dict]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
[first class objects]: https://en.wikipedia.org/wiki/First-class_object
diff --git a/concepts/functools/about.md b/concepts/functools/about.md
index 32748a45c23..e5afb577d39 100644
--- a/concepts/functools/about.md
+++ b/concepts/functools/about.md
@@ -12,7 +12,7 @@ The functools module is for higher-order functions: functions that act on or ret
Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable.
-Here ```maxsize = 128``` means that it is going to memoize lastest 128 function calls at max.
+Here ```maxsize = 128``` means that it is going to memoize latest 128 function calls at max.
The lru_cache works the same way but it can cache at max maxsize calls and if type = True, then the function arguments of different types will be cached separately i.e. 5 and 5.0 will be cached differently.
@@ -23,8 +23,8 @@ The lru_cache works the same way but it can cache at max maxsize calls and if ty
```python
>>> @cache
- def factorial(n):
- return n * factorial(n-1) if n else 1
+>>> def factorial(n):
+>>> return n * factorial(n-1) if n else 1
>>> factorial(10) # no previously cached result, makes 11 recursive calls
3628800
@@ -37,9 +37,10 @@ The lru_cache works the same way but it can cache at max maxsize calls and if ty
# Some types such as str and int may be cached separately even when typed is false.
-@lru_cache(maxsize = 128)
- def factorial(n):
- return n * factorial(n-1) if n else 1
+>>> @lru_cache(maxsize = 128)
+>>> def factorial(n):
+>>> return n * factorial(n-1) if n else 1
+
>>> factorial(10)
3628800
@@ -50,7 +51,7 @@ CacheInfo(hits=0, misses=11, maxsize=128, currsize=11)
## Generic functions
-***[Generic functions](https://pymotw.com/3/functools/#generic-functions)*** are those which preform the operation based on the argument given to them. In statically typed languages it can be done by function overloading.
+***[Generic functions](https://pymotw.com/3/functools/#generic-functions)*** are those which perform the operation based on the argument given to them. In statically typed languages it can be done by function overloading.
In python functools provides the `singledispatch()` decorator to register a set of generic functions for automatic switching based on the type of the first argument to a function.
@@ -193,11 +194,11 @@ True
The ```pow_2.func``` is same as the ```pow``` function.
-Here ```pow_2.args``` return an empty tuple becuse we does not pass any positional argument to out partial object call.
+Here ```pow_2.args``` returns an empty tuple because we do not pass any positional argument to our partial object call.
-```pow_2.keywords``` return a dictionary of keywords argument which will be supplied when the partial object is called.
+```pow_2.keywords``` returns a dictionary of keywords argument which will be supplied when the partial object is called.
-Here ```two_pow.args``` return an ```(2,)``` tuple because we passed 2 as an argument while creating the pratial object, which fixed the value of ```base``` argument as ```2```.
+Here ```two_pow.args``` returns a ```(2,)``` tuple because we passed 2 as an argument while creating the partial object, which fixed the value of ```base``` argument as ```2```.
### ```partialmethod```
diff --git a/concepts/functools/introduction.md b/concepts/functools/introduction.md
index 15e83e3e61a..c91aedc81bd 100644
--- a/concepts/functools/introduction.md
+++ b/concepts/functools/introduction.md
@@ -12,7 +12,7 @@ The functools module is for higher-order functions: functions that act on or ret
Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable.
-Here ```maxsize = 128``` means that it is going to memoize lastest 128 function calls at max.
+Here ```maxsize = 128``` means that it is going to memoize latest 128 function calls at max.
### ```@functools.cache(user_function)```
diff --git a/concepts/generators/.meta/config.json b/concepts/generators/.meta/config.json
index 6c29169d3f4..3322727ef74 100644
--- a/concepts/generators/.meta/config.json
+++ b/concepts/generators/.meta/config.json
@@ -1,5 +1,9 @@
{
- "blurb": "Generator are functions that returns a lazy iterator, lazy iterator is an iterator like: list, tuple, etc. But doesn't need store its content in memory",
+ "blurb": "Generators are functions or expressions that return a special type of iterator called a 'generator-iterator'. Generator-iterators are lazy: they do not store their values in memory but generate them when needed.",
"authors": ["J08K"],
- "contributors": []
+ "contributors": [
+ "BethanyG",
+ "kytrinyx",
+ "meatball133"
+ ]
}
diff --git a/concepts/generators/about.md b/concepts/generators/about.md
index 9a26ab55480..59b5035d6b9 100644
--- a/concepts/generators/about.md
+++ b/concepts/generators/about.md
@@ -1,5 +1,13 @@
# About
+A `generator` is a function or expression that returns a special type of [iterator][iterator] called [generator iterator][generator-iterator].
+`Generator-iterators` are [lazy][lazy iterator]: they do not store their `values` in memory, but _generate_ their values when needed.
+
+A generator function looks like any other function, but contains one or more [yield expressions][yield expression].
+Each `yield` will suspend code execution, saving the current execution state (_including all local variables and try-statements_).
+When the generator resumes, it picks up state from the suspension - unlike regular functions which reset with every call.
+
+
## Constructing a generator
Generators are constructed much like other looping or recursive functions, but require a [`yield` expression](#the-yield-expression), which we will explore in depth a bit later.
@@ -94,9 +102,9 @@ When `yield` is evaluated, it pauses the execution of the enclosing function and
The function then _stays in scope_, and when `__next__()` is called, execution resumes until `yield` is encountered again.
-```exercism/note
+~~~~exercism/note
Using `yield` expressions is prohibited outside of functions.
-```
+~~~~
```python
>>> def infinite_sequence():
@@ -131,5 +139,9 @@ Generators are also very helpful when a process or calculation is _complex_, _ex
Now whenever `__next__()` is called on the `infinite_sequence` object, it will return the _previous number_ + 1.
+
+[generator-iterator]: https://docs.python.org/3.11/glossary.html#term-generator-iterator
[iterables]: https://wiki.python.org/moin/Iterator
+[iterator]: https://docs.python.org/3.11/glossary.html#term-iterator
+[lazy iterator]: https://en.wikipedia.org/wiki/Lazy_evaluation
[yield expression]: https://docs.python.org/3.11/reference/expressions.html#yield-expressions
diff --git a/concepts/generators/introduction.md b/concepts/generators/introduction.md
index 82a686d1e05..ad1175ca0b6 100644
--- a/concepts/generators/introduction.md
+++ b/concepts/generators/introduction.md
@@ -1,7 +1,13 @@
# Introduction
-A generator in Python is a _callable function_ that returns a [lazy iterator][lazy iterator].
+A generator in Python is a _callable function_ or expression that returns a special type of [iterator][iterator] called [generator iterator][generator-iterator].
+`Generator-iterators` are [lazy][lazy iterator]: they do not store their `values` in memory, but _generate_ their values when needed.
-_Lazy iterators_ are similar to `lists`, and other `iterators`, but with one key difference: They do not store their `values` in memory, but _generate_ their values when needed.
+A generator function looks like any other function, but contains one or more [yield expressions][yield expression].
+Each `yield` will suspend code execution, saving the current execution state (_including all local variables and try-statements_).
+When the generator function resumes, it picks up state from the suspension - unlike regular functions which reset with every call.
[lazy iterator]: https://en.wikipedia.org/wiki/Lazy_evaluation
+[iterator]: https://docs.python.org/3.11/glossary.html#term-iterator
+[yield expression]: https://docs.python.org/3.11/reference/expressions.html#yield-expressions
+[generator-iterator]: https://docs.python.org/3.11/glossary.html#term-generator-iterator
diff --git a/concepts/generators/links.json b/concepts/generators/links.json
index 972bbe7ae97..134a723c693 100644
--- a/concepts/generators/links.json
+++ b/concepts/generators/links.json
@@ -1,12 +1,16 @@
[
{
- "url": "https://docs.python.org/3.10/reference/expressions.html#yield-expressions",
+ "url": "https://docs.python.org/3.11/reference/expressions.html#yield-expressions",
"description": "Official Python 3.10 docs for the yield expression."
},
{
"url": "https://en.wikipedia.org/wiki/Lazy_evaluation",
"description": "Wikipedia page about lazy evaluation"
},
+ {
+ "url": "https://www.pythonmorsels.com/iterators/",
+ "description": "Python Morsels: Iterators & Generators"
+ },
{
"url": "https://realpython.com/introduction-to-python-generators/",
"description": "Real python, introduction to generators and yield"
diff --git a/concepts/list-methods/about.md b/concepts/list-methods/about.md
index 3a5132fd0ad..1c9686360d4 100644
--- a/concepts/list-methods/about.md
+++ b/concepts/list-methods/about.md
@@ -136,10 +136,22 @@ The order of list elements can be reversed _**in place**_ with `.reverse(
[3, 2, 1]
```
-List elements can be sorted _**in place**_ using `.sort()`.
- Internally, Python uses [`Timsort`][timsort] to arrange the elements.
- The default order is _ascending_.
- The Python docs have [additional tips and techniques for sorting][sorting how to] `lists` effectively.
+A list can be re-ordered _**in place**_ with the help of [`.sort()`][sort].
+Default sort order is _ascending_ from the left.
+The Python docs offer [additional tips and techniques for sorting][sorting how to].
+
+
+~~~~exercism/note
+ From 2002 to 2022, Python used an algorithm called [`Timsort`][timsort] internally to arrange lists, but switched to [`Powersort`][powersort] from `Python 3.11` onward.
+You can read more details and discussion on the change from the core Python team in the GitHub [issue 78742][78742].
+
+For technical details on the algorithm, see the J. Ian Munro and Sebastian Wild paper [Nearly-Optimal Mergesorts: Fast, Practical Sorting Methods That Optimally Adapt to Existing Runs][nearly-optimal-mergesorts]
+
+[78742]: https://github.com/python/cpython/issues/78742
+[nearly-optimal-mergesorts]: https://arxiv.org/abs/1805.04154
+[powersort]: https://www.wild-inter.net/publications/munro-wild-2018
+[timsort]: https://en.wikipedia.org/wiki/Timsort
+~~~~
```python
@@ -254,7 +266,7 @@ For a detailed explanation of names, values, list, and nested list behavior, tak
[set]: https://docs.python.org/3/library/stdtypes.html#set
[shallow vs deep]: https://realpython.com/copying-python-objects/
[slice notation]: https://docs.python.org/3/reference/expressions.html#slicings
+[sort]: https://docs.python.org/3/library/stdtypes.html#list.sort
[sorted]: https://docs.python.org/3/library/functions.html#sorted
[sorting how to]: https://docs.python.org/3/howto/sorting.html
-[timsort]: https://en.wikipedia.org/wiki/Timsort
[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
diff --git a/concepts/lists/about.md b/concepts/lists/about.md
index 014a6d56725..f7d4054eef0 100644
--- a/concepts/lists/about.md
+++ b/concepts/lists/about.md
@@ -7,7 +7,7 @@ A [`list`][list] is a mutable collection of items in _sequence_.
Lists support both [common][common sequence operations] and [mutable][mutable sequence operations] sequence operations such as `min()`/`max()`, `.index()`, `.append()` and `.reverse()`.
- List elements can be iterated over using the `for item in ` construct. `for index, item in enumerate(` construct. `for index, item in enumerate()` can be used when both the element index and the element value are needed.
Lists are implemented as [dynamic arrays][dynamic array] -- similar to Java's [`Arraylist`][arraylist] type, and are most often used to store groups of similar data (_strings, numbers, sets etc._) of unknown length (_the number of entries may arbitrarily expand or shrink_).
@@ -18,7 +18,7 @@ Accessing elements, checking for membership via `in`, or appending items to the
For a similar data structure that supports memory efficient `appends`/`pops` from both sides, see [`collections.deque`][deque], which has approximately the same O(1) performance in either direction.
-Because lists are mutable and can contain references to arbitrary objects, they also take up more space in memory than a fixed-size [`array.array`][array.array] type of the same apparent length.
+Because lists are mutable and can contain references to arbitrary Python objects, they also take up more space in memory than an [`array.array`][array.array] or a [`tuple`][tuple] (_which is immutable_) of the same apparent length.
Despite this, lists are an extremely flexible and useful data structure and many built-in methods and operations in Python produce lists as their output.
@@ -135,7 +135,8 @@ TypeError: 'int' object is not iterable
## Accessing elements
-Items inside lists (_as well as elements in other sequence types such as [`str`][string] & [`tuple`][tuple]_), can be accessed using _bracket notation_. Indexes can be from **`left`** --> **`right`** (_starting at zero_) or **`right`** --> **`left`** (_starting at -1_).
+Items inside lists (_as well as elements in other sequence types such as [`str`][string] & [`tuple`][tuple]_), can be accessed using _bracket notation_.
+Indexes can be from **`left`** --> **`right`** (_starting at zero_) or **`right`** --> **`left`** (_starting at -1_).
@@ -173,9 +174,11 @@ Items inside lists (_as well as elements in other sequence types such as [`str`]
'Toast'
```
-A section of a list can be accessed via _slice notation_ (`[start:stop]`). A _slice_ is defined as an element sequence at position `index`, such that `start <= index < stop`. [_Slicing_][slice notation] returns a copy of the "sliced" items and does not modify the original `list`.
+A section of a list can be accessed via _slice notation_ (`[start:stop]`).
+A _slice_ is defined as an element sequence at position `index`, such that `start <= index < stop`.
+[_Slicing_][slice notation] returns a copy of the "sliced" items and does not modify the original `list`.
-A `step` parameter can also be used in the slice (`[start:stop:step]`) to "skip over" or filter the returned elements (_for example, a `step` of 2 will select every other element in the section_):
+A `step` parameter can also be used in the slice (`[::]`) to "skip over" or filter the returned elements (_for example, a `step` of 2 will select every other element in the section_):
```python
>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]
@@ -269,7 +272,7 @@ Lists can also be combined via various techniques:
>>> first_one
['George', 5, 'cat', 'Tabby']
-# This loops through the first list and appends it's items to the end of the second list.
+# This loops through the first list and appends its items to the end of the second list.
>>> first_one = ["cat", "Tabby"]
>>> second_one = ["George", 5]
@@ -284,7 +287,7 @@ Lists can also be combined via various techniques:
## Some cautions
Recall that variables in Python are _labels_ that point to _underlying objects_.
-`lists` add one more layer as _container objects_ -- they hold object references for their collected items.
+`lists` add one more layer as _container objects_ -- they hold object _references_ for their collected items.
This can lead to multiple potential issues when working with lists, if not handled properly.
@@ -293,7 +296,7 @@ Assigning a `list` object to a new variable _name_ **does not copy the `list` ob
Any change made to the elements in the `list` under the _new_ name _impact the original_.
-Making a `shallow_copy` via `list.copy()` or slice will avoid this first-leve referencing complication.
+Making a `shallow_copy` via `list.copy()` or slice will avoid this first-level referencing complication.
A `shallow_copy` will create a new `list` object, but **will not** create new objects for the contained list _elements_. This type of copy will usually be enough for you to add or remove items from the two `list` objects independently, and effectively have two "separate" lists.
@@ -305,21 +308,22 @@ A `shallow_copy` will create a new `list` object, but **will not** create new ob
# Altering the list via the new name is the same as altering the list via the old name.
>>> same_list.append("Clarke")
->>> same_list
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
+
>>> actual_names
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
# Likewise, altering the data in the list via the original name will also alter the data under the new name.
>>> actual_names[0] = "Wanda"
->>> same_list
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']
# If you copy the list, there will be two separate list objects which can be changed independently.
>>> copied_list = actual_names.copy()
>>> copied_list[0] = "Tony"
+
>>> actual_names
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']
+
>>> copied_list
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
```
@@ -455,4 +459,4 @@ The collections module also provides a `UserList` type that can be customized to
[set]: https://docs.python.org/3/library/stdtypes.html#set
[slice notation]: https://docs.python.org/3/reference/expressions.html#slicings
[string]: https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str
-[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
\ No newline at end of file
+[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
diff --git a/concepts/loops/about.md b/concepts/loops/about.md
index 88bb71e6194..0f39e733d0c 100644
--- a/concepts/loops/about.md
+++ b/concepts/loops/about.md
@@ -235,17 +235,17 @@ The loop [`else` clause][loop else] is unique to Python and can be used for "wra
'Found an S, stopping iteration.'
```
-[loop else]: https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
-[range]: https://docs.python.org/3/library/stdtypes.html#range
[break statement]: https://docs.python.org/3/reference/simple_stmts.html#the-break-statement
+[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
[continue statement]: https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement
-[while statement]: https://docs.python.org/3/reference/compound_stmts.html#the-while-statement
-[truth value testing]: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
[enumerate]: https://docs.python.org/3/library/functions.html#enumerate
-[iterator]: https://docs.python.org/3/glossary.html#term-iterator
-[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
-[range is not an iterator]: https://treyhunner.com/2018/02/python-range-is-not-an-iterator/
[for statement]: https://docs.python.org/3/reference/compound_stmts.html#for
[iterable]: https://docs.python.org/3/glossary.html#term-iterable
+[iterator]: https://docs.python.org/3/glossary.html#term-iterator
+[loop else]: https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
[next built-in]: https://docs.python.org/3/library/functions.html#next
+[range is not an iterator]: https://treyhunner.com/2018/02/python-range-is-not-an-iterator/
+[range]: https://docs.python.org/3/library/stdtypes.html#range
[stopiteration]: https://docs.python.org/3/library/exceptions.html#StopIteration
+[truth value testing]: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
+[while statement]: https://docs.python.org/3/reference/compound_stmts.html#the-while-statement
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index d5a4f7271ce..1155bcf7a5c 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -177,7 +177,7 @@ This means calculations within `()` have the highest priority, followed by `**`,
## Precision & Representation
-Integers in Python have [arbitrary precision][arbitrary-precision] -- the amount of digits is limited only by the available memory of the host system.
+Integers in Python have [arbitrary precision][arbitrary-precision] -- the number of digits is limited only by the available memory of the host system.
Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system.
Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
diff --git a/concepts/random/.meta/config.json b/concepts/random/.meta/config.json
new file mode 100644
index 00000000000..7319e329bad
--- /dev/null
+++ b/concepts/random/.meta/config.json
@@ -0,0 +1,5 @@
+{
+ "blurb": "The random module contains functionality to generate random values for modelling, simulations and games. It should not be used for security or cryptographic applications.",
+ "authors": ["BethanyG", "colinleach"],
+ "contributors": []
+}
diff --git a/concepts/random/about.md b/concepts/random/about.md
new file mode 100644
index 00000000000..9ed984179d3
--- /dev/null
+++ b/concepts/random/about.md
@@ -0,0 +1,245 @@
+# About
+
+Many programs need (apparently) random values to simulate real-world events.
+
+Common, familiar examples include:
+- A coin toss: a random value from `('H', 'T')`.
+- The roll of a die: a random integer from 1 to 6.
+- Shuffling a deck of cards: a random ordering of a card list.
+
+Generating truly random values with a computer is a [surprisingly difficult technical challenge][truly-random], so you may see these results referred to as "pseudorandom".
+
+In practice, a well-designed library like the [`random`][random] module in the Python standard library is fast, flexible, and gives results that are amply good enough for most applications in modelling, simulation and games.
+
+The rest of this page will list a few of the most common functions in `random`.
+We encourage you to explore the full `random` documentation, as there are many more options than what we cover here.
+
+
+
+~~~~exercism/caution
+
+The `random` module should __NOT__ be used for security and cryptographic applications.
+
+Instead, Python provides the [`secrets`][secrets] module.
+This is specially optimized for cryptographic security.
+Some of the prior issues and reasons for creating the secrets module can be found in [PEP 506][PEP 506].
+
+[secrets]: https://docs.python.org/3.11/library/secrets.html#module-secrets
+[PEP 506]: https://peps.python.org/pep-0506/
+~~~~
+
+
+
+## Importing
+
+Before you can utilize the tools in the `random` module, you must first import it:
+
+```python
+>>> import random
+
+# Choose random integer from a range
+>>> random.randrange(1000)
+360
+
+>>> random.randrange(-1, 500)
+228
+
+>>> random.randrange(-10, 11, 2)
+-8
+
+# Choose random integer between two values (inclusive)
+>>> random.randint(5, 25)
+22
+
+```
+
+To avoid typing the name of the module, you can import specific functions by name:
+
+```python
+>>> from random import choice, choices
+
+# Using choice() to pick Heads or Tails 10 times
+>>> tosses = []
+>>> for side in range(10):
+>>> tosses.append(choice(['H', 'T']))
+
+>>> print(tosses)
+['H', 'H', 'H', 'H', 'H', 'H', 'H', 'T', 'T', 'H']
+
+
+# Using choices() to pick Heads or Tails 8 times
+>>> picks = []
+>>> picks.extend(choices(['H', 'T'], k=8))
+>>> print(picks)
+['T', 'H', 'H', 'T', 'H', 'H', 'T', 'T']
+```
+
+
+## Creating random integers
+
+The `randrange()` function has three forms, to select a random value from `range(start, stop, step)`:
+ 1. `randrange(stop)` gives an integer `n` such that `0 <= n < stop`
+ 2. `randrange(start, stop)` gives an integer `n` such that `start <= n < stop`
+ 3. `randrange(start, stop, step)` gives an integer `n` such that `start <= n < stop` and `n` is in the sequence `start, start + step, start + 2*step...`
+
+For the common case where `step == 1`, the `randint(a, b)` function may be more convenient and readable.
+Possible results from `randint()` _include_ the upper bound, so `randint(a, b)` is the same as using `randrange(a, b+1)`:
+
+```python
+>>> import random
+
+# Select one number at random from the range 0, 499
+>>> random.randrange(500)
+219
+
+# Select 10 numbers at random between 0 and 9 two steps apart.
+>>> numbers = []
+>>> for integer in range(10):
+>>> numbers.append(random.randrange(0, 10, 2))
+>>> print(numbers)
+[2, 8, 4, 0, 4, 2, 6, 6, 8, 8]
+
+# roll a die
+>>> random.randint(1, 6)
+4
+```
+
+
+
+## Working with sequences
+
+The functions in this section assume that you are starting from some [sequence][sequence-types], or other container.
+
+
+This will typically be a `list`, or with some limitations a `tuple` or a `set` (_a `tuple` is immutable, and `set` is unordered_).
+
+
+
+### `choice()` and `choices()`
+
+The `choice()` function will return one entry chosen at random from a given sequence.
+At its simplest, this might be a coin-flip:
+
+```python
+# This will pick one of the two values in the list at random 5 separate times
+>>> [random.choice(['H', 'T']) for _ in range(5)]
+['T', 'H', 'H', 'T', 'H']
+
+We could accomplish essentially the same thing using the `choices()` function, supplying a keyword argument with the list length:
+
+
+```python
+>>> random.choices(['H', 'T'], k=5)
+['T', 'H', 'T', 'H', 'H']
+```
+
+
+In the examples above, we assumed a fair coin with equal probability of heads or tails, but weights can also be specified.
+For example, if a bag contains 10 red balls and 15 green balls, and we would like to pull one out at random:
+
+```python
+>>> random.choices(['red', 'green'], [10, 15])
+['red']
+```
+
+
+
+### `sample()`
+
+The `choices()` example above assumes what statisticians call ["sampling with replacement"][sampling-with-replacement].
+Each pick or choice has **no effect** on the probability of future choices, and the distribution of potential choices remains the same from pick to pick.
+
+
+In the example with red and green balls: after each choice, we _return_ the ball to the bag and shake well before the next pick.
+This is in contrast to a situation where we pull out a red ball and _it stays out_.
+Not returning the ball means there are now fewer red balls in the bag, and the next choice is now _less likely_ to be red.
+
+To simulate this "sampling without replacement", the random module provides the `sample()` function.
+The syntax of `sample()` is similar to `choices()`, except it adds a `counts` keyword parameter:
+
+
+```python
+>>> random.sample(['red', 'green'], counts=[10, 15], k=10)
+['green', 'green', 'green', 'green', 'green', 'red', 'red', 'red', 'red', 'green']
+```
+
+Samples are returned in the order they were chosen.
+
+
+
+### `shuffle()`
+
+Both `choices()` and `sample()` return new lists when `k > 1`.
+In contrast, `shuffle()` randomizes the order of a list _**in place**_, and the original ordering is lost:
+
+```python
+>>> my_list = [1, 2, 3, 4, 5]
+>>> random.shuffle(my_list)
+>>> my_list
+[4, 1, 5, 2, 3]
+```
+
+
+## Working with Distributions
+
+Until now, we have concentrated on cases where all outcomes are equally likely.
+For example, `random.randrange(100)` is equally likely to give any integer from 0 to 99.
+
+Many real-world situations are far less simple than this.
+As a result, statisticians have created a wide variety of [`distributions`][probability-distribution] to describe "real world" results mathematically.
+
+
+
+### Uniform distributions
+
+For integers, `randrange()` and `randint()` are used when all probabilities are equal.
+This is called a [`uniform`][uniform-distribution] distribution.
+
+
+There are floating-point equivalents to `randrange()` and `randint()`.
+
+__`random()`__ gives a `float` value `x` such that `0.0 <= x < 1.0`.
+
+__`uniform(a, b)`__ gives `x` such that `a <= x <= b`.
+
+```python
+>>> [round(random.random(), 3) for _ in range(5)]
+[0.876, 0.084, 0.483, 0.22, 0.863]
+
+>>> [round(random.uniform(2, 5), 3) for _ in range(5)]
+[2.798, 2.539, 3.779, 3.363, 4.33]
+```
+
+
+
+### Gaussian distribution
+
+Also called the "normal" distribution or the "bell-shaped" curve, this is a very common way to describe imprecision in measured values.
+
+For example, suppose the factory where you work has just bought 10,000 bolts which should be identical.
+You want to set up the factory robot to handle them, so you weigh a sample of 100 and find that they have an average (or `mean`) weight of 4.731g.
+This is extremely unlikely to mean that they all weigh exactly 4.731g.
+Perhaps you find that values range from 4.627 to 4.794g but cluster around 4.731g.
+
+This is the [`Gaussian distribution`][gaussian-distribution], for which probabilities peak at the mean and tails off symmetrically on both sides (hence "bell-shaped").
+To simulate this in software, we need some way to specify the width of the curve (_typically, expensive bolts will cluster more tightly around the mean than cheap bolts!_).
+
+By convention, this is done with the [`standard deviation`][standard-deviation]: small values for a sharp, narrow curve, large for a low, broad curve.
+Mathematicians love Greek letters, so we use `ΞΌ` ('mu') to represent the mean and `Ο` ('sigma') to represent the standard deviation.
+Thus, if you read that "95% of values are within 2Ο of ΞΌ" or "the Higgs boson has been detected with 5-sigma confidence", such comments relate to the standard deviation.
+
+```python
+>>> mu = 4.731
+>>> sigma = 0.316
+>>> [round(random.gauss(mu, sigma), 3) for _ in range(5)]
+[4.72, 4.957, 4.64, 4.556, 4.968]
+```
+
+[gaussian-distribution]: https://simple.wikipedia.org/wiki/Normal_distribution
+[probability-distribution]: https://simple.wikipedia.org/wiki/Probability_distribution
+[random]: https://docs.python.org/3/library/random.html
+[sampling-with-replacement]: https://www.youtube.com/watch?v=LnGFL_A6A6A
+[sequence-types]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
+[standard-deviation]: https://simple.wikipedia.org/wiki/Standard_deviation
+[truly-random]: https://www.malwarebytes.com/blog/news/2013/09/in-computers-are-random-numbers-really-random
+[uniform-distribution]: https://www.investopedia.com/terms/u/uniform-distribution.asp#:~:text=In%20statistics%2C%20uniform%20distribution%20refers,a%20spade%20is%20equally%20likely.
diff --git a/concepts/random/introduction.md b/concepts/random/introduction.md
new file mode 100644
index 00000000000..6bf880be57f
--- /dev/null
+++ b/concepts/random/introduction.md
@@ -0,0 +1,108 @@
+# Introduction
+
+Many programs need (apparently) random values to simulate real-world events.
+
+Common, familiar examples include:
+- A coin toss: a random value from `('H', 'T')`.
+- The roll of a die: a random integer from 1 to 6.
+- Shuffling a deck of cards: a random ordering of a card list.
+- The creation of trees and bushes in a 3-D graphics simulation.
+
+Generating _truly_ random values with a computer is a [surprisingly difficult technical challenge][truly-random], so you may see these results referred to as "pseudorandom".
+
+In practice, a well-designed library like the [`random`][random] module in the Python standard library is fast, flexible, and gives results that are amply good enough for most applications in modelling, simulation and games.
+
+For this brief introduction, we show the four most commonly used functions from the module.
+We encourage you to explore the full [`random`][random] documentation, as there are many tools and options.
+
+
+~~~~exercism/caution
+
+The `random` module should __NOT__ be used for security and cryptographic applications!!
+
+Instead, Python provides the [`secrets`][secrets] module.
+This is specially optimized for cryptographic security.
+Some of the prior issues and reasons for creating the secrets module can be found in [PEP 506][PEP 506].
+
+[secrets]: https://docs.python.org/3.11/library/secrets.html#module-secrets
+[PEP 506]: https://peps.python.org/pep-0506/
+~~~~
+
+
+Before you can utilize the tools in the `random` module, you must first import it:
+
+```python
+>>> import random
+
+# Choose random integer from a range
+>>> random.randrange(1000)
+360
+
+>>> random.randrange(-1, 500)
+228
+
+>>> random.randrange(-10, 11, 2)
+-8
+
+# Choose random integer between two values (inclusive)
+>>> random.randint(5, 25)
+22
+
+```
+
+To avoid typing the name of the module, you can import specific functions by name:
+
+```python
+>>> from random import choice, choices
+
+# Using choice() to pick Heads or Tails 10 times
+>>> tosses = []
+>>> for side in range(10):
+>>> tosses.append(choice(['H', 'T']))
+
+>>> print(tosses)
+['H', 'H', 'H', 'H', 'H', 'H', 'H', 'T', 'T', 'H']
+
+
+# Using choices() to pick Heads or Tails 8 times
+>>> picks = []
+>>> picks.extend(choices(['H', 'T'], k=8))
+>>> print(picks)
+['T', 'H', 'H', 'T', 'H', 'H', 'T', 'T']
+```
+
+
+
+## `randrange()` and `randint()`
+
+Shown in the first example above, the `randrange()` function has three forms:
+
+1. `randrange(stop)` gives an integer `n` such that `0 <= n < stop`
+2. `randrange(start, stop)` gives an integer `n` such that `start <= n < stop`
+3. `randrange(start, stop, step)` gives an integer `n` such that `start <= n < stop`
+ and `n` is in the sequence `start, start + step, start + 2*step...`
+
+For the most common case where `step == 1`, `randint(a, b)` may be more convenient and readable.
+Possible results from `randint()` _include_ the upper bound, so `randint(a, b)` is the same as using `randrange(a, b+1)`.
+
+
+
+## `choice()` and `choices()`
+
+These two functions assume that you are starting from some [sequence][sequence-types], or other container.
+This will typically be a `list`, or with some limitations a `tuple` or a `set` (_a `tuple` is immutable, and `set` is unordered_).
+
+The `choice()` function will return one entry chosen at random from a given sequence, and `choices()` will return `k` number of entries chosen at random from a given sequence.
+In the examples shown above, we assumed a fair coin with equal probability of heads or tails, but weights can also be specified.
+
+For example, if a bag contains 10 red balls and 15 green balls, and we would like to pull one out at random:
+
+
+```python
+>>> random.choices(['red', 'green'], [10, 15])
+['red']
+```
+
+[random]: https://docs.python.org/3/library/random.html
+[sequence-types]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
+[truly-random]: https://www.malwarebytes.com/blog/news/2013/09/in-computers-are-random-numbers-really-random
diff --git a/concepts/random/links.json b/concepts/random/links.json
new file mode 100644
index 00000000000..22f60dbfb46
--- /dev/null
+++ b/concepts/random/links.json
@@ -0,0 +1,14 @@
+[
+ {
+ "url": "https://docs.python.org/3/library/random.html/",
+ "description": "Official documentation for the random module."
+ },
+ {
+ "url": "https://engineering.mit.edu/engage/ask-an-engineer/can-a-computer-generate-a-truly-random-number/",
+ "description": "MIT Engineering: Can a computer generate a truly random number?"
+ },
+ {
+ "url": "https://www.malwarebytes.com/blog/news/2013/09/in-computers-are-random-numbers-really-random",
+ "description": "Are Random Numbers Really Random?"
+ }
+]
diff --git a/concepts/recursion/about.md b/concepts/recursion/about.md
index 1c66756caf2..1cf24388269 100644
--- a/concepts/recursion/about.md
+++ b/concepts/recursion/about.md
@@ -6,7 +6,7 @@ Recursion can be viewed as another way to loop/iterate.
And like looping, a Boolean expression or `True/False` test is used to know when to stop the recursive execution.
_Unlike_ looping, recursion without termination in Python cannot not run infinitely.
Values used in each function call are placed in their own frame on the Python interpreter stack.
-If the total amount of function calls takes up more space than the stack has room for, it will result in an error.
+If the total number of function calls takes up more space than the stack has room for, it will result in an error.
## Looping vs Recursive Implementation
diff --git a/concepts/recursion/introduction.md b/concepts/recursion/introduction.md
index aebfd6596be..fb7e1970705 100644
--- a/concepts/recursion/introduction.md
+++ b/concepts/recursion/introduction.md
@@ -5,7 +5,7 @@ It can be viewed as another way to loop/iterate.
Like looping, a Boolean expression or `True/False` test is used to know when to stop the recursive execution.
_Unlike_ looping, recursion without termination in Python cannot not run infinitely.
Values used in each function call are placed in their own frame on the Python interpreter stack.
-If the total amount of function calls takes up more space than the stack has room for, it will result in an error.
+If the total number of function calls takes up more space than the stack has room for, it will result in an error.
```python
def print_increment(step, max_value):
diff --git a/concepts/secrets/.meta/config.json b/concepts/secrets/.meta/config.json
new file mode 100644
index 00000000000..152aa0eb3ba
--- /dev/null
+++ b/concepts/secrets/.meta/config.json
@@ -0,0 +1,5 @@
+{
+ "blurb": "The secrets module is a cryptographically-secure alternative to the random module, intended for security-critical uses.",
+ "authors": ["BethanyG", "colinleach"],
+ "contributors": []
+}
diff --git a/concepts/secrets/about.md b/concepts/secrets/about.md
new file mode 100644
index 00000000000..5987ab37e91
--- /dev/null
+++ b/concepts/secrets/about.md
@@ -0,0 +1,51 @@
+# About
+
+A previous concept discussed the [concept:python/random]() module, which produces [pseudo-random numbers][pseudo-random-numbers] and pseudo-random list orderings.
+
+The [`secrets`][secrets] module overlaps with `random` in some of its functionality, but the two modules are designed with very different priorities.
+
+- `random` is optimized for high performance in modelling and simulation, with "good enough" pseudo-random number generation.
+- `secrets` is designed to be crytographically secure for applications such as password hashing, security token generation, and account authentication.
+
+
+Further details on why the addition of the `secrets` module proved necessary are given in [PEP 506][PEP506].
+
+The `secrets` is relatively small and straightforward, with methods for generating random integers, bits, bytes or tokens, or a random entry from a given sequence.
+
+To use `scerets`, you mush first `import` it:
+
+
+```python
+>>> import secrets
+
+#Returns n, where 0 <= n < 1000.
+>>> secrets.randbelow(1000)
+577
+
+#32-bit integers.
+>>> secrets.randbits(32)
+3028709440
+
+>>> bin(secrets.randbits(32))
+'0b11111000101100101111110011110100'
+
+#Pick at random from a sequence.
+>>> secrets.choice(['my', 'secret', 'thing'])
+'thing'
+
+#Generate a token made up of random hexadecimal digits.
+>>> secrets.token_hex()
+'f683d093ea9aa1f2607497c837cf11d7afaefa903c5805f94b64f068e2b9e621'
+
+#Generate a URL-safe token of random alphanumeric characters.
+>>> secrets.token_urlsafe(16)
+'gkSUKRdiPDHqmImPi2HMnw'
+```
+
+
+If you are writing security-sensitive applications, you will certainly want to read the [full documentation][secrets], which gives further advice and examples.
+
+
+[PEP506]: https://peps.python.org/pep-0506/
+[pseudo-random-numbers]: https://www.khanacademy.org/computing/computer-science/cryptography/crypt/v/random-vs-pseudorandom-number-generators
+[secrets]: https://docs.python.org/3/library/secrets.html
diff --git a/concepts/secrets/introduction.md b/concepts/secrets/introduction.md
new file mode 100644
index 00000000000..04308ed0a2a
--- /dev/null
+++ b/concepts/secrets/introduction.md
@@ -0,0 +1,17 @@
+# Introduction
+
+A previous concept discussed the [concept:python/random]() module, which produces [pseudo-random numbers][pseudo-random-numbers] and pseudo-random list orderings.
+
+The [`secrets`][secrets] module overlaps with `random` in some of its functionality, but the two modules are designed with very different priorities.
+
+- `random` is optimized for high performance in modelling and simulation, with "good enough" pseudo-random number generation.
+- `secrets` is designed to be crytographically secure for applications such as password hashing, security token generation, and account authentication.
+
+
+Further details on why the addition of the `secrets` module proved necessary are given in [PEP 506][PEP506].
+
+If you are writing security-sensitive applications, you will certainly want to read the [full documentation][secrets], which gives further advice and examples.
+
+[PEP506]: https://peps.python.org/pep-0506/
+[pseudo-random-numbers]: https://www.khanacademy.org/computing/computer-science/cryptography/crypt/v/random-vs-pseudorandom-number-generators
+[secrets]: https://docs.python.org/3/library/secrets.html
diff --git a/concepts/secrets/links.json b/concepts/secrets/links.json
new file mode 100644
index 00000000000..ccf44e6fe59
--- /dev/null
+++ b/concepts/secrets/links.json
@@ -0,0 +1,18 @@
+[
+ {
+ "url": "https://docs.python.org/3/library/secrets.html/",
+ "description": "The secrets module."
+ },
+ {
+ "url": "https://peps.python.org/pep-0506/",
+ "description": "PEP 506, giving reasons why the secrets module is necessary."
+ },
+ {
+ "url": "https://en.wikipedia.org/wiki/Pseudorandomness",
+ "description": "Wikipedia: Pseudorandomness."
+ },
+ {
+ "url": "https://www.khanacademy.org/computing/computer-science/cryptography/crypt/v/random-vs-pseudorandom-number-generators",
+ "description": "Khan Academy: Pseudorandom Number Generators."
+ }
+]
diff --git a/concepts/sets/about.md b/concepts/sets/about.md
index 5f272402eef..2c011c14471 100644
--- a/concepts/sets/about.md
+++ b/concepts/sets/about.md
@@ -1,119 +1,153 @@
# Sets
-A [`set`][type-set] is a mutable and _unordered_ collection of _hashable_ objects.
-Items within a `set` are distinct and duplicate members are not allowed.
-Like most collections, `sets` can hold any (or multiple) data type(s) -- as long as those types can be [hashed][hashable].
-Sets also come in an _immutable_ [`frozenset`][type-frozenset] flavor.
+A [`set`][type-set] is a _mutable_ and _unordered_ collection of [_hashable_][hashable] objects.
+Set members must be distinct β duplicate items are not allowed.
+They can hold multiple different data types and even nested structures like a `tuple` of `tuples` β as long as all elements can be _hashed_.
+Sets also come in an immutable [`frozensets`][type-frozenset] flavor.
-Like other collection types, `sets` support membership testing through `in`, length calculation through `len()`, shallow copies through `copy()`, and iteration via `for item in `.
-_Unlike_ sequence types (_`string`, `list` & `tuple`_), `sets` are **neither ordered nor indexed**, and _do not support_ slicing, sorting, or other sequence-type behaviors.
+Sets are most commonly used to quickly remove duplicates from other data structures or item groupings.
+They are also used for efficient comparisons when sequencing and duplicate tracking are not needed.
-`sets` are most commonly used to quickly dedupe groups of items.
-They're also used for fast membership testing, finding supersets & subsets of items, and performing "set math" (_calculating union, intersection, difference & symmetric difference between groups of items._).
+Like other collection types (_dictionaries, lists, tuples_), `sets` support:
+- Iteration via `for item in `
+- Membership checking via `in` and `not in`,
+- Length calculation through `len()`, and
+- Shallow copies through `copy()`
-Checking membership in a `set` has only O(1) time complexity versus checking for membership in a `list` or `string`, which has worst-case O(n) time complexity.
-Operations such as `.union()`, `.intersection()`, or `.difference()` have an average O(n) time complexity.
+`sets` do not support:
+- Indexing of any kind
+- Ordering via sorting or insertion
+- Slicing
+- Concatenation via `+`
-## Construction
-A `set` can be declared as a _set literal_ with curly `{}` brackets and commas between elements.
+Checking membership in a `set` has constant time complexity (on average) versus checking membership in a `list` or `string`, where the time complexity grows as the length of the data increases.
+Methods such as `.union()`, `.intersection()`, or `.difference()` also have constant time complexity (on average).
+
+
+## Set Construction
+
+While sets can be created in many different ways, the most straightforward construction methods are declaring a _set literal_, using the `set` class constructor (`set()`), and using a _set comprehension_.
+
+### Set Literals
+
+A `set` can be directly entered as a _set literal_ with curly `{}` brackets and commas between elements.
Duplicates are silently omitted:
+
```python
->>> one_element = {'π'}
->>> one_element
-{'π'}
+>>> one_element = {'β'}
+{'β'}
->>> multiple_elements = {'Hello!', 'Β‘Hola!', 'ΠΡΠΈΠ²Π΅Ρ!', 'γγγ«γ‘γ―οΌ'}
->>> multiple_elements
-{'γγγ«γ‘γ―οΌ', 'Β‘Hola!', 'Hello!', 'ΠΡΠΈΠ²Π΅Ρ!'}
+>>> multiple_elements = {'β', 'π»', 'πΉ', 'π'}
+{'β', 'π»', 'πΉ', 'π'}
->>> multiple_duplicates = {'Hello!', 'Β‘Hola!', 'ΠΡΠΈΠ²Π΅Ρ!', 'γγγ«γ‘γ―οΌ', 'Β‘Hola!', 'ΠΡΠΈΠ²Π΅Ρ!'}
->>> multiple_duplicates
-{'γγγ«γ‘γ―οΌ', 'Β‘Hola!', 'Hello!', 'ΠΡΠΈΠ²Π΅Ρ!'}
+>>> multiple_duplicates = {'Hello!', 'Hello!', 'Hello!',
+ 'Β‘Hola!','ΠΡΠΈΠ²ΡΡ!', 'γγγ«γ‘γ―οΌ',
+ 'Β‘Hola!','ΠΡΠΈΠ²ΡΡ!', 'γγγ«γ‘γ―οΌ'}
+{'γγγ«γ‘γ―οΌ', 'Β‘Hola!', 'Hello!', 'ΠΡΠΈΠ²ΡΡ!'}
```
-Set literals use the same curly braces as `dict` literals, so the `set()` constructor must be used to declare an empty `set`.
+Set literals use the same curly braces as `dict` literals, which means you need to use `set()` to create an empty `set`.
+
+### The Set Constructor
+
+`set()` (_the constructor for the `set` class_) can be used with any `iterable` passed as an argument.
+Elements of the `iterable` are cycled through and added to the `set` individually.
+Element order is not preserved and duplicates are silently omitted:
-The `set()` constructor can also be used with any _iterable_ passed as an argument.
-Elements are cycled through by the constructor and added to the `set` individually.
-Order is not preserved and duplicates are silently omitted:
```python
+# To create an empty set, the constructor must be used.
>>> no_elements = set()
->>> no_elements
set()
-# The tuple is unpacked and each distinct element is added. Duplicates are removed.
->>> multiple_elements_from_tuple = set(("Parrot", "Bird", 334782, "Bird", "Parrot"))
->>> multiple_elements_from_tuple
+# The tuple is unpacked & each element is added.
+# Duplicates are removed.
+>>> elements_from_tuple = set(("Parrot", "Bird",
+ 334782, "Bird", "Parrot"))
{334782, 'Bird', 'Parrot'}
-# The list is unpacked and each distinct element is added.
->>> multiple_elements_from_list = set([2, 3, 2, 3, 3, 3, 5, 7, 11, 7, 11, 13, 13])
->>> multiple_elements_from_set
+# The list is unpacked & each element is added.
+# Duplicates are removed.
+>>> elements_from_list = set([2, 3, 2, 3, 3, 3, 5,
+ 7, 11, 7, 11, 13, 13])
{2, 3, 5, 7, 11, 13}
```
-Results when using a set constructor with a string or dictionary may be surprising:
+### Set Comprehensions
+
+Like `lists` and `dicts`, sets can be created via _comprehension_:
```python
-# String elements (Unicode code points) are iterated through and added *individually*.
->>> multiple_elements_string = set("Timbuktu")
->>> multiple_elements_string
+# First, a list with duplicates
+>>> numbers = [1,2,3,4,5,6,6,5,4,8,9,9,9,2,3,12,18]
+
+# This set comprehension squares the numbers divisible by 3
+# Duplicates are removed.
+>>> calculated = {item**2 for item in numbers if item % 3 == 0}
+{9, 36, 81, 144, 324}
+```
+
+### Gotchas when Creating Sets
+
+Due to its "unpacking" behavior, using the `set` constructor with a string might be surprising:
+
+```python
+# String elements (Unicode code points) are
+# iterated through and added *individually*.
+>>> elements_string = set("Timbuktu")
{'T', 'b', 'i', 'k', 'm', 't', 'u'}
-# Unicode separators and positioning code points are also added *individually*.
+# Unicode separators and positioning code points
+# are also added *individually*.
>>> multiple_code_points_string = set('ΰ€
ΰ€ΰ₯ΰ€―ΰ€Ύΰ€Έ')
->>> multiple_code_points_string
{'ΰ€
', 'ΰ€', 'ΰ€―', 'ΰ€Έ', 'ΰ€Ύ', 'ΰ₯'}
-
-# The iteration default for dictionaries is over the keys.
->>> source_data = {"fish": "gold", "monkey": "brown", "duck" : "white", "crow": "black"}
->>> set(source_data)
-{'crow', 'duck', 'fish', 'monkey'}
```
-Sets can hold heterogeneous datatypes, but all `set` elements must be _hashable_:
+Remember: sets can hold different datatypes and _nested_ datatypes, but all `set` elements must be _hashable_:
```python
-
->>> lists_as_elements = {['π
','π€£'], ['π','π','π'], ['π', 'π€ͺ', 'π']}
+# Attempting to use a list for a set member throws a TypeError
+>>> lists_as_elements = {['π','π¦'],
+ ['βοΈ','βοΈ','π'],
+ ['β΅οΈ', 'π²', 'π']}
Traceback (most recent call last):
-
- File "", line 1, in
- lists_as_elements = {['π
','π€£'], ['π','π','π'], ['π', 'π€ͺ', 'π']}
-
+ File "", line 1, in
TypeError: unhashable type: 'list'
-# standard sets are mutable, so they cannot be hashed.
->>> sets_as_elements = {{'π
','π€£'}, {'π','π','π'}, {'π', 'π€ͺ', 'π'}}
-Traceback (most recent call last):
- File "", line 1, in
- sets_as_elements = {{'π
','π€£'}, {'π','π','π'}, {'π', 'π€ͺ', 'π'}}
+# Standard sets are mutable, so they cannot be hashed.
+>>> sets_as_elements = {{'π','π¦'},
+ {'βοΈ','βοΈ','π'},
+ {'β΅οΈ', 'π²', 'π'}}
+Traceback (most recent call last):
+ File "", line 1, in
TypeError: unhashable type: 'set'
```
-Therefore, to create a `set` of `sets`, the contained sets must be of type `frozenset()`
+However, a `set` of `sets` can be created via type `frozenset()`:
```python
-# frozensets don't have a literal form
->>> set_1 = frozenset({'π', 'π', 'π€ͺ'})
->>> set_2 = frozenset({'π
', 'π€£'})
->>> set_3 = frozenset({'π', 'π', 'π'})
+# Frozensets don't have a literal form.
+>>> set_1 = frozenset({'π','π¦'})
+>>> set_2 = frozenset({'βοΈ','βοΈ','π'})
+>>> set_3 = frozenset({'β΅οΈ', 'π²', 'π'})
>>> frozen_sets_as_elements = {set_1, set_2, set_3}
>>> frozen_sets_as_elements
-{frozenset({'π', 'π', 'π€ͺ'}), frozenset({'π
', 'π€£'}), frozenset({'π', 'π', 'π'})}
+{frozenset({'β΅οΈ', 'π', 'π²'}),
+ frozenset({'π', 'π¦'}),
+ frozenset({'βοΈ', 'βοΈ', 'π'})}
```
-## Working with Sets
-Elements can be added/removed using `.add(- )` / `.remove(
- )`.
-`remove(
- )` will raise a `KeyError` if the item is not present in the `set`.
+## Adding and Removing Set Members
+
+Elements can be added or removed from a `set` using the methods `.add(
- )` and `.remove(
- )`.
+The `.remove(
- )` method will raise a `KeyError` if the item is not present in the `set`:
```python
>>> creatures = {'crow', 'duck', 'fish', 'monkey', 'elephant'}
@@ -122,100 +156,139 @@ Elements can be added/removed using `.add(
- )` / `.remove(
- )`
>>> creatures
{'beaver', 'crow', 'elephant', 'fish', 'monkey'}
-# Trying to remove an item that is not present will raise a KeyError
+# Trying to remove an item that is not present raises a KeyError
>>> creatures.remove('bear')
Traceback (most recent call last):
-
- File "", line 1, in
- creatures.remove('bear')
-
-KeyError: 'bear'
+ File "", line 1, in
+ KeyError: 'bear'
```
-`.discard(
- )` will also remove an item from the `set`, but will **not** raise a `KeyError` if the item is not present.
-`.clear()` will remove all items.
-`.pop()` will remove and _return_ an **arbitrary** item and raises a `KeyError` if the `set` is empty.
+### Additional Strategies for Removing Set Members
-## Set Methods
+- `.discard(
- )` will remove an item from the `set`, but will **not** raise a `KeyError` if the item is not present.
+- `.clear()` will remove all items from the set.
+- `.pop()` will remove and _return_ an **arbitrary** item, and raises a `KeyError` if the `set` is empty.
-Sets implement methods that generally mimic [mathematical set operations][mathematical-sets].
-Most (_though not all_) of these methods can be performed using either operator(s) or method call(s).
-Using operators requires that both inputs be `sets` or `frozensets`, while methods will generally take any iterable as an argument.
-### Fast Membership Testing Between Groups
+## Set Operations
-The `.isdisjoint()` method is used to test if a `set` has **no elements in common** with another set or iterable.
-It will accept any `iterable` or `set` as an arugment, returning `True` if they are **disjoint**, `False` otherwise.
-Note that for `dcts`, the iteration default is over`.keys()`.
+Sets have methods that generally mimic [mathematical set operations][mathematical-sets].
+Most (_not all_) of these methods have an [operator][operator] equivalent.
+Methods generally take any `iterable` as an argument, while operators require that both sides of the operation are `sets` or `frozensets`.
-```python
->>> mammals = {'squirrel','dog','cat','cow', 'tiger', 'elephant'}
->>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
-# Dictionary of animal names with colors
->>> animals = {'chicken': 'white','sparrow': 'grey','eagle': 'brown and white',
- 'albatross': 'grey and white','crow': 'black','elephant': 'grey',
- 'dog': 'rust','cow': 'black and white','tiger': 'orange and black',
- 'cat': 'grey','squirrel': 'black'}
+### Membership Testing Between Sets
-# List of additonal animals
->>> additional_animals = ['pangolin', 'panda', 'parrot', 'lemur', 'tiger', 'pangolin']
-...
+The `.isdisjoint()` method is used to test if a `sets` elements have any overlap with the elements of another.
+The method will accept any `iterable` or `set` as an argument.
+It will return `True` if the two sets have **no elements in common**, `False` if elements are **shared**.
->>> mammals.isdisjoint(birds)
+```python
+# Both mammals and additional_animals are lists.
+>>> mammals = ['squirrel','dog','cat','cow', 'tiger', 'elephant']
+>>> additional_animals = ['pangolin', 'panda', 'parrot',
+ 'lemur', 'tiger', 'pangolin']
+
+# Animals is a dict.
+>>> animals = {'chicken': 'white',
+ 'sparrow': 'grey',
+ 'eagle': 'brown and white',
+ 'albatross': 'grey and white',
+ 'crow': 'black',
+ 'elephant': 'grey',
+ 'dog': 'rust',
+ 'cow': 'black and white',
+ 'tiger': 'orange and black',
+ 'cat': 'grey',
+ 'squirrel': 'black'}
+
+# Birds is a set.
+>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
+
+# Mammals and birds don't share any elements.
+>>> birds.isdisjoint(mammals)
True
->>> mammals.isdisjoint(animals)
-False
-
+# There are also no shared elements between
+# additional_animals and birds.
>>> birds.isdisjoint(additional_animals)
True
->>> set(additional_animals).isdisjoint(animals)
+# Animals and mammals have shared elements.
+# **Note** The first object needs to be a set or converted to a set
+# since .isdisjoint() is a set method.
+>>> set(animals).isdisjoint(mammals)
False
```
-`.issubset()` | ` <= ` are used to check if every element in `` is also in ``.
-`.issuperset()` | ` >= ` are used to check the inverse -- if every element in `` is also in ``.
+### Checking for Subsets and Supersets
-```python
->>> animals = {'chicken': 'white','sparrow': 'grey','eagle': 'brown and white',
- 'albatross': 'grey and white','crow': 'black','elephant': 'grey',
- 'dog': 'rust','cow': 'black and white','tiger': 'organge and black',
- 'cat': 'grey','squirrel': 'black'}
+`.issubset()` is used to check if every element in `` is also in ``.
+The operator form is ` <= `:
->>> mammals = {'squirrel','dog','cat','cow', 'tiger', 'elephant'}
->>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
+```python
+# Set methods will take any iterable as an argument.
+# All members of birds are also members of animals.
+>>> birds.issubset(animals)
+True
-# Methods will take any iterable as an argument
->>> mammals.issubset(animal_colors)
+# All members of mammals also appear in animals.
+# **Note** The first object needs to be a set or converted to a set
+# since .issubset() is a set method.
+>>> set(mammals).issubset(animals)
True
+# Both objects need to be sets to use a set operator
+>>> birds <= set(mammals)
+False
-# A set is always a loose subset of itself
->>> animals <= animals
+# A set is always a loose subset of itself.
+>>> set(additional_animals) <= set(additional_animals)
True
+```
+
+`.issuperset()` is the inverse of `.issubset()`.
+It is used to check if every element in `` is also in ``.
+The operator form is ` >= `:
->>> birds <= animals
+
+```python
+# All members of mammals also appear in animals.
+# **Note** The first object needs to be a set or converted to a set
+# since .issuperset() is a set method.
+>>> set(animals).issuperset(mammals)
True
->>> birds <= mammals
+# All members of animals do not show up as members of birds.
+>>> birds.issuperset(animals)
False
+
+# Both objects need to be sets to use a set operator
+>>> birds >= set(mammals)
+False
+
+# A set is always a loose superset of itself.
+>>> set(animals) >= set(animals)
+True
```
-` < ` and ` > ` are used to test for _proper subsets_:
-(`` <= ``) AND (`` != ``) for the `<` operator; (`` >= ``) AND (`` != ``) for the `>` operator.
-They have no method equivelent.
+### 'Proper' Subsets and Supersets
+
+` < ` and ` > ` are used to test for _proper subsets_.
+A `set` is a proper subset if (`` <= ``) **AND** (`` != ``) for the `<` operator.
+
+A `set is a proper superset if `(`` >= ``) **AND** (`` != ``) for the `>` operator.
+These operators have no method equivalent:
```python
->>> animal_names = {'albatross','cat','chicken','cow','crow','dog',
- 'eagle','elephant','sparrow','squirrel','tiger'}
+>>> animal_names = {'albatross','cat','chicken','cow','crow','dog',
+ 'eagle','elephant','sparrow','squirrel','tiger'}
->>> animal_names_also = {'albatross','cat','chicken','cow','crow','dog',
- 'eagle','elephant','sparrow','squirrel','tiger'}
+>>> animals_also = {'albatross','cat','chicken','cow','crow','dog',
+ 'eagle','elephant','sparrow','squirrel','tiger'}
->>> mammals = {'squirrel','dog','cat','cow', 'tiger', 'elephant'}
->>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
+>>> mammals = {'squirrel','dog','cat','cow', 'tiger', 'elephant'}
+>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
>>> mammals < animal_names
True
@@ -223,81 +296,114 @@ True
>>> animal_names > birds
True
-# A set is never a *proper subset* of itself
->>> animal_names_also < animal_names
+# A set is not a *proper subset* if set == other set.
+>>> animals_also < animal_names
False
-
->>> animals < animals
-
+# A set is never a *proper subset* of itself
+>>> animals_also < animals_also
+False
```
-### Set Operations
+### Set Unions
-`.union(*)` and ` | | | ... | ` return a new `set` with elements from `` and all ``.
+`.union(*)` returns a new `set` with elements from `` and all ``.
+The operator form of this method is ` | | | ... | `.
```python
->>> perennial_vegetables = {'Asparagus', 'Broccoli', 'Sweet Potatoe', 'Kale'}
->>> annual_vegetables = {'Corn', 'Zucchini', 'Sweet Peas', 'Summer Squash'}
-
->>> more_perennials = ['Radicchio', 'Rhubarb', 'Spinach', 'Watercress']
+>>> perennials = {'Asparagus', 'Broccoli', 'Sweet Potato', 'Kale'}
+>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Summer Squash'}
+>>> more_perennials = ['Radicchio', 'Rhubarb',
+ 'Spinach', 'Watercress']
# Methods will take any iterable as an argument.
->>> perennial_vegetables.union(more_perennials)
-{'Asparagus','Broccoli','Kale','Radicchio','Rhubarb','Spinach','Sweet Potatoe','Watercress'}
+>>> perennials.union(more_perennials)
+{'Asparagus','Broccoli','Kale','Radicchio','Rhubarb',
+'Spinach','Sweet Potato','Watercress'}
# Operators require sets.
->>> perennial_vegetables | annual_vegetables
-{'Asparagus','Broccoli','Corn','Kale','Summer Squash','Sweet Peas','Sweet Potatoe','Zucchini'}
-
+>>> set(more_perennials) | perennials
+{'Asparagus',
+ 'Broccoli',
+ 'Kale',
+ 'Radicchio',
+ 'Rhubarb',
+ 'Spinach',
+ 'Sweet Potato',
+ 'Watercress'}
```
-`.difference(*)` and ` - - - ...` return a new `set` with elements from the original `` that are not in ``.
+### Set Differences
+
+`.difference(*)` returns a new `set` with elements from the original `` that are not in ``.
+The operator version of this method is ` - - - ...`.
```python
->>> berries_and_veggies = {'Asparagus', 'Broccoli', 'Watercress', 'Goji Berries', 'Goose Berries', 'Ramps',
- 'Walking Onions', 'Raspberries','Blueberries', 'Blackberries', 'Strawberries',
- 'Rhubarb', 'Kale', 'Artichokes', 'Currants', 'Honeyberries'}
+>>> berries_and_veggies = {'Asparagus',
+ 'Broccoli',
+ 'Watercress',
+ 'Goji Berries',
+ 'Goose Berries',
+ 'Ramps',
+ 'Walking Onions',
+ 'Blackberries',
+ 'Strawberries',
+ 'Rhubarb',
+ 'Kale',
+ 'Artichokes',
+ 'Currants'}
-# Methods will take any iterable as an argument.
>>> veggies = ('Asparagus', 'Broccoli', 'Watercress', 'Ramps',
'Walking Onions', 'Rhubarb', 'Kale', 'Artichokes')
->>> just_berries = berries_and_veggies.difference(veggies)
->>> just_berries
-{'Blackberries','Blueberries','Currants','Goji Berries',
- 'Goose Berries','Honeyberries','Raspberries','Strawberries'}
+# Methods will take any iterable as an argument.
+>>> berries = berries_and_veggies.difference(veggies)
+{'Blackberries','Currants','Goji Berries',
+ 'Goose Berries', 'Strawberries'}
->>> berries_and_veggies - just_berries
-{'Artichokes','Asparagus','Broccoli','Kale','Ramps','Rhubarb','Walking Onions','Watercress'}
+# Operators require sets.
+>>> berries_and_veggies - berries
+{'Artichokes','Asparagus','Broccoli','Kale',
+'Ramps','Rhubarb','Walking Onions','Watercress'}
```
-`.intersection(*)` and ` & & & ... ` return a new `set` with elements common to the original `set` and all ``.
+### Set Intersections
+
+`.intersection(*)` returns a new `set` with elements common to the original `set` and all `` (in other words, the `set` where everything [intersects][intersection]).
+The operator version of this method is ` & & & ... `
```python
->>> perennials = {'Annatto','Asafetida','Asparagus','Azalea','Winter Savory', 'Blackberries','Broccoli','Curry Leaf',
- 'Fennel','French Sorrel','Fuchsia','Kaffir Lime','Kale','Lavender','Mint','Oranges',
- 'Oregano','Ramps','Roses','Tarragon','Watercress','Wild Bergamot'}
+>>> perennials = {'Annatto','Asafetida','Asparagus','Azalea',
+ 'Winter Savory', 'Broccoli','Curry Leaf','Fennel',
+ 'Kaffir Lime','Kale','Lavender','Mint','Oranges',
+ 'Oregano', 'Tarragon', 'Wild Bergamot'}
->>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Marjoram', 'Summer Squash', 'Okra',
- 'Shallots', 'Basil', 'Cilantro', 'Cumin', 'Sunflower', 'Chervil', 'Summer Savory'}
+>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Marjoram',
+ 'Summer Squash', 'Okra','Shallots', 'Basil',
+ 'Cilantro', 'Cumin', 'Sunflower', 'Chervil',
+ 'Summer Savory'}
->>> herbs = ['Annatto','Asafetida','Basil','Chervil','Cilantro','Curry Leaf','Fennel','Kaffir Lime',
- 'Lavender','Marjoram','Mint','Oregano','Summer Savory' 'Tarragon','Wild Bergamot',
- 'Wild Celery','Winter Savory']
+>>> herbs = ['Annatto','Asafetida','Basil','Chervil','Cilantro',
+ 'Curry Leaf','Fennel','Kaffir Lime','Lavender',
+ 'Marjoram','Mint','Oregano','Summer Savory',
+ 'Tarragon','Wild Bergamot','Wild Celery',
+ 'Winter Savory']
# Methods will take any iterable as an argument.
>>> perennial_herbs = perennials.intersection(herbs)
->>> perennial_herbs
-{'Mint', 'Annatto', 'Winter Savory', 'Curry Leaf', 'Lavender', 'Fennel',
- 'Oregano', 'Kaffir Lime','Asafetida', 'Wild Bergamot', 'Tarragon'}
+{'Annatto', 'Asafetida', 'Curry Leaf', 'Fennel', 'Kaffir Lime',
+ 'Lavender', 'Mint', 'Oregano', 'Wild Bergamot','Winter Savory'}
+# Operators require both groups be sets.
>>> annuals & set(herbs)
{'Basil', 'Chervil', 'Marjoram', 'Cilantro'}
```
-`.symmetric_difference()` and ` ^ ` return a new `set` that contains elements that are in `` OR ``, but **not in both**.
+### Set Symmetric Differences
+
+`.symmetric_difference()` returns a new `set` that contains elements that are in `` OR ``, but **not in both**.
+The operator version of this method is ` ^ `.
```python
>>> plants_1 = {'π²','π','π΅', 'π₯','π΄', 'π₯'}
@@ -309,14 +415,71 @@ False
>>> fruit_and_flowers
{'πΈ', 'πΊ', 'π', 'π₯', 'π₯','π»' }
+
+# Operators require both groups be sets.
>>> fruit_and_flowers ^ plants_1
{'π²', 'πΈ', 'π΄', 'π΅','πΊ', 'π»'}
->>> fruit_and_flowers ^ plants_2
-{ 'π₯', 'π΄','π²', 'π΅', 'π', 'π₯'}
+>>> fruit_and_flowers ^ set(plants_2)
+{'π₯', 'π΄', 'π΅', 'π', 'π²', 'π₯'}
```
-[type-set]: https://docs.python.org/3/library/stdtypes.html#set
-[type-frozenset]: https://docs.python.org/3/library/stdtypes.html#frozenset
-[mathematical-sets]: https://en.wikipedia.org/wiki/Set_theory#Basic_concepts_and_notation
+~~~~exercism/note
+
+A symmetric difference of more than two sets will result in a `set` that includes both the elements unique to each `set` AND elements shared between more than two sets in the series (_details in the Wikipedia article on [symmetric difference][symmetric_difference]_).
+
+To obtain only items unique to each `set` in the series, intersections between all 2-set combinations need to be aggregated in a separate step, and removed:
+
+
+```python
+>>> one = {'black pepper','breadcrumbs','celeriac','chickpea flour',
+ 'flour','lemon','parsley','salt','soy sauce',
+ 'sunflower oil','water'}
+
+>>> two = {'black pepper','cornstarch','garlic','ginger',
+ 'lemon juice','lemon zest','salt','soy sauce','sugar',
+ 'tofu','vegetable oil','vegetable stock','water'}
+
+>>> three = {'black pepper','garlic','lemon juice','mixed herbs',
+ 'nutritional yeast', 'olive oil','salt','silken tofu',
+ 'smoked tofu','soy sauce','spaghetti','turmeric'}
+
+>>> four = {'barley malt','bell pepper','cashews','flour',
+ 'fresh basil','garlic','garlic powder', 'honey',
+ 'mushrooms','nutritional yeast','olive oil','oregano',
+ 'red onion', 'red pepper flakes','rosemary','salt',
+ 'sugar','tomatoes','water','yeast'}
+
+>>> intersections = (one & two | one & three | one & four |
+ two & three | two & four | three & four)
+...
+{'black pepper','flour','garlic','lemon juice','nutritional yeast',
+'olive oil','salt','soy sauce', 'sugar','water'}
+
+# The ^ operation will include some of the items in intersections,
+# which means it is not a "clean" symmetric difference - there
+# are overlapping members.
+>>> (one ^ two ^ three ^ four) & intersections
+{'black pepper', 'garlic', 'soy sauce', 'water'}
+
+# Overlapping members need to be removed in a separate step
+# when there are more than two sets that need symmetric difference.
+>>> (one ^ two ^ three ^ four) - intersections
+...
+{'barley malt','bell pepper','breadcrumbs', 'cashews','celeriac',
+ 'chickpea flour','cornstarch','fresh basil', 'garlic powder',
+ 'ginger','honey','lemon','lemon zest','mixed herbs','mushrooms',
+ 'oregano','parsley','red onion','red pepper flakes','rosemary',
+ 'silken tofu','smoked tofu','spaghetti','sunflower oil', 'tofu',
+ 'tomatoes','turmeric','vegetable oil','vegetable stock','yeast'}
+```
+
+[symmetric_difference]: https://en.wikipedia.org/wiki/Symmetric_difference
+~~~~
+
[hashable]: https://docs.python.org/3.7/glossary.html#term-hashable
+[mathematical-sets]: https://en.wikipedia.org/wiki/Set_theory#Basic_concepts_and_notation
+[operator]: https://www.computerhope.com/jargon/o/operator.htm
+[type-frozenset]: https://docs.python.org/3/library/stdtypes.html#frozenset
+[type-set]: https://docs.python.org/3/library/stdtypes.html#set
+[intersection]: https://www.mathgoodies.com/lessons/sets/intersection
diff --git a/concepts/sets/introduction.md b/concepts/sets/introduction.md
index b2eaddc8e6f..5d66b6a8ad8 100644
--- a/concepts/sets/introduction.md
+++ b/concepts/sets/introduction.md
@@ -1,14 +1,28 @@
# Sets
-A [`set`][type-set] is a mutable and _unordered_ collection of _hashable_ objects.
-Items within a `set` are unique, and no duplicates are allowed.
-Like most collections, `sets` can hold any (or multiple) data type(s) -- as long as those types can be [hashed][hashable].
-Sets also come in an _immutable_ [`frozenset`][type-frozenset] flavor.
+A [`set`][type-set] is a _mutable_ and _unordered_ collection of [_hashable_][hashable] objects.
+Set members must be distinct β duplicate items are not allowed.
+They can hold multiple different data types and even nested structures like a `tuple` of `tuples` β as long as all elements can be _hashed_.
+Sets also come in an immutable [`frozensets`][type-frozenset] flavor.
-Like other collection types, `sets` support membership testing through `in`, length calculation through `len()`, shallow copies through `copy()`, & iteration via `for item in `.
-_Unlike_ sequence types (_`string`, `list` & `tuple`_), `sets` are **neither ordered nor indexed**, and _do not support_ slicing, sorting, or other sequence-type behaviors.
+Sets are most commonly used to quickly remove duplicates from other data structures or item groupings.
+They are also used for efficient comparisons when sequencing and duplicate tracking are not needed.
-`sets` are most commonly used to quickly dedupe groups of items.
+Like other collection types (_dictionaries, lists, tuples_), `sets` support:
+- Iteration via `for item in `
+- Membership checking via `in` and `not in`,
+- Length calculation through `len()`, and
+- Shallow copies through `copy()`
+
+`sets` do not support:
+- Indexing of any kind
+- Ordering via sorting or insertion
+- Slicing
+- Concatenation via `+`
+
+
+Checking membership in a `set` has constant time complexity (on average) versus checking membership in a `list` or `string`, where the time complexity grows as the length of the data increases.
+Methods such as `.union()`, `.intersection()`, or `.difference()` also have constant time complexity (on average).
[type-set]: https://docs.python.org/3/library/stdtypes.html#set
[hashable]: https://docs.python.org/3.7/glossary.html#term-hashable
diff --git a/concepts/string-methods/.meta/config.json b/concepts/string-methods/.meta/config.json
index 4bb37fcda42..d429a738102 100644
--- a/concepts/string-methods/.meta/config.json
+++ b/concepts/string-methods/.meta/config.json
@@ -1,5 +1,5 @@
{
"blurb": "The 'str' class provides many useful 'string-methods'. They can be used for cleaning, splitting, translating, or otherwise working with any 'str' object. Because strings are immutable, any functions or methods that operate on a 'str' will return a new instance or copy of the 'str' rather than modifying the original 'str' object.",
"authors": ["kimolivia"],
- "contributors": ["valentin-p", "bethanyg"]
+ "contributors": ["valentin-p", "BethanyG"]
}
diff --git a/concepts/string-methods/about.md b/concepts/string-methods/about.md
index e01cbaf8908..308b89d9d6a 100644
--- a/concepts/string-methods/about.md
+++ b/concepts/string-methods/about.md
@@ -18,6 +18,7 @@ Some of the more commonly used `str` methods include:
Being _immutable_, a `str` object's value in memory cannot change; methods that appear to modify a string return a new copy or instance of that `str` object.
+
[`.endswith()`][str-endswith] returns `True` if the string ends with ``, `False` otherwise.
```python
@@ -41,13 +42,13 @@ There may also be [locale][locale] rules in place for a language or character se
```python
->>> man_in_hat_th = 'ΰΈΉΰΉΰΈΰΈ²ΰΈ’ΰΉΰΈΰΈ«ΰΈ‘ΰΈ§ΰΈ'
->>> man_in_hat_ru = 'mΡΠΆΡΠΈΠ½Π° Π² ΡΠ»ΡΠΏΠ΅'
+>>> man_in_hat_th = 'ΰΈΰΈΉΰΉΰΈΰΈ²ΰΈ’ΰΉΰΈͺΰΉΰΈ«ΰΈ‘ΰΈ§ΰΈ'
+>>> man_in_hat_ru = 'ΠΌΡΠΆΡΠΈΠ½Π° Π² ΡΠ»ΡΠΏΠ΅'
>>> man_in_hat_ko = 'λͺ¨μλ₯Ό μ΄ λ¨μ'
>>> man_in_hat_en = 'the man in the hat.'
>>> man_in_hat_th.title()
-'ΰΈΰΈΉΰΉΰΈΰΈ²ΰΈ’ΰΉΰΈΰΈ«ΰΈ‘ΰΈ§ΰΈ'
+'ΰΈΰΈΉΰΉΰΈΰΈ²ΰΈ’ΰΉΰΈͺΰΉΰΈ«ΰΈ‘ΰΈ§ΰΈ'
>>> man_in_hat_ru.title()
'ΠΡΠΆΡΠΈΠ½Π° Π Π¨Π»ΡΠΏΠ΅'
diff --git a/concepts/string-methods/links.json b/concepts/string-methods/links.json
index 2e6fc8460f3..c153ea44531 100644
--- a/concepts/string-methods/links.json
+++ b/concepts/string-methods/links.json
@@ -1,18 +1,18 @@
[
{
"url": "https://docs.python.org/3/library/stdtypes.html#string-methods",
- "description": "string methods"
+ "description": "Python Documentation: string methods"
},
{
"url": "https://docs.python.org/3/library/stdtypes.html#common-sequence-operations",
- "description": "common sequence operations"
+ "description": "Python Documentation: common sequence operations"
},
{
"url": "https://realpython.com/python-strings/",
- "description": "strings and character data in Python"
+ "description": "Real Python: strings and character data in Python"
},
{
"url": "https://www.programiz.com/python-programming/string",
- "description": "more string operations and functions"
+ "description": "Programiz: more string operations and functions"
}
]
diff --git a/concepts/strings/about.md b/concepts/strings/about.md
index 0107f6e70f0..227a252e821 100644
--- a/concepts/strings/about.md
+++ b/concepts/strings/about.md
@@ -9,7 +9,7 @@ The Python docs also provide a very detailed [unicode HOWTO][unicode how-to] tha
Strings implement all [common sequence operations][common sequence operations] and can be iterated through using `for item in ` or `for index, item in enumerate()` syntax.
Individual code points (_strings of length 1_) can be referenced by `0-based index` number from the left, or `-1-based index` number from the right.
-Strings can be concatenated with `+`, or via `.join()`, split via `.split()`, and offer multiple formatting and assembly options.
+Strings can be concatenated with `+`, or via `.join()`, split via `.split()`, and offer multiple formatting, assembly, and templating options.
A `str` literal can be declared via single `'` or double `"` quotes. The escape `\` character is available as needed.
@@ -18,7 +18,7 @@ A `str` literal can be declared via single `'` or double `"` quotes. The escape
>>> single_quoted = 'These allow "double quoting" without "escape" characters.'
->>> double_quoted = "These allow embedded 'single quoting', so you don't have to use an 'escape' character".
+>>> double_quoted = "These allow embedded 'single quoting', so you don't have to use an 'escape' character."
```
@@ -168,12 +168,12 @@ sentence = word + " " + "means" + " " + number + " in " + language + "."
"Π΄Π΅Π²'ΡΡΡ means nine in Ukrainian."
```
-If a `list`, `tuple`, `set` or other collection of individual strings needs to be combined into a single `str`, [`.join()`][str-join], is a better option:
+If a `list`, `tuple`, `set` or other collection of individual strings needs to be combined into a single `str`, [`.join()`][str-join] is a better option:
```python
# str.join() makes a new string from the iterables elements.
->>> chickens = ["hen", "egg", "rooster"]
+>>> chickens = ["hen", "egg", "rooster"] # Lists are iterable.
>>> ' '.join(chickens)
'hen egg rooster'
@@ -183,6 +183,34 @@ If a `list`, `tuple`, `set` or other collection of individual strings needs to b
>>> ' πΏ '.join(chickens)
'hen πΏ egg πΏ rooster'
+
+
+# Any iterable can be used as input.
+>>> flowers = ("rose", "daisy", "carnation") # Tuples are iterable.
+>>> '*-*'.join(flowers)
+'rose*-*daisy*-*carnation'
+
+>>> flowers = {"rose", "daisy", "carnation"} # Sets are iterable, but output order is not guaranteed.
+>>> '*-*'.join(flowers)
+'rose*-*carnation*-*daisy'
+
+>>> phrase = "This is my string" # Strings are iterable, but be careful!
+>>> '..'.join(phrase)
+'T..h..i..s.. ..i..s.. ..m..y.. ..s..t..r..i..n..g'
+
+
+# Separators are inserted **between** elements, but can be any string (including spaces).
+# This can be exploited for interesting effects.
+>>> under_words = ['under', 'current', 'sea', 'pin', 'dog', 'lay']
+>>> separator = ' β€΄οΈ under' # Note the leading space, but no trailing space.
+>>> separator.join(under_words)
+'under β€΄οΈ undercurrent β€΄οΈ undersea β€΄οΈ underpin β€΄οΈ underdog β€΄οΈ underlay'
+
+# The separator can be composed different ways, as long as the result is a string.
+>>> upper_words = ['upper', 'crust', 'case', 'classmen', 'most', 'cut']
+>>> separator = ' π ' + upper_words[0] # This becomes one string, similar to ' β€΄οΈ under'.
+>>> separator.join(upper_words)
+ 'upper π uppercrust π uppercase π upperclassmen π uppermost π uppercut'
```
Strings support all [common sequence operations][common sequence operations].
@@ -194,7 +222,9 @@ Indexes _with_ items can be iterated through in a loop via `for index, item in e
>>> exercise = 'αα±α·αα»ααΊα·'
-# Note that there are more code points than perceived glyphs or characters
+# Note that there are more code points than perceived glyphs or characters.
+# Care should be used when iterating over languages that use
+# combining characters, or when dealing with emoji.
>>> for code_point in exercise:
... print(code_point)
...
diff --git a/concepts/tuples/about.md b/concepts/tuples/about.md
index bc0e81462e5..ea8179e2d8a 100644
--- a/concepts/tuples/about.md
+++ b/concepts/tuples/about.md
@@ -98,7 +98,7 @@ Other data structures can be included as `tuple` elements, including other `tupl
>>> nested_data_structures = ({"fish": "gold", "monkey": "brown", "parrot" : "grey"}, ("fish", "mammal", "bird"))
({"fish": "gold", "monkey": "brown", "parrot" : "grey"}, ("fish", "mammal", "bird"))
->>> nested_data_structures_1 : (["fish", "gold", "monkey", "brown", "parrot", "grey"], ("fish", "mammal", "bird"))
+>>> nested_data_structures_1 = (["fish", "gold", "monkey", "brown", "parrot", "grey"], ("fish", "mammal", "bird"))
(["fish", "gold", "monkey", "brown", "parrot", "grey"], ("fish", "mammal", "bird"))
```
@@ -219,11 +219,11 @@ Additionally, users can adapt a [`dataclass`][dataclass] to provide similar name
[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
[dataclass pros and cons]: https://stackoverflow.com/questions/51671699/data-classes-vs-typing-namedtuple-primary-use-cases
[dataclass]: https://docs.python.org/3/library/dataclasses.html
-[dict]: https://github.com/exercism/v3/blob/master/languages/python/reference/concepts/builtin_types/dict.md
+[dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[hashability]: https://docs.python.org/3/glossary.html#hashable
-[list]: https://github.com/exercism/v3/blob/master/languages/python/reference/concepts/builtin_types/list.md
+[list]: https://docs.python.org/3/library/stdtypes.html#list
[mutable sequence operations]: https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types
[namedtuple]: https://docs.python.org/3/library/collections.html#collections.namedtuple
[sequence]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
-[set]: https://github.com/exercism/v3/blob/master/languages/python/reference/concepts/builtin_types/set.md
-[tuple]: https://github.com/exercism/v3/blob/master/languages/python/reference/concepts/builtin_types/tuple.md
\ No newline at end of file
+[set]: https://docs.python.org/3/library/stdtypes.html#set
+[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
diff --git a/concepts/tuples/introduction.md b/concepts/tuples/introduction.md
index 9ab6042e694..4271336863e 100644
--- a/concepts/tuples/introduction.md
+++ b/concepts/tuples/introduction.md
@@ -7,6 +7,6 @@ The elements of a tuple can be iterated over using the `for item in ` con
If both element index and value are needed, `for index, item in enumerate()` can be used.
Like any sequence, elements within `tuples` can be accessed via _bracket notation_ using a `0-based index` number from the left or a `-1-based index` number from the right.
-[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
-[mutable sequence operations]: https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types
\ No newline at end of file
+[mutable sequence operations]: https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types
+[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
\ No newline at end of file
diff --git a/concepts/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md
index 1cec2f92ec9..d4b9168ad13 100644
--- a/concepts/unpacking-and-multiple-assignment/about.md
+++ b/concepts/unpacking-and-multiple-assignment/about.md
@@ -129,9 +129,9 @@ ValueError: too many values to unpack (expected 1)
### Unpacking a list/tuple with `*`
-When [unpacking a `list`/`tuple`][packing and unpacking] you can use the `*` operator to capture the "leftover" values.
+When [unpacking a `list`/`tuple`][packing and unpacking] you can use the `*` operator to capture "leftover" values.
This is clearer than slicing the `list`/`tuple` (_which in some situations is less readable_).
-For example, we can extract the first element and then assign the remaining values into a new `list` without the first element:
+For example, we can extract the first element and pack the remaining values into a new `list` without the first element:
```python
>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
@@ -169,7 +169,7 @@ We can also use `*` in deep unpacking:
### Unpacking a dictionary
-[Unpacking a dictionary][packing and unpacking] is a bit different than unpacking a `list`/`tuple`.
+[Unpacking a dictionary][packing and unpacking] is a bit different from unpacking a `list`/`tuple`.
Iteration over dictionaries defaults to the **keys**.
So when unpacking a `dict`, you can only unpack the **keys** and not the **values**:
@@ -180,7 +180,7 @@ So when unpacking a `dict`, you can only unpack the **keys** and not the **value
"apple"
```
-If you want to unpack the values then you can use the `values()` method:
+If you want to unpack the values then you can use the `.values()` method:
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
@@ -189,9 +189,9 @@ If you want to unpack the values then you can use the `values()` method:
6
```
-If both **keys** and **values** are needed, use the `items()` method.
-Using `items()` will generate tuples with **key-value** pairs.
-This is because of [`dict.items()` generates an iterable with key-value `tuples`][items].
+If both **keys** and **values** are needed, use the [`.items()`][items] method.
+`.items()` generates an [iterable view][view-objects] containing **key-value** pairs.
+These can be unpacked into a `tuple`:
```python
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
@@ -222,12 +222,20 @@ This will pack all the values into a `list`/`tuple`.
>>> combined_fruits
("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")
-# If the * operator is used on the left side of "=" the result is a list
+# If the * operator is used on the left side of "=" the result is a list.
+# Note the trailing comma.
>>> *combined_fruits_too, = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']
+
+# A list literal can be used instead, but might not be as readable.
+>>> [*combined_fruits_too] = *fruits, *more_fruits
+>>> combined_fruits_too
+['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']
```
+For more details on the use of `*` and `**`, check out [PEP 3132][pep-3132] and [PEP 448][pep-448].
+
### Packing a dictionary with `**`
Packing a dictionary is done by using the `**` operator.
@@ -356,8 +364,8 @@ numbers = [1, 2, 3]
1
```
-Using `*` unpacking with the `zip()` function is another common use case.
-Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the values from each `iterable` grouped:
+Using `*` unpacking with the [`zip()` built-in][zip] is another common use case.
+The `zip()` function takes multiple iterables and returns a `list` of `tuples` with the values from each `iterable` grouped:
```python
>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
@@ -367,8 +375,12 @@ Since `zip()` takes multiple iterables and returns a `list` of `tuples` with the
```
[args and kwargs]: https://www.geeksforgeeks.org/args-kwargs-python/
-[items]: https://www.geeksforgeeks.org/python-dictionary-items-method/
+[items]: https://docs.python.org/3/library/stdtypes.html#dict.items
[multiple assignment]: https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/
[packing and unpacking]: https://www.geeksforgeeks.org/packing-and-unpacking-arguments-in-python/
+[pep-448]: https://peps.python.org/pep-0448/
+[pep-3132]: https://peps.python.org/pep-3132/
[sorting algorithms]: https://realpython.com/sorting-algorithms-python/
[unpacking]: https://www.geeksforgeeks.org/unpacking-arguments-in-python/?ref=rp
+[view-objects]: https://docs.python.org/3/library/stdtypes.html#dict-views
+[zip]: https://docs.python.org/3/library/functions.html#zip
diff --git a/config.json b/config.json
index 871f4c84297..31f53360614 100644
--- a/config.json
+++ b/config.json
@@ -150,7 +150,7 @@
"uuid": "5ac0c40c-4038-47b8-945b-8480e4a3f44c",
"concepts": ["dict-methods"],
"prerequisites": ["dicts"],
- "status": "wip"
+ "status": "beta"
},
{
"slug": "locomotive-engineer",
@@ -193,7 +193,7 @@
"uuid": "3ba3fc89-3e1b-48a5-aff0-5aeaba8c8810",
"concepts": ["generators"],
"prerequisites": ["conditionals", "dicts", "lists", "loops", "classes"],
- "status": "wip"
+ "status": "beta"
},
{
"slug": "log-levels",
@@ -206,7 +206,6 @@
"comprehensions",
"loops",
"sequences",
- "string-formatting",
"string-methods",
"tuples"
],
@@ -364,21 +363,6 @@
],
"difficulty": 1
},
- {
- "slug": "wordy",
- "name": "Wordy",
- "uuid": "af50bb9a-e400-49ce-966f-016c31720be1",
- "practices": ["string-methods"],
- "prerequisites": [
- "basics",
- "lists",
- "loops",
- "strings",
- "string-methods",
- "numbers"
- ],
- "difficulty": 1
- },
{
"slug": "resistor-color",
"name": "Resistor Color",
@@ -466,7 +450,7 @@
"slug": "house",
"name": "House",
"uuid": "7c2e93ae-d265-4481-b583-a496608c6031",
- "practices": ["loops"],
+ "practices": [],
"prerequisites": [
"basics",
"lists",
@@ -527,6 +511,14 @@
],
"difficulty": 1
},
+ {
+ "slug": "line-up",
+ "name": "Line Up",
+ "uuid": "e09d877e-489b-4dbd-89c5-f6b15e867b67",
+ "practices": ["string-formatting"],
+ "prerequisites": ["basics", "strings"],
+ "difficulty": 1
+ },
{
"slug": "difference-of-squares",
"name": "Difference of Squares",
@@ -602,7 +594,7 @@
"slug": "square-root",
"name": "Square Root",
"uuid": "c32f994a-1080-4f05-bf88-051975a75d64",
- "practices": ["numbers"],
+ "practices": [],
"prerequisites": ["basics", "numbers", "conditionals", "loops"],
"difficulty": 2
},
@@ -676,11 +668,26 @@
],
"difficulty": 2
},
+ {
+ "slug": "eliuds-eggs",
+ "name": "Eliud's Eggs",
+ "uuid": "356e2d29-7efc-4fa3-bec7-8b61c3e967da",
+ "practices": ["loops"],
+ "prerequisites": [
+ "basics",
+ "lists",
+ "list-methods",
+ "loops",
+ "strings",
+ "string-methods"
+ ],
+ "difficulty": 2
+ },
{
"slug": "protein-translation",
"name": "Protein Translation",
"uuid": "c89243f3-703e-4fe0-8e43-f200eedf2825",
- "practices": ["loops"],
+ "practices": [],
"prerequisites": [
"basics",
"conditionals",
@@ -768,7 +775,7 @@
"slug": "nth-prime",
"name": "Nth Prime",
"uuid": "a20924d2-fe6d-4714-879f-3239feb9d2f2",
- "practices": [],
+ "practices": ["generators"],
"prerequisites": [
"basics",
"bools",
@@ -921,7 +928,6 @@
"loops",
"numbers",
"strings",
- "string-formatting",
"string-methods"
],
"difficulty": 2
@@ -968,6 +974,28 @@
"prerequisites": ["basics", "bools", "numbers", "classes"],
"difficulty": 2
},
+ {
+ "slug": "game-of-life",
+ "name": "Conway's Game of Life",
+ "uuid": "1675a497-d3b2-4772-bbee-4edae5a44e91",
+ "practices": [],
+ "prerequisites": [
+ "lists"
+ ],
+ "difficulty": 3
+ },
+ {
+ "slug": "state-of-tic-tac-toe",
+ "name": "State of Tic-Tac-Toe",
+ "uuid": "cb40b36f-f7dc-4018-aad5-38976defb352",
+ "practices": ["loops"],
+ "prerequisites": [
+ "conditionals",
+ "lists",
+ "loops"
+ ],
+ "difficulty": 3
+ },
{
"slug": "saddle-points",
"name": "Saddle Points",
@@ -1197,6 +1225,21 @@
],
"difficulty": 3
},
+ {
+ "slug": "wordy",
+ "name": "Wordy",
+ "uuid": "af50bb9a-e400-49ce-966f-016c31720be1",
+ "practices": ["string-methods"],
+ "prerequisites": [
+ "basics",
+ "lists",
+ "loops",
+ "strings",
+ "string-methods",
+ "numbers"
+ ],
+ "difficulty": 3
+ },
{
"slug": "crypto-square",
"name": "Crypto Square",
@@ -1315,17 +1358,19 @@
"difficulty": 4
},
{
- "slug": "minesweeper",
- "name": "Minesweeper",
- "uuid": "7e768b54-4591-4a30-9ddb-66ca13400ca3",
- "practices": ["lists"],
- "prerequisites": [
- "basics",
+ "slug": "swift-scheduling",
+ "name": "Swift Scheduling",
+ "uuid": "ebddfc37-a3fc-4524-bd62-9c70f979713c",
+ "practices": [],
+ "prerequisites": ["basics",
"bools",
"conditionals",
"lists",
+ "list-methods",
"loops",
- "numbers"
+ "numbers",
+ "strings",
+ "string-methods"
],
"difficulty": 4
},
@@ -1397,6 +1442,22 @@
],
"difficulty": 4
},
+ {
+ "slug": "flower-field",
+ "name": "Flower Field",
+ "uuid": "0c2751c1-5d2f-499a-81b8-226e5092ea88",
+ "practices": ["lists"],
+ "prerequisites": [
+ "conditionals",
+ "lists",
+ "list-methods",
+ "loops",
+ "numbers",
+ "strings",
+ "string-methods"
+ ],
+ "difficulty": 4
+ },
{
"slug": "rail-fence-cipher",
"name": "Rail Fence Cipher",
@@ -1472,7 +1533,7 @@
"slug": "scale-generator",
"name": "Scale Generator",
"uuid": "8cd58325-61fc-46fd-85f9-425b4c41f3de",
- "practices": ["generators"],
+ "practices": [],
"prerequisites": [
"basics",
"bools",
@@ -1490,12 +1551,7 @@
"slug": "largest-series-product",
"name": "Largest Series Product",
"uuid": "21624a3e-6e43-4c0e-94b0-dee5cdaaf2aa",
- "practices": [
- "functions",
- "higher-order-functions",
- "functional-tools",
- "anonymous-functions"
- ],
+ "practices": ["generators"],
"prerequisites": [
"basics",
"conditionals",
@@ -1541,7 +1597,7 @@
},
{
"slug": "pascals-triangle",
- "name": "Pascals Triangle",
+ "name": "Pascal's Triangle",
"uuid": "e1e1c7d7-c1d9-4027-b90d-fad573182419",
"practices": ["recursion"],
"prerequisites": [
@@ -1704,6 +1760,14 @@
],
"difficulty": 5
},
+ {
+ "slug": "camicia",
+ "name": "Camicia",
+ "uuid": "ec314b34-2ee3-4eec-9a97-aece9e5fd6c2",
+ "practices": [],
+ "prerequisites": ["lists", "sets", "strings"],
+ "difficulty": 5
+ },
{
"slug": "rational-numbers",
"name": "Rational Numbers",
@@ -1757,6 +1821,17 @@
],
"difficulty": 5
},
+ {
+ "slug": "relative-distance",
+ "name": "Relative Distance",
+ "uuid": "d590865c-ef30-424a-8cfb-7f31f04dee1b",
+ "practices": [],
+ "prerequisites": [
+ "lists",
+ "dicts"
+ ],
+ "difficulty": 5
+ },
{
"slug": "dot-dsl",
"name": "DOT DSL",
@@ -2130,15 +2205,6 @@
"difficulty": 2,
"status": "deprecated"
},
- {
- "slug": "nucleotide-count",
- "name": "Nucleotide Count",
- "uuid": "105f25ec-7ce2-4797-893e-05e3792ebd91",
- "practices": [],
- "prerequisites": [],
- "difficulty": 2,
- "status": "deprecated"
- },
{
"slug": "binary",
"name": "Binary",
@@ -2175,15 +2241,6 @@
"difficulty": 3,
"status": "deprecated"
},
- {
- "slug": "parallel-letter-frequency",
- "name": "Parallel Letter Frequency",
- "uuid": "da03fca4-4606-48d8-9137-6e40396f7759",
- "practices": [],
- "prerequisites": [],
- "difficulty": 3,
- "status": "deprecated"
- },
{
"slug": "point-mutations",
"name": "Point Mutations",
@@ -2228,9 +2285,18 @@
"prerequisites": [],
"difficulty": 4,
"status": "deprecated"
+ },
+ {
+ "slug": "minesweeper",
+ "name": "Minesweeper",
+ "uuid": "7e768b54-4591-4a30-9ddb-66ca13400ca3",
+ "practices": [],
+ "prerequisites": [],
+ "difficulty": 4,
+ "status": "deprecated"
}
],
- "foregone": ["lens-person"]
+ "foregone": ["lens-person", "nucleotide-count", "parallel-letter-frequency"]
},
"concepts": [
{
@@ -2358,6 +2424,11 @@
"slug": "enums",
"name": "Enums"
},
+ {
+ "uuid": "8ac7c6b5-5786-45dd-8ce3-5b139da06471",
+ "slug": "fractions",
+ "name": "Fractions"
+ },
{
"uuid": "26b147e0-2cdc-4325-a6b4-6a2dd5bb69b1",
"slug": "function-arguments",
@@ -2552,6 +2623,21 @@
"uuid": "565f7618-4552-4eb0-b829-d6bacd03deaf",
"slug": "with-statement",
"name": "With Statement"
+ },
+ {
+ "uuid": "af6cad74-50c2-48f4-a6ce-cfeb72548d00",
+ "slug": "random",
+ "name": "Random"
+ },
+ {
+ "uuid": "000e7768-38b9-4904-9ae2-9a4e448f366c",
+ "slug": "fractions",
+ "name": "Fractions"
+ },
+ {
+ "uuid": "e1496136-8d58-4409-9a41-4b6ee4721c6b",
+ "slug": "secrets",
+ "name": "Secrets"
}
],
"key_features": [
diff --git a/docs/ABOUT.md b/docs/ABOUT.md
index 6177394a518..8f06e20267a 100644
--- a/docs/ABOUT.md
+++ b/docs/ABOUT.md
@@ -20,14 +20,14 @@ Code can be written and executed from the command line, in an interactive interp
The [zen of Python (PEP 20)][the zen of python] and [What is Pythonic?][what is pythonic] lay out additional philosophies and perspectives on the language.
-Tests and tooling for this track currently support `3.7` - `3.11.2` (_tests_) and [`Python 3.11.2`][311-new-features] (_tooling_).
-It is highly recommended that students upgrade to at least `Python 3.8`, as some features used by this track may not be supported in earlier versions.
+Tests and tooling for this track currently support `3.10` - `3.13.5` (_tests_) and [`Python 3.13.5`][313-new-features] (_tooling_).
+It is highly recommended that students upgrade to at least `Python 3.10`, as some features used by this track may not be supported in earlier versions.
That being said, most of the exercises will work with `Python 3.6+`, or even earlier versions.
But we don't guarantee support for versions not listed under [Active Python Releases][active-python-releases].
We will try to note when a feature is only available in a certain version.
-Complete documentation for the current release of Python (3.11.x) can be found at [docs.python.org][python docs].
+Complete documentation for the current release of Python (3.13.x) can be found at [docs.python.org][python docs].
- [Python Tutorial][python tutorial]
- [Python Library Reference][python library reference]
@@ -37,8 +37,8 @@ Complete documentation for the current release of Python (3.11.x) can be found a
- [Python Glossary of Terms][python glossary of terms]
-[311-new-features]: https://docs.python.org/3/whatsnew/3.11.html
-[active-python-releases]: https://www.python.org/downloads/
+[313-new-features]: https://docs.python.org/3/whatsnew/3.13.html
+[active-python-releases]: https://devguide.python.org/versions/#full-chart
[duck typing]: https://en.wikipedia.org/wiki/Duck_typing
[dynamic typing in python]: https://stackoverflow.com/questions/11328920/is-python-strongly-typed
[editors for python]: https://djangostars.com/blog/python-ide/
@@ -49,16 +49,16 @@ Complete documentation for the current release of Python (3.11.x) can be found a
[peps]: https://www.python.org/dev/peps/
[psf membership]: https://www.python.org/psf/membership/
[psf]: https://www.python.org/psf/
-[python docs]: https://docs.python.org/3/
-[python faqs]: https://docs.python.org/3/faq/index.html
+[python docs]: https://docs.python.org/3.13/
+[python faqs]: https://docs.python.org/3.13/faq/index.html
[python for beginners]: https://www.python.org/about/gettingstarted/
[python glossary of terms]: https://docs.python.org/3/glossary.html
-[python how tos]: https://docs.python.org/3/howto/index.html
+[python how tos]: https://docs.python.org/3.13/howto/index.html
[python is used extensively]: https://www.python.org/about/apps/
-[python language reference]: https://docs.python.org/3/reference/index.html
-[python library reference]: https://docs.python.org/3/library/index.html
-[python tutorial]: https://docs.python.org/3/tutorial/index.html
-[significant indentation]: https://docs.python.org/3/reference/lexical_analysis.html#indentation
+[python language reference]: https://docs.python.org/3.13/reference/index.html
+[python library reference]: https://docs.python.org/3.13/library/index.html
+[python tutorial]: https://docs.python.org/3.13/tutorial/index.html
+[significant indentation]: https://docs.python.org/3.13/reference/lexical_analysis.html#indentation
[the zen of python]: https://www.python.org/dev/peps/pep-0020/
[type hints]: https://docs.python.org/3/library/typing.html
[what is pythonic]: https://blog.startifact.com/posts/older/what-is-pythonic.html
diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md
index 04f9c899349..d70d15153f7 100644
--- a/docs/INSTALLATION.md
+++ b/docs/INSTALLATION.md
@@ -14,24 +14,25 @@ Some quick links into the documentation by operating system:
- [Windows][windows] Additionally, this Microsoft article on [installing Python on windows][python-on-windows] is helpful.
- [Unix & Linux Systems][unix-and-linux] (_these largely work for MacOS as well_)
-- [MacOS][macos] : **This is outdated.**
- We recommend reviewing some of the methods outlined in the Real Python article [Installing Python][installing-python] or the articles by Brett Cannon linked above.
+- [MacOS][macos]
+ We also recommend reviewing some of the methods outlined in the Real Python article [Installing Python][installing-python] or the articles by Brett Cannon linked above.
-Exercism tests and tooling currently support `3.7` - `3.11.2` (_tests_) and [`Python 3.11.2`][311-new-features] (_tooling_).
+
+Exercism tests and tooling currently support `3.10` - `3.13.5` (_tests_) and [`Python 3.13.5`][313-new-features] (_tooling_).
Exceptions to this support are noted where they occur.
Most of the exercises will work with `Python 3.6+`, or even earlier versions.
But we don't guarantee support for versions not listed under [Active Python Releases][active-python-releases].
-Please refer to the [Python 3.11.x documentation][3.11 docs] for what is currently supported.
+Please refer to the [Python 3.13.x documentation][3.13 docs] for what is currently supported.
-[3.11 docs]: https://docs.python.org/3.11/
-[311-new-features]: https://docs.python.org/3/whatsnew/3.11.html
+[3.13 docs]: https://docs.python.org/3.13/
+[313-new-features]: https://docs.python.org/3/whatsnew/3.13.html
[Python-three downloads]: https://www.python.org/downloads/
[active-python-releases]: https://www.python.org/downloads/
[helpful guide]: https://realpython.com/installing-python/
-[installing-python]: https://realpython.com/installing-python/#what-your-options-are_1
+[installing-python]: https://realpython.com/installing-python/
[macos]: https://docs.python.org/3/using/mac.html
[python-m-pip]: https://snarky.ca/why-you-should-use-python-m-pip/
[python-on-windows]: https://docs.microsoft.com/en-us/windows/python/beginners
diff --git a/docs/LEARNING.md b/docs/LEARNING.md
index 50a3259eed7..4a85339a936 100644
--- a/docs/LEARNING.md
+++ b/docs/LEARNING.md
@@ -6,21 +6,26 @@ Python is (_as [Wikipedia says][wikipython]_), a *general-purpose and high-level
It is especially good at 'gluing' different systems and programs together.
-And we think the best way to lean is to _play_ and to _practice_ with coding projects big and small - or with small problems like the ones here on exercism!
+And we think the best way to learn is to _play_ and to _practice_ with coding projects big and small - or with small problems like the ones here on exercism!
Below you will find some additional jumping-off places to start your learning journey, recommended by our community.
- [Python Documentation Tutorial][Python Documentation Tutorial]
+- [Stanford Code in Place Course][code in place]
+- [Stanford Code in Place Self-Guided][cip self guided]
- [Automate the Boring Stuff with Python (_book_)][automate the boring stuff]
-- [Automate the Boring Stuff Videos (_covers first 15 book chapters_)][automate the videos]
+- [Automate the Boring Stuff with Python Workbook (_exercises_)][automate the boring stuff workbook]
+- [Think Python][Think Python]
+- [Beyond the Basic Stuff with Python][beyond the basics]
+- [Beyond the Basic Stuff with Python Video][beyond the basics video]
+- [Python Programming Exercises, Gently Explained][PPEG]
+- [Python for Non-Programmers][python-for-non-programmers]
- [Learn X in Y minutes (where X = Python3)][Learn X in Y minutes]
- [Python at Free Code Camp][python at free code camp]
+- [Python at Khan Academy][python at kahn academy]
- [Intro to Python (_python-course.eu_)][python-course.eu]
-- [Think Python][Think Python]
-- [Python for Non-Programmers][python-for-non-programmers]
-- [Python 4 Everyone][python4everyone]
- [Googles Python Class][googles python class]
- [Microsoft's Python Learning Path][MS Python]
- [Introduction to Computer Science and Programming in Python (MIT)][mitocw600]
@@ -30,14 +35,19 @@ Below you will find some additional jumping-off places to start your learning jo
[CS50P]: https://pll.harvard.edu/course/cs50s-introduction-programming-python?delta=0
[Learn X in Y minutes]: https://learnxinyminutes.com/docs/python3/
[MS Python]: https://docs.microsoft.com/en-us/learn/paths/python-language/
+[PPEG]: https://inventwithpython.com/pythongently/
[Python Documentation Tutorial]: https://docs.python.org/3/tutorial/index.html
-[Python at Free Code Camp]: https://www.freecodecamp.org/learn/scientific-computing-with-python/
-[Think Python]: http://www.greenteapress.com/thinkpython/html/index.html
-[automate the boring stuff]: https://automatetheboringstuff.com/2e/
-[automate the videos]: https://www.youtube.com/watch?v=1F_OgqRuSdI&list=PL0-84-yl1fUnRuXGFe_F7qSH1LEnn9LkW
-[googles python class]: https://developers.google.com/edu/python/introduction
+[Python at Free Code Camp]: https://www.freecodecamp.org/learn/python-v9/
+[Think Python]: https://allendowney.github.io/ThinkPython/
+[automate the boring stuff workbook]: https://automatetheboringstuff.com/#toc
+[automate the boring stuff]: https://automatetheboringstuff.com/#toc
+[beyond the basics video]: https://www.youtube.com/watch?v=kSrnLbioN6w
+[beyond the basics]: https://inventwithpython.com/beyond/
+[cip self guided]: https://codeinplace.stanford.edu/public/studenthome
+[code in place]: https://codeinplace.stanford.edu/
+[googles python class]: https://developers.google.com/edu/python
[mitocw600]: https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/
+[python at kahn academy]: https://www.khanacademy.org/computing/intro-to-python-fundamentals
[python-course.eu]: https://python-course.eu/python-tutorial/
[python-for-non-programmers]: https://store.lerner.co.il/python-for-non-programmers-live
-[python4everyone]: https://www.py4e.com/
[wikipython]: https://en.wikipedia.org/wiki/Python_(programming_language)
diff --git a/docs/PROBLEM-SOLVING.md b/docs/PROBLEM-SOLVING.md
index 198b9ea5ef9..47a99e8a8ab 100644
--- a/docs/PROBLEM-SOLVING.md
+++ b/docs/PROBLEM-SOLVING.md
@@ -8,9 +8,9 @@ Below are some community-sourced articles, videos, and books that can help you d
- Free Code Camp offers a good overview article on [How to Think Like a Programmer - Lessons in Problem-Solving][free-code-camp-think-like-a-programmer].
-- Kurtis Pykes writing for Towards Data Science has a nice [hands-on tutorial for problem-solving][Kurtis Pykes: Hands-on Tutorial - How to Improve Your Problem-Solving Skills as A Programmer].
-- UC Berkeley has a nice PDF summary of [G. Polya's Problem Solving Techniques][g-polya-how-to-solve-it-summary].
+- Jeremy Howard, founder of fast.ai has a nice summary of [G. Polya's Problem Solving Techniques][g-polya-how-to-solve-it-summary].
- Originally written in 1945 as guidance for tackling complicated math problems,[G. Polya's How To Solve It][g-polya-how-to-solve-it] (full book) is still _excellent_ advice for problem-solving in general.
+- Kurtis Pykes writing for Towards Data Science has a nice [hands-on tutorial for problem-solving][Kurtis Pykes: Hands-on Tutorial - How to Improve Your Problem-Solving Skills as A Programmer].
- Mentioned in the Free Code Camp Article, V. Anton Spraul's [Think Like a Programmer: An Introduction to Creative Problem Solving][v-anton-spraul-think-like-a-programmer] is a more modern and programming-focused take on the same general methods Polya outlines for mathematics.
- [Felienne Hermans][felienne-hermans] is more focused on _how_ people learn the art of coding and how that maps to learning in general.
She has written [The Programmers Brian][programmers-brain-free-online], with strategies for reading code better, thinking about code clearly, writing better code, and becoming a better code collaborator.
@@ -32,8 +32,8 @@ Membership (paid) information is available at [acm(dot)org][association-for-comp
[felienne-hermans-programming-is-writing-is-programming]: https://www.youtube.com/watch?v=uO3a4HIBDU4
[felienne-hermans]: https://www.felienne.com/
[free-code-camp-think-like-a-programmer]: https://www.freecodecamp.org/news/how-to-think-like-a-programmer-lessons-in-problem-solving-d1d8bf1de7d2/
-[g-polya-how-to-solve-it-summary]: https://math.berkeley.edu/~gmelvin/polya.pdf
-[g-polya-how-to-solve-it]: https://press.princeton.edu/books/paperback/9780691164076/how-to-solve-it
+[g-polya-how-to-solve-it-summary]: https://gist.github.com/jph00/d60301884c56fe063101a7cc6193b3af
+[g-polya-how-to-solve-it]: https://archive.org/details/howtosolveit0000gpol_c0p2/page/n9/mode/2up
[paul-vickers-how-to-think-like-a-programmer]: https://www.researchgate.net/publication/236270907_How_to_Think_like_a_Programmer_Problem_Solving_for_the_Bewildered
[programmers-brain-free-online]: https://www.manning.com/books/the-programmers-brain#toc
[programmers-brain-manning]: https://www.manning.com/books/the-programmers-brain
diff --git a/docs/RESOURCES.md b/docs/RESOURCES.md
index ea8527592ab..f04fcf601d0 100644
--- a/docs/RESOURCES.md
+++ b/docs/RESOURCES.md
@@ -5,24 +5,26 @@
- [The Docs on pip][pip]
- [Tall, Snarky Canadian (_The Blog of Core Python Developer Brett Cannon_)][Tall, Snarky Canadian]
- [Practical Python][practical python]
-- [Python 3 Module of the Week (PyMOTW-3)][pymotw-3]
-- [Beyond the Basic Stuff with Python][Beyond the Basic Stuff with Python]
-- [The Big Book of Small Python Projects][The Big Book of Small Python Projects]
- [Data Structures and Information Retrieval in Python][Data Structures and Information Retrieval in Python]
-- [python practice projects][python practice projects]
-- [Python Courses eu][python-course.eu main]
-- [Fluent Python Notebooks][fluent-notebooks]
+- [Fluent Python, 2nd Edition][Fluent Python 2] (_you might be able to find free access through a library or online_)
+- [Fluent Python Notebooks][fluent-notebooks] (_these are based on the first edition, but still really good_)
+- [The Big Book of Small Python Projects][The Big Book of Small Python Projects]
+- [Practice Python Projects][practice python projects]
+- [Dataquest: Python Projects for Beginners][Python projects for beginners]
+- [Mouse vs Python (Mike Driscoll's blog)][mouse vs python]
+- [Simon Willison's Weblog][simon willison]
-[Beyond the Basic Stuff with Python]: https://inventwithpython.com/beyond/
[Data Structures and Information Retrieval in Python]: https://allendowney.github.io/DSIRP/
[Practical Python]: https://dabeaz-course.github.io/practical-python/
+[Python projects for beginners]: https://www.dataquest.io/blog/python-projects-for-beginners/
[Tall, Snarky Canadian]: https://snarky.ca/
[The Big Book of Small Python Projects]: http://inventwithpython.com/bigbookpython/
[The Python Library Reference]: https://docs.python.org/3/library/index.html
+[fluent python 2]: https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/
[fluent-notebooks]: https://github.com/AllenDowney/fluent-python-notebooks
+[mouse vs python]: https://www.blog.pythonlibrary.org/
[pip]: https://pip.pypa.io/en/stable/user_guide/
-[pymotw-3]: https://pymotw.com/3/
+[practice python projects]: https://learnbyexample.github.io/practice_python_projects/preface.html
[python docs]: https://docs.python.org/3/
-[python practice projects]: http://pythonpracticeprojects.com/
-[python-course.eu main]: https://python-course.eu/
+[simon willison]: https://simonwillison.net/
diff --git a/docs/TESTS.md b/docs/TESTS.md
index 242555371f3..8c01c524816 100644
--- a/docs/TESTS.md
+++ b/docs/TESTS.md
@@ -10,8 +10,7 @@ You should also install the following `pytest` plugins:
We also recommend using the code linting program [pylint][pylint], as it is part of our automated feedback on the website and can be a very useful static code analysis tool.
For ease-of-use, the [pytest-pylint][pytest-pylint] plugin for `pytest` will allow you to run `pylint` via `pytest` on the command line.
-Pylint configuration can be a bit much, so this [tutorial from pycqa.org][tutorial from pycqa.org] can be helpful for getting started, as can this overview of [Code Quality: Tools and Best Practices][Code Quality: Tools and Best Practices] from Real Python.
-
+Pylint configuration can be a bit much, so this [tutorial from pylint.readthedocs.io][tutorial from pylint.readthedocs.io] can be helpful for getting started, as can this overview of [Code Quality: Tools and Best Practices][Code Quality: Tools and Best Practices] from Real Python.
## Installing pytest
@@ -25,44 +24,55 @@ Please adjust the install commands below accordingly.
To install `pytest` in a virtual environment, ensure the environment **is activated** prior to executing commands.
Otherwise, the `pytest` installation will be global.
-
#### Windows
```powershell
PS C:\Users\foobar> py -m pip install pytest pytest-cache pytest-subtests pytest-pylint
-Successfully installed pytest-7.2.2 ...
+Successfully installed pytest-8.3.3 ...
```
#### Linux / MacOS
```bash
$ python3 -m pip install pytest pytest-cache pytest-subtests pytest-pylint
-Successfully installed pytest-7.2.2 ...
+Successfully installed pytest-8.3.3 ...
```
-
To check if installation was successful:
```bash
$ python3 -m pytest --version
-pytest 7.2.2
+pytest 8.3.3
```
## Running the tests
-To run the tests, go to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
+To run the tests, go to the folder where the exercise is stored using `cd` in your terminal (_replace `` below with your path_).
```bash
-$ cd {exercise-folder-location}
+$ cd
```
+
+
+~~~~exercism/note
+ `` or most things inside angle brackets denote a **_placeholder value_**.
+A normal path or file name should be written _without_ any brackets.
+
+
+For example: `/Users/janedoe/exercism/python/exercises/concept/chaitanas-colossal-coaster` (on *nix systems), `C:\Users\janedoe\exercism\python\exercises\practice\hello-world\` (on Windows), `myFolder` or `my_file.py`.
+~~~~
+
+
+
+
The file you will want to run usually ends in `_test.py`.
This file contains the tests for the exercise solution, and are the same tests that run on the website when a solution is uploaded.
-Next, run the following command in your terminal, replacing `{exercise_test.py}` with the location/name of the test file:
+Next, run the following command in your terminal, replacing `` with the location/name of the test file:
```bash
-$ python3 -m pytest -o markers=task {exercise_test.py}
+$ python3 -m pytest -o markers=task
==================== 7 passed in 0.08s ====================
```
@@ -85,22 +95,21 @@ More information on pytest marks can be found in the `pytest` documentation on [
_More information on customizing pytest configurations can be found in the pytest documentation on [configuration file formats][configuration file formats]_
-
### Test Failures
When tests fail, `pytest` prints the text of each failed test, along with the expected and actual `return` values of each to the terminal.
Below is an generic example of a failed test:
```bash
-$(my_venv) python3 -m pytest -o markers=task {exercise_test.py}
+$(my_venv) python3 -m pytest -o markers=task
=================== FAILURES ====================
______________ name_of_failed_test ______________
-# Test code inside of {exercise_test.py} that failed.
+# Test code inside of that failed.
...
E TypeOfError: ReturnedValue != ExpectedValue
-exercise_test.py:{line_of_failed_test}: TypeOfError
+exercise_test.py:: TypeOfError
============ short test summary info ============
FAILED exercise_test.py::ExerciseTest::name_of_failed_test
========== 1 failed, 2 passed in 0.13s ==========
@@ -110,13 +119,12 @@ FAILED exercise_test.py::ExerciseTest::name_of_failed_test
If you really want to be specific about what pytest returns on your screen, here are some handy command-line arguments that allows you to configure its behavior.
-
#### Return All Details [`-v`]
Adding the `-v` (_verbose_) flag will return both environment information and a test summary in addition to test failures:
```bash
-$(my_venv) python3 -m pytest -o markers=task -v exercises//
+$(my_venv) python3 -m pytest -o markers=task -v exercises//
======================================== test session starts ===========================================
platform darwin -- Python 3.9.0, pytest-6.2.5, -- /usr/local/envs/my_env/bin/python3
@@ -125,7 +133,7 @@ metadata: {'Python': '3.9.0', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Pa
rootdir: /Users//exercism/python, configfile: pytest.ini
plugins: subtests-0.5.0, pylint-0.18.0
-collected 5 items
+collected 5 items
exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_one FAILED [ 20%]
exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_two FAILED
@@ -149,7 +157,7 @@ Using the `-x` flag will run the tests as normal, but stop the test run upon the
This helps when you want to debug a single task or test failure at a time:
```bash
-$(my_venv) python3 -m pytest -o markers=task -x exercises//
+$(my_venv) python3 -m pytest -o markers=task -x exercises//
=================== FAILURES ====================
_______________ example_test_foo ________________
@@ -166,7 +174,6 @@ FAILED example_test.py::ExampleTest::example_test_foo
The `pytest-cache` plugin remembers which tests failed last time you ran `pytest`, so using the flag `--ff` will tell `pytest` to run previously failed tests **first**, then continue with the remainder of the tests.
This might speed up your testing if you are making a lot of smaller fixes around one particular task or set of inputs.
-
```bash
$(my_venv) python3 -m pytest -o markers=task --ff
==================== 7 passed in 503s ====================
@@ -192,7 +199,6 @@ This will test your solution.
When `pytest` encounters a failed test, the program will stop and tell you which test failed.
When you make fixes and run the test again, `pytest` will first run the previous test that failed, then continue with the remaining tests.
-
### Using PDB, the Python Debugger, with pytest
If you want to "debug like a pro", you can use the `--pdb` argument after the `pytest` command, and drop into the built-in [Python debugger][pdb], `PDB`.
@@ -206,13 +212,11 @@ When a test fails, dropping into `PDB` will allow you to step through your code
More details on the `PDB` module can be found in the [Python documentation on PDB][pdb].
Additionally, the [pytest docs on PDB][pytest-pdb] and [this guide from Real Python](https://realpython.com/python-debugging-pdb/) are extremely helpful.
-
## Extending your IDE
If you'd like to extend your IDE with some tools that will help you with testing and improving your code, check the [tools](./tools) page.
We explore multiple IDEs, editors and some useful extensions for linting and debugging there.
-
## Additional information
### Adding python to your PATH
@@ -225,10 +229,10 @@ If you do not know where you have installed Python, run the following command in
```bash
$ python3 -c "import os, sys; print(os.path.dirname(sys.executable))"
-{python_directory}
+
```
-The _returned_ directory is where your current active Python version is installed, in this section it is referred to as `{python_directory}`.
+The _returned_ directory is where your current active Python version is installed, in this section it is referred to as ``.
#### Windows
@@ -241,36 +245,35 @@ Then find the `Path` variable in your _User variables_, select it, and click `Ed

-Then add a new line, as shown in the picture, replacing `{python_directory}` with your Python installation's directory:
+Then add a new line, as shown in the picture, replacing `` with your Python installation's directory:

-
#### MacOS/Linux
The below should work for most Linux and MacOS flavors with a `bash` shell.
Commands may vary by Linux distro, and whether a `fish` or `zsh` shell is used.
-Replace `{python_directory}` with the output of `python3 -c "import os, sys; print(os.path.dirname(sys.executable))"`
+Replace `` with the output of `python3 -c "import os, sys; print(os.path.dirname(sys.executable))"`
```bash
-export PATH=β$PATH:{python_directory}}β
+export PATH="$PATH:"
```
[Code Quality: Tools and Best Practices]: https://realpython.com/python-code-quality/
[Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
[configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
[marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
-[pdb]: https://docs.python.org/3.9/library/pdb.html
+[pdb]: https://docs.python.org/3.11/library/pdb.html
[pip]: https://pip.pypa.io/en/stable/getting-started/
[psf-installer]: https://www.python.org/downloads/
[pylint]: https://pylint.pycqa.org/en/latest/user_guide/
-[pytest-cache]:http://pythonhosted.org/pytest-cache/
+[pytest-cache]: http://pythonhosted.org/pytest-cache/
[pytest-pdb]: https://docs.pytest.org/en/6.2.x/usage.html#dropping-to-pdb-python-debugger-on-failures
-[pytest-pylint]:https://github.com/carsongee/pytest-pylint
-[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
+[pytest-pylint]: https://github.com/carsongee/pytest-pylint
+[pytest-subtests]: https://github.com/pytest-dev/pytest-subtests
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
[python command line]: https://docs.python.org/3/using/cmdline.html
[python-m-pip]: https://snarky.ca/why-you-should-use-python-m-pip/
[quick-and-dirty]: https://snarky.ca/a-quick-and-dirty-guide-on-how-to-install-packages-for-python/
-[tutorial from pycqa.org]: https://pylint.pycqa.org/en/v2.17.2/tutorial.html
+[tutorial from pylint.readthedocs.io]: https://pylint.readthedocs.io/en/v2.17.7/tutorial.html
[working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
diff --git a/docs/TOOLS.md b/docs/TOOLS.md
index 20ce04ded09..11713826c14 100644
--- a/docs/TOOLS.md
+++ b/docs/TOOLS.md
@@ -15,6 +15,7 @@ If you have an editor, IDE, tool, or plugin recommendation, we encourage you to
- [Environments](#virtual-environments)
- [Venv](#creating-a-virtual-environment-with-venv)
- [Conda](#creating-a-virtual-environment-using-conda)
+ - [uv](#uv)
- [Virtual Environment Wrapper](#virtual-environment-wrapper)
- [Editors and IDEs](#editors-and-ides)
- [Visual Studio Code](#visual-studio-code)
@@ -30,7 +31,10 @@ If you have an editor, IDE, tool, or plugin recommendation, we encourage you to
Before you start exploring, make sure that you have a recent version of Python installed.
-The Exercism platform currently supports `Python 3.7 - 3.11.2` (_exercises and tests_) and `Python 3.11.2` (_tooling_).
+The Exercism web platform currently supports `Python 3.10 - 3.13.5` (_exercises and tests_) and `Python 3.13.5` (_tooling_).
+Our online test runner currently uses `pytest 8.4.0` and `pytest-subtests 0.14.2`.
+Our online analyzer uses `pylint 4.0.4`.
+Using different versions of `Python`, `pytest`, or `pylint` locally might give you different results than the website.
For more information, please refer to [Installing Python locally][Installing Python locally].
@@ -42,10 +46,16 @@ Python virtual environments offer lightweight runtime and package isolation.
Different environments can hold different versions of the Python runtime together with any project or library dependencies.
This helps avoid bugs and incompatibilities caused by upgrading a library for one project that "breaks" a dependency in a different one.
-There are two major *virtual environment* tools in use today, the Python standard library [`venv`][venv] and the third-party [`conda env`][condaenv], using the [`conda`][conda] package manager and (_usually_) the Anaconda Python distribution.
-Both of are straightforward to use and/or install.
+There are many *virtual environment* and *virtual environment-like* tools in use today, and a growing movement to use [inline script metadata][PEP723], [dev containers][dev containers], or [docker][docker] instead of virtual environments.
+It is important to try out different strategies and find one that suits your development style and project needs.
+Don't assume that the strategies here are one-size-fits all, or are your only options.
+For a more general rundown of tools not covered here, check out [this blog post by Bas Nijholt][bas nihholt] and stay curious.
-Additionally, [`PyEnv`][pyenv] and [virtualenvwrapper][virtualenvwrapper] are tools that can help to manage multiple versions of Python and multiple Python environments on the same machine.
+
+There are three major *virtual environment* tools we'll cover here, the Python standard library [`venv`][venv], the third-party [`conda env`][condaenv], using the [`conda`][conda] package manager and (_usually_) the Anaconda Python distribution, and the third-party `pip` + `venv` replacement [`uv`][uv].
+All three are fairly straightforward to use and/or install.
+
+Additionally, [`PyEnv`][pyenv], [`Poetry`][poetry], and [virtualenvwrapper][virtualenvwrapper] are tools that can help to manage multiple versions of Python and multiple Python environments on the same machine.
@@ -205,6 +215,19 @@ Executing transaction: done
+### UV
+
+Working on Projects with uv: [uv][uv-docs]
+
+`uv` is a Python package + project management tool from [Astral][astral] written in [Rust][rust programming language].
+It was designed to replace `pip`, `venv`, `poetry`, and other Python packaging/dependency management tools.
+Unlike `Conda` or `Mamba`, uv is specialized/tailored to only Python.
+ However, uv does support building Python extension modules written in other languages such as Fortran.
+Astral is also the developer of the popular [Ruff][ruff] linter and [TY][ty], a static type checker for Python type hints, both written in Rust.
+
+
+Rather than go into detail here, the [UV documentation][uv documentation] is your best source for installation and setup.
+
### Virtual Environment wrapper
Documents and background: [virtualenvwrapper][virtualenvwrapper].
@@ -366,14 +389,19 @@ You can also [develop plugins][sublime plugin development] of your own for the e
[Installing Python locally]: https://exercism.org/docs/tracks/Python/installation
[MS Python extension]: https://marketplace.visualstudio.com/items?itemName=ms-python.python
+[PEP723]: https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata
[anaconda]: https://www.anaconda.com/products/individual
+[astral]: https://astral.sh/about
[atom]: https://atom.io/
+[bas nihholt]: https://www.nijho.lt/post/python-environments/
[conda command ref]: https://docs.conda.io/projects/conda/en/latest/commands.html#conda-vs-pip-vs-virtualenv-commands
[conda-cheatsheet]: https://docs.conda.io/projects/conda/en/latest/_downloads/843d9e0198f2a193a3484886fa28163c/conda-cheatsheet.pdf
[conda-docs]: https://docs.conda.io/projects/conda/en/latest/user-guide/index.html
[conda]: https://docs.conda.io/projects/conda/en/latest/index.html
[condaenv]: https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html
+[dev containers]: https://andypickup.com/developing-in-python-with-dev-containers-part-1-setup-f1aeb89cbfed
[docker in vscode]: https://code.visualstudio.com/docs/containers/overview
+[docker]: https://www.docker.com/blog/containerized-python-development-part-1/
[emacs at fullstack python]: https://www.fullstackpython.com/emacs.html
[emacs setup at real python]: https://realpython.com/emacs-the-best-python-editor
[emacs wiki python programming]: https://www.emacswiki.org/emacs/PythonProgrammingInEmacs#h5o-4
@@ -387,6 +415,7 @@ You can also [develop plugins][sublime plugin development] of your own for the e
[linting python in vscode]: https://code.visualstudio.com/docs/python/linting
[miniconda]: https://docs.conda.io/en/latest/miniconda.html
[opensource spacemacs guide]: https://opensource.com/article/19/12/spacemacs
+[poetry]: https://github.com/python-poetry/poetry
[pycharm config venv]: https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html
[pycharm database tools]: https://www.jetbrains.com/help/pycharm/relational-databases.html
[pycharm debug configuration]: https://www.jetbrains.com/help/pycharm/run-debug-configuration-py-test.html
@@ -404,6 +433,8 @@ You can also [develop plugins][sublime plugin development] of your own for the e
[python testing in vscode]: https://code.visualstudio.com/docs/python/testing
[python web dev in vscode]: https://code.visualstudio.com/docs/python/tutorial-django
[rtorr vim cheat sheet]: https://vim.rtorr.com/
+[ruff]: https://docs.astral.sh/ruff/
+[rust programming language]: https://rust-lang.org/
[spacemacs github repo]: https://github.com/syl20bnr/spacemacs
[spacemacs official docs]: https://github.com/syl20bnr/spacemacs#documentation
[spacemacs python layer]: https://www.spacemacs.org/layers/+lang/python/README.html
@@ -431,6 +462,10 @@ You can also [develop plugins][sublime plugin development] of your own for the e
[sublime support docs]: https://www.sublimetext.com/support
[sublime text 4]: https://www.sublimetext.com/
[sublime text at real python]: https://realpython.com/setting-up-sublime-text-3-for-full-stack-python-development/
+[ty]: https://docs.astral.sh/ty/
+[uv documentation]: https://docs.astral.sh/uv/getting-started/
+[uv-docs]: https://docs.astral.sh/uv/guides/projects/#working-on-projects
+[uv]: https://github.com/astral-sh/uv
[venv wrapper tutorial]: https://virtualenvwrapper.readthedocs.io/en/latest/plugins.html#plugins
[venv]: https://docs.python.org/3.9/tutorial/venv.html
[vim cheat sheet]: https://vimsheet.com/
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 5914429db9d..b7a4b010b91 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -415,7 +415,7 @@ def halve_and_quadruple(num):
print((num / 2) * 4)
return (num / 2) * 4
-What the `print` calls revealed is that we used `/` when we should have used `//`, the [floor divison operator][floor divison operator].
+What the `print` calls revealed is that we used `/` when we should have used `//`, the [floor division operator][floor division operator].
## Logging
@@ -487,7 +487,7 @@ AssertionError: divisor must not be 0
```
-If we start reading the Traceback at the bottom (as we should) we quickly see the problem is that `0` should not be passsed as the `divisor`.
+If we start reading the Traceback at the bottom (as we should) we quickly see the problem is that `0` should not be passed as the `divisor`.
`assert` can also be used to test that a value is of the expected type:
diff --git a/docs/config.json b/docs/config.json
index ccaea6ac247..8474ac07ad9 100644
--- a/docs/config.json
+++ b/docs/config.json
@@ -14,20 +14,6 @@
"title": "How to learn Python",
"blurb": "An overview of how to get started from scratch with Python."
},
- {
- "uuid": "7a2e1a4f-1fa8-4327-b700-5af101fcdc89",
- "slug": "test-driven-development",
- "path": "docs/TDD.md",
- "title": "Test Driven Development",
- "blurb": "An overview of Test Driven Development."
- },
- {
- "uuid": "3829fdff-47ac-4283-ae47-a5db1dbce956",
- "slug": "traceback-reading",
- "path": "docs/TRACEBACKS.md",
- "title": "How to read tracebacks",
- "blurb": "An overview of how to read Python tracebacks for debugging."
- },
{
"uuid": "8666f259-de7d-4928-ae6f-15ff6fe6bb74",
"slug": "tests",
@@ -35,6 +21,13 @@
"title": "Testing on the Python track",
"blurb": "Learn how to test your Python exercises on Exercism."
},
+ {
+ "uuid": "7a2e1a4f-1fa8-4327-b700-5af101fcdc89",
+ "slug": "test-driven-development",
+ "path": "docs/TDD.md",
+ "title": "Test Driven Development",
+ "blurb": "An overview of Test Driven Development."
+ },
{
"uuid": "f18d3af2-fb71-41c6-984a-32b3ba86bf02",
"slug": "problem-solving",
@@ -42,6 +35,13 @@
"title": "Problem Solving Resources",
"blurb": "Learn some general problem-solving techniques to help you with programming."
},
+ {
+ "uuid": "3829fdff-47ac-4283-ae47-a5db1dbce956",
+ "slug": "traceback-reading",
+ "path": "docs/TRACEBACKS.md",
+ "title": "How to read tracebacks",
+ "blurb": "An overview of how to read Python tracebacks for debugging."
+ },
{
"uuid": "73ced51d-76d0-45af-952c-8a6d7b5f3f7a",
"slug": "resources",
diff --git a/exercises/concept/black-jack/.docs/instructions.md b/exercises/concept/black-jack/.docs/instructions.md
index 199fabc900e..e95c5fadb9f 100644
--- a/exercises/concept/black-jack/.docs/instructions.md
+++ b/exercises/concept/black-jack/.docs/instructions.md
@@ -67,7 +67,7 @@ Define the `value_of_ace(