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 fe8ff089a67..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.10.6
+ 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]
+ 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.1.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..9f7631e1465 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@271ff311a4947af354c6abcd696a306553b9ec18
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..a70deb6b890 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@f4a6aa5acc07742989788e70fd89cdc0980f0d1e
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 4742a0db0a0..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@v7
+ - 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 691ca9f92fc..6ff557f7087 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,38 +4,41 @@
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.10.6`.
+๐ 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.10.6`.
+๐ 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.
-Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_.
-These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
+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.
+Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
@@ -44,34 +47,38 @@ These two exercise groupings can be found in the track [config.json][config-json
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.
@@ -171,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].
@@ -196,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.10.6` for students completing exercises.
-The track `test runner`, `analyzer`, and `representer` run in docker on `python:3.10.6-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.10.6`.
-- 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.
@@ -223,12 +230,12 @@ 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.
-- Practice Exercise **Test Suits** for many practice exercises are similarly [auto-generated](#auto-generated-files) from data in [problem specifications][problem-specifications].
+- Practice Exercise **Test Suits** for most practice exercises are similarly [auto-generated](#auto-generated-files) from data in [problem specifications][problem-specifications].
- Any changes to them need to be proposed/discussed in the `problem-specifications` repository and approved by **3 track maintainers**, since changes could potentially affect many (_or all_) exercism language tracks.
- If Python-specific test changes become necessary, they can be appended to the exercise `tests.toml` file.
@@ -271,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**)
@@ -370,43 +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
-[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
@@ -428,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 64930816362..20c3bd1ce0c 100644
--- a/README.md
+++ b/README.md
@@ -4,24 +4,43 @@
Exercism Python Track
[](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 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.10.6`.
+๐ Track exercises support Python `3.10` - `3.13.13`.
Exceptions to this support are noted where they occur.
- ๐ Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.10.6`.
+๐ Track tooling (_test-runner, representer, analyzer, and Continuous Integration_) runs on Python `3.13.13`.
-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. Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
+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.
+Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `python/exercises` directory.
@@ -41,30 +60,31 @@ 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.`**
-Please read this [community blog post](https://exercism.org/blog/freeing-our-maintainers) for details.
+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/). Please keep in mind [Chesterton's Fence][chestertons-fence].
-_Thoughtful suggestions will likely result faster & more enthusiastic responses from volunteers._
+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 in faster & more enthusiastic responses from volunteers._
-
โจ ๐ฆ _**Want to jump directly into Exercism specifications & detail?**_
[Structure][exercism-track-structure] **|** [Tasks][exercism-tasks] **|** [Concepts][exercism-concepts] **|** [Concept Exercises][concept-exercises] **|** [Practice Exercises][practice-exercises] **|** [Presentation][exercise-presentation]
[Writing Style Guide][exercism-writing-style] **|** [Markdown Specification][exercism-markdown-specification] (_โจ version in [contributing][website-contributing-section] on exercism.org_)
-
+
+
## 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].
@@ -94,10 +114,11 @@ This repository uses the [MIT License](/LICENSE).
[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
+[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
[the-words-that-we-use]: https://github.com/exercism/docs/blob/main/community/good-member/words.md
[website-contributing-section]: https://exercism.org/docs/building
[zero-clause-bsd]: https://docs.python.org/3/license.html#zero-clause-bsd-license-for-code-in-the-python-release-documentation
diff --git a/bin/data.py b/bin/data.py
index 54dec7612b3..7fce51b9087 100644
--- a/bin/data.py
+++ b/bin/data.py
@@ -4,9 +4,16 @@
from itertools import chain
import json
from pathlib import Path
-import tomli
from typing import List, Any, Dict, Type
+# Tomli was subsumed into Python 3.11.x, but was renamed to to tomllib.
+# This avoids ci failures for Python < 3.11.2.
+try:
+ import tomllib
+except ModuleNotFoundError:
+ import tomli as tomllib
+
+
def _custom_dataclass_init(self, *args, **kwargs):
# print(self.__class__.__name__, "__init__")
@@ -355,7 +362,7 @@ class TestsTOML:
@classmethod
def load(cls, toml_path: Path):
with toml_path.open("rb") as f:
- data = tomli.load(f)
+ data = tomllib.load(f)
return cls({uuid: TestCaseTOML(uuid, *opts) for
uuid, opts in
data.items() if
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 d0406aad5e2..2ad23a9b5f1 100755
--- a/bin/generate_tests.py
+++ b/bin/generate_tests.py
@@ -17,12 +17,13 @@
from githelp import Repo
_py = sys.version_info
-if _py.major < 3 or (_py.major == 3 and _py.minor < 6):
- print("Python version must be at least 3.6")
+if _py.major < 3 or (_py.major == 3 and _py.minor < 7):
+ print("Python version must be at least 3.7")
sys.exit(1)
import argparse
from datetime import datetime
+from datetime import timezone
import difflib
import filecmp
import importlib.util
@@ -34,11 +35,17 @@
from itertools import repeat
from string import punctuation, whitespace
from subprocess import check_call
-import tomli
from tempfile import NamedTemporaryFile
from textwrap import wrap
from typing import Any, Dict, List, NoReturn, Union
+# Tomli was subsumed into Python 3.11.x, but was renamed to to tomllib.
+# This avoids ci failures for Python < 3.11.2.
+try:
+ import tomllib
+except ModuleNotFoundError:
+ import tomli as tomllib
+
from jinja2 import Environment, FileSystemLoader, TemplateNotFound, UndefinedError
from dateutil.parser import parse
@@ -131,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
@@ -197,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]:
"""
@@ -254,6 +263,19 @@ def format_file(path: Path) -> NoReturn:
def check_template(slug: str, tests_path: Path, tmpfile: Path):
+ """Generate a new test file and diff against existing file.
+
+ Note: The timestamp in each test file creates issues with
+ Python difflib, so it is skipped when being prepped
+ for diff.
+
+ You can see this "skipping" on lines 281 & 283.
+ However, this rather crude method creates
+ an empty "false positive" diff. This empty diff is
+ then skipped in lines 293 & 294, so that it can be
+ considered a pass..
+ """
+
try:
check_ok = True
if not tmpfile.is_file():
@@ -264,20 +286,25 @@ def check_template(slug: str, tests_path: Path, tmpfile: Path):
check_ok = False
if check_ok and not filecmp.cmp(tmpfile, tests_path):
with tests_path.open() as f:
- current_lines = f.readlines()
+ current_lines = f.readlines()[3:]
with tmpfile.open() as f:
- rendered_lines = f.readlines()
- diff = difflib.unified_diff(
+ rendered_lines = f.readlines()[3:]
+
+ diff = list(difflib.unified_diff(
current_lines,
rendered_lines,
fromfile=f"[current] {tests_path.name}",
tofile=f"[generated] {tmpfile.name}",
- )
- logger.debug(f"{slug}: ##### DIFF START #####")
- for line in diff:
- logger.debug(line.strip())
- logger.debug(f"{slug}: ##### DIFF END #####")
- check_ok = False
+ lineterm="\n",
+ ))
+ if not diff:
+ check_ok = True
+ else:
+ logger.debug(f"{slug}: ##### DIFF START #####")
+ for line in diff:
+ logger.debug(line.strip())
+ logger.debug(f"{slug}: ##### DIFF END #####")
+ check_ok = False
if not check_ok:
logger.error(
f"{slug}: check failed; tests must be regenerated with bin/generate_tests.py"
@@ -384,9 +411,11 @@ 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
+ env.globals["current_date"] = datetime.now(tz=timezone.utc).date()
env.tests["error_case"] = error_case
result = True
for exercise in sorted(Path("exercises/practice").glob(exercise_glob)):
diff --git a/concepts/basics/.meta/config.json b/concepts/basics/.meta/config.json
index 122161cc814..86bb653b158 100644
--- a/concepts/basics/.meta/config.json
+++ b/concepts/basics/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "Python is a dynamic and strongly typed object-oriented programming language in which variables can be bound and re-bound to any data type. It employs both duck typing and gradual typing (via type hints). Python uses significant indentation to denote code blocks and puts strong emphasis on code readability.",
+ "blurb": "Python is a dynamic and strongly typed programming language in which variables can be bound and re-bound to any data type. It employs both duck typing and gradual typing (via type hints). Python uses significant indentation to denote code blocks and puts strong emphasis on code readability.",
"authors": ["BethanyG"],
"contributors": ["cmccandless", "PaulT89"]
}
diff --git a/concepts/basics/about.md b/concepts/basics/about.md
index ddcc57790a0..6f932bfd16f 100644
--- a/concepts/basics/about.md
+++ b/concepts/basics/about.md
@@ -1,14 +1,14 @@
# basics
-[Python][python docs] is a [dynamic and strongly][dynamic typing in python] typed [object-oriented][object oriented programming] programming language.
+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].
-It supports multiple programming paradigms including both imperative (_object-oriented, procedural_) and declarative (_functional, concurrent_) flavors.
-But do not be fooled: while programming across paradigms is fully _supported_, [everything in Python is an object][everythings an object].
-
-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].
+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.
-The [zen of Python (PEP 20)][the zen of python] and [What is Pythonic?][what is pythonic] lay out additional philosophies.
+
+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].
@@ -19,158 +19,241 @@ 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.
+
+
-## Getting Started
+~~~~exercism/note
-Objects are [assigned][assignment statements] to [names][naming and binding] in Python via the `=` or _assignment operator_. [Variables][variables] are written in [`snake_case`][snake case], and constants usually in `SCREAMING_SNAKE_CASE`.
+In general, content, tests, and analyzer tooling for the Python track follow the style conventions outlined in [PEP 8](https://www.python.org/dev/peps/pep-0008/) and [PEP 257](https://www.python.org/dev/peps/pep-0257/) for Python code style, with the additional (strong) suggestion that there be no single letter variable names.
+
+The [zen of Python (PEP 20)][the zen of python] and [What is Pythonic?][what is pythonic] lay out additional philosophies.
+
+On the Python track, [variables][variables] are always written in [`snake_case`][snake case], and constants in `SCREAMING_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
+~~~~
+
+
+## Name Assignment (Variables & Constants)
+
+In Python, there are no keywords used in creating variables or constants.
+Instead, 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.
+
+For example, `my_first_variable` can be re-assigned many times using `=`, and can refer to different object types with each re-assignment:
-A `name` (_variable or constant_) is not itself typed, and can be attached or re-attached to different objects or values over its lifetime.
-For extended naming conventions and formatting advice, see [PEP 8][pep8].
```python
->>> my_first_variable = 1
->>> my_first_variable = "Last one, I promise"
+>>> my_first_variable = 1 # my_first_variable bound to an integer object of value one.
+>>> my_first_variable = 2 # my_first_variable re-assigned to integer value 2.
+
+>>> print(type(my_first_variable))
+
+
+>>> print(my_first_variable)
+2
+
+>>> my_first_variable = "Now, I'm a string." # <--You may re-bind a name to a different object type and value.
+>>> 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)
+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.
+>>> print(type(my_first_variable))
+
-"Last one, I promise"
+>>> print(my_first_variable)
+Counter({3: 3, 1: 2, 2: 1, 4: 1, 5: 1, 6: 1, 7: 1})
```
-Constants are usually defined on a [module][module] or `global` level, and although they _can_ be changed, they are _intended_ to be assigned only once.
-Their `SCREAMING_SNAKE_CASE` is a message to other developers that the assignment should not be altered.
+### 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.
+
```python
-# All caps signal that this is intended as a constant
+# All caps signal that this is intended as a constant.
MY_FIRST_CONSTANT = 16
# Re-assignment will be allowed by the compiler & interpreter,
-# but is VERY strongly discouraged.
-# Please don't do: MY_FIRST_CONSTANT = "Some other value"
+# but this is VERY strongly discouraged.
+# Please don't do this, it could create problems in your program!
+MY_FIRST_CONSTANT = "Some other value"
```
-In Python, units of functionality are encapsulated in [_functions._][functions], which are themselves [objects][objects] (_it's [turtles all the way down][turtles all the way down]_).
+
+## Functions
+
+In Python, units of functionality are encapsulated in [_functions_][functions], which are themselves [objects][objects] (_it's [turtles all the way down][turtles all the way down]_).
Functions can be executed by themselves, passed as arguments to other functions, nested, or bound to a class.
When functions are bound to a [class][classes] name, they're referred to as [methods][method objects].
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 keyword `def` begins a [function definition][function definition].
-`def` must be followed by the function name and a parenthesized list of zero or more formal [parameters][parameters].
- Parameters can be of several different varieties, and can even [vary][more on functions] in length.
-The `def` line is terminated with a colon (`:`).
+The `def` keyword begins a [function definition][function definition].
+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_:
-Statements for the `function body` begin on the line following `def`, and must be _indented in a block_.
-There is no strict indentation amount (_either space **OR** [tab] characters are acceptable_), but [indentation][indentation] must be _consistent for all indented statements_.
-Functions explicitly return a value or object via the [`return`][return] keyword.
```python
-# Function definition on first line.
->>> def add_two_numbers(number_one, number_two):
-... return number_one + number_two # Returns the sum of the numbers, and is indented by 2 spaces.
+# The body of a 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)
>>> add_two_numbers(3, 4)
7
-```
-
-Functions that do not have an explicit `return` expression will return [`None`][none].
-
-```python
-# This function will return None.
-def add_two_numbers(number_one, number_two):
- result = number_one + number_two
-
->>> print(add_two_numbers(5, 7))
-None
-```
-Inconsistent indentation will raise an error:
-```python
-# The return statement line does not match the first line indent.
+# Inconsistent indentation in your code blocks will raise an error.
>>> def add_three_numbers_misformatted(number_one, number_two, number_three):
-... result = number_one + number_two + number_three # Indented by 4 spaces.
-... return result #this was only indented by 3 spaces
+... result = number_one + number_two + number_three # This was indented by 4 spaces.
+... print(result) # <--This was only indented by 3 spaces.
+...
+...
File "", line 3
- return result
- ^
+ print(result)
+ ^
IndentationError: unindent does not match any outer indentation level
```
-Functions are [_called_][calls] using their name followed by `()`.
-The number of arguments passed in the parentheses must match the number of parameters in the original function definition unless [default arguments][default arguments] have been used.
+
+Functions _explicitly_ return a value or object via the [`return`][return] keyword:
+
```python
->>> def number_to_the_power_of(number_one, number_two):
- """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
-
- Takes number_one and raises it to the power of number_two, returning the result.
- """
+# Function definition on first line, explicit return used on final line.
+def add_two_numbers(number_one, number_two):
+ return number_one + number_two
-... return number_one ** number_two
+# Calling the function in the Python shell returns the sum of the numbers.
+>>> add_two_numbers(3, 4)
+7
->>> number_to_the_power_of(3,3)
-27
+# 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
```
-A mis-match between parameters and arguments will raise an error:
+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 concept.
+For the purposes of this exercise and explanation, `None` is a placeholder that represents nothing, or null:
+
```python
->>> number_to_the_power_of(4,)
-Traceback (most recent call last):
- File "", line 1, in
-TypeError: number_to_the_power_of() missing 1 required positional argument: 'number_two'
+# 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
```
-Adding a [default value][default arguments] for a parameter can defend against such errors:
+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
-def number_to_the_power_of_default(number_one, number_two=2):
- """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
-
- Takes number_one and raises it to the power of number_two, returning the result.
- """
- return number_one ** number_two
+# 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
->>> number_to_the_power_of_default(4)
-16
+# 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
```
-Methods bound to class names are invoked via dot notation (`.()`), as are functions, constants, or global names imported as part of a module.:
+
+### Calling Functions
+
+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 raise_to_power(number, power):
+... return number ** power
+...
-import string
+>>> raise_to_power(3,3) # Invoking the function with the arguments 3 and 3.
+27
-# This is a constant provided by the *string* module.
->>> print(string.ascii_lowercase)
-"abcdefghijklmnopqrstuvwxyz"
-# This is a method call of the str *class*.
+# A mis-match between the number of parameters and the number of arguments will raise an error.
+>>> raise_to_power(4,)
+...
+Traceback (most recent call last):
+ File "", line 1, in
+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 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."
->>> str.upper(start_text)
-"MY SILLY SENTENCE FOR EXAMPLES."
+>>> start_text.upper() # <--Calling the upper() method on start_text directly.
+'MY SILLY SENTENCE FOR EXAMPLES.'
-# This is a method call of an *instance* of the str *class*.
->>> start_text.upper()
-"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
```
+
+## Comments
+
[Comments][comments] in Python start with a `#` that is not part of a string, and end at line termination.
-Unlike many other programming languages, Python does not support multi-line comment marks.
+Unlike many other programming languages, Python **does not support** multi-line comment marks.
Each line of a comment block must start with the `#` character.
+
Comments are ignored by the interpreter:
+
```python
# This is a single line comment.
@@ -181,35 +264,76 @@ x = "foo" # This is an in-line comment.
# these should be used sparingly.
```
+
+## Docstrings
+
The first statement of a function body can optionally be a [_docstring_][docstring], which concisely summarizes the function or object's purpose.
-Docstrings are read by automated documentation tools and are returned by calling `.__doc__` on the function, method, or class name.
-They are recommended for programs of any size where documentation is needed, and their conventions are laid out in [PEP257][PEP257]:
+Docstrings are declared using triple double quotes (""") indented at the same level as the code block:
+
+
+```python
+# An example from PEP257 of a multi-line docstring
+# reformatted to use Google style non-type hinted docstrings.
+# Some additional details can be found in the Sphinx documentation:
+# https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#getting-started
+
+def complex(real=0.0, imag=0.0):
+ """Form a complex number.
+
+ Keyword Arguments:
+ real (float): The real part of the number (default 0.0)
+ imag (float): The imaginary part of the number (default 0.0)
+
+ """
+
+ if imag == 0.0 and real == 0.0:
+ return complex_zero
+
+```
+
+
+Docstrings are read by automated documentation tools and are returned by calling the special attribute `.__doc__` on the function, method, or class name.
+They are recommended for programs of any size where documentation is needed, and their conventions are laid out in [PEP257][pep257].
+
+Docstrings can also function as [lightweight unit tests][doctests], which can be read and run by PyTest, or by importing the `doctest` module.
+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):
+# An example on a user-defined function using a Google style docstring.
+>>> def raise_to_power(number, power):
"""Raise a number to an arbitrary power.
+
+ Parameters:
+ number (int): The base number.
+ power (int): The power to raise the base number to.
- :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
+ Returns:
+ int: The 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
+...
->>> print(number_to_the_power_of.__doc__)
+# Calling the .__doc__ attribute of the function and printing the result.
+>>> 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
+Parameters:
+ number (int): The base number.
+ power (int): The power to raise the base number to.
- Takes number_one and raises it to the power of number_two, returning the result.
+Returns:
+ int: The number raised to the specified power.
-# __doc__() for the built-in type: str.
+Takes a number and raises it to the specified power, returning the result.
+
+...
+
+# Printing the __doc__ attribute of the built-in type: str.
>>> print(str.__doc__)
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str
@@ -219,37 +343,29 @@ errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
-encoding defaults to sys.getdefaultencoding().
+encoding defaults to 'utf-8'.
errors defaults to 'strict'.
```
-Docstrings can also include [doctests][doctests], which are interactive examples of how a method or function should work.
-Doctests can be read and run by PyTest, or by importing the `doctest` module.
[PEP257]: https://www.python.org/dev/peps/pep-0257/
-[assignment statements]: https://docs.python.org/3/reference/simple_stmts.html#assignment-statements
[calls]: https://docs.python.org/3/reference/expressions.html#calls
[classes]: https://docs.python.org/3/reference/datamodel.html#classes
[comments]: https://realpython.com/python-comments-guide/#python-commenting-basics
-[default arguments]: https://docs.python.org/3/tutorial/controlflow.html#default-argument-values
[docstring]: https://docs.python.org/3/tutorial/controlflow.html#tut-docstrings
[doctests]: https://docs.python.org/3/library/doctest.html
[duck typing]: https://en.wikipedia.org/wiki/Duck_typing
[dynamic typing in python]: https://stackoverflow.com/questions/11328920/is-python-strongly-typed
[everythings an object]: https://docs.python.org/3/reference/datamodel.html
+[facts-and-myths-about-python-names]: https://nedbatchelder.com/text/names.html
[function definition]: https://docs.python.org/3/tutorial/controlflow.html#defining-functions
[functions]: https://docs.python.org/3/reference/compound_stmts.html#function
[gradual typing]: https://en.wikipedia.org/wiki/Gradual_typing
-[indentation]: https://docs.python.org/3/reference/lexical_analysis.html#indentation
[method objects]: https://docs.python.org/3/c-api/method.html#method-objects
[module]: https://docs.python.org/3/tutorial/modules.html
-[more on functions]: https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions
-[naming and binding]: https://docs.python.org/3/reference/executionmodel.html#naming-and-binding
[none]: https://docs.python.org/3/library/constants.html
-[object oriented programming]: https://en.wikipedia.org/wiki/Object-oriented_programming
[objects]: https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy
[parameters]: https://docs.python.org/3/glossary.html#term-parameter
-[pep8]: https://www.python.org/dev/peps/pep-0008/
[peps]: https://www.python.org/dev/peps/
[psf membership]: https://www.python.org/psf/membership/
[psf]: https://www.python.org/psf/
@@ -262,9 +378,5 @@ Doctests can be read and run by PyTest, or by importing the `doctest` module.
[python tutorial]: https://docs.python.org/3/tutorial/index.html
[return]: https://docs.python.org/3/reference/simple_stmts.html#return
[significant indentation]: https://docs.python.org/3/reference/lexical_analysis.html#indentation
-[snake case]: https://en.wikipedia.org/wiki/Snake_case
-[the zen of python]: https://www.python.org/dev/peps/pep-0020/
[turtles all the way down]: https://en.wikipedia.org/wiki/Turtles_all_the_way_down
[type hints]: https://docs.python.org/3/library/typing.html
-[variables]: https://realpython.com/python-variables/
-[what is pythonic]: https://blog.startifact.com/posts/older/what-is-pythonic.html
diff --git a/concepts/basics/introduction.md b/concepts/basics/introduction.md
index ca69d5af68b..cb61a0184ab 100644
--- a/concepts/basics/introduction.md
+++ b/concepts/basics/introduction.md
@@ -1,230 +1,201 @@
# Introduction
-[Python][python docs] is a [dynamic and strongly][dynamic typing in python] typed [object-oriented][object oriented programming] programming language.
-It employs both [duck typing][duck typing] and [gradual typing][gradual typing] via [type hints][type hints].
-It supports multiple programming paradigms including both imperative (_object-oriented, procedural_) and declarative (_functional, concurrent_) flavors.
+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 puts a strong emphasis on code readability and (_similar to Haskell_) uses [significant indentation][significant indentation] for function, method, and class definitions.
-The [zen of Python (PEP 20)][the zen of python] and [What is Pythonic?][what is pythonic] lay out additional philosophies.
+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]**.
+
+We'll dig more into what all of that means as we continue through the Python track concepts.
+
+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:
-Objects are [assigned][assignment statements] to [names][naming and binding] via the _assignment operator_, `=`.
-[Variables][variables] are written in [`snake_case`][snake case], and _constants_ usually in `SCREAMING_SNAKE_CASE`.
-A `name` (_variable or constant_) is not itself _typed_, and can be attached or re-attached to different objects over its lifetime.
-For extended naming conventions and advice, see [PEP 8][pep8].
```python
->>> my_first_variable = 1
->>> my_first_variable = "Last one, I promise"
+>>> my_first_variable = 1 # <--my_first_variable bound to an integer object of value one.
+>>> my_first_variable = 2 # <--my_first_variable re-assigned to integer value 2.
+
+>>> print(type(my_first_variable))
+
+
>>> print(my_first_variable)
+2
+
+>>> my_first_variable = "Now, I'm a string." # <--You may re-bind a name to a different object type and value.
+>>> print(type(my_first_variable))
+
-"Last one, I promise"
+>>> my_first_variable = 'You can call me "str".' # <--Strings can be declared using single or double quote marks.
+>>> print(my_first_variable)
+You can call me "str".
```
-Constants are typically defined on a [module][module] or _global_ level, and although they _can_ be changed, they are _intended_ to be named only once.
-Their `SCREAMING_SNAKE_CASE` is a message to other developers that the assignment should not be altered:
-```python
-# All caps signal that this is intended as a constant.
-MY_FIRST_CONSTANT = 16
+### Constants
-# Re-assignment will be allowed by the compiler & interpreter,
-# but this is VERY strongly discouraged.
-# Please don't do: MY_FIRST_CONSTANT = "Some other value"
-```
+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.
-The keyword `def` begins a [function definition][function definition].
-It must be followed by the function name and a parenthesized list of zero or more formal [parameters][parameters].
- Parameters can be of several different varieties, and can even [vary][more on functions] in length.
-The `def` line is terminated with a colon.
-Statements for the _body_ of the function begin on the line following `def`, and must be _indented in a block_.
-There is no strict indentation amount (_either space **OR** [tab] characters are acceptable_), but [indentation][indentation] must be _consistent for all indented statements_.
-Functions explicitly return a value or object via the [`return`][return] keyword.
-```python
-# Function definition on first line.
-def add_two_numbers(number_one, number_two):
- return number_one + number_two # Returns the sum of the numbers, and is indented by 2 spaces.
+## Functions
->>> add_two_numbers(3, 4)
-7
-```
+The `def` keyword begins a [function definition][function definition].
+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_.
-Functions that do not have an explicit `return` expression will return [`None`][none].
```python
-# This function will return None.
+# The body of this function is indented by 2 spaces & prints the sum of the numbers.
def add_two_numbers(number_one, number_two):
- result = number_one + number_two
+ total = number_one + number_two
+ print(total)
->>> print(add_two_numbers(5, 7))
-None
-```
+>>> add_two_numbers(3, 4)
+7
-Inconsistent indentation will raise an error:
-```python
-# The return statement line does not match the first line indent.
+# Inconsistent indentation in your code blocks will raise an error.
>>> def add_three_numbers_misformatted(number_one, number_two, number_three):
-... result = number_one + number_two + number_three # Indented by 4 spaces.
-... return result #this was only indented by 3 spaces
+... result = number_one + number_two + number_three # This was indented by 4 spaces.
+... print(result) # <--This was only indented by 3 spaces.
+...
+...
File "", line 3
- return result
- ^
+ print(result)
+ ^
IndentationError: unindent does not match any outer indentation level
```
-Functions are [_called_][calls] using their name followed by `()`.
-The number of arguments passed in the parentheses must match the number of parameters in the original function definition unless [default arguments][default arguments] have been used:
-```python
-def number_to_the_power_of(number_one, number_two):
- """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
-
- Takes number_one and raises it to the power of number_two, returning the result.
- """
+Functions _explicitly_ return a value or object via the [`return`][return] keyword:
- return number_one ** number_two
->>> number_to_the_power_of(3,3)
-27
-```
+```python
+# Function definition on first line, explicit return used on final line.
+def add_two_numbers(number_one, number_two):
+ return number_one + number_two
-A mis-match between parameters and arguments will raise an error:
-```python
->>> number_to_the_power_of(4,)
-Traceback (most recent call last):
- File "", line 1, in
-TypeError: number_to_the_power_of() missing 1 required positional argument: 'number_two'
+# Calling the function in the Python shell returns the sum of the numbers.
+>>> add_two_numbers(3, 4)
+7
+# 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
```
-Adding a [default value][default arguments] for a parameter can defend against such errors:
+
+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 concept.
+For the purposes of this exercise and explanation, `None` is a placeholder that represents nothing, or null:
+
```python
-def number_to_the_power_of_default(number_one, number_two=2):
- """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
-
- Takes number_one and raises it to the power of number_two, returning the result.
- """
-
- return number_one ** number_two
+# 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)
+>>>
+
->>> number_to_the_power_of_default(4)
-16
+# Using print() with the function call shows that
+# the function is actually returning the **None** object.
+>>> print(square_a_number(2))
+None
```
-Methods bound to class names are invoked via dot notation (.), as are functions, constants, or global names imported as part of a module.:
+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
-import string
+>>> add_two_numbers(5, 7)
+>>> print(add_two_numbers(5, 7))
+None
-# This is a constant provided by the *string* module.
->>> print(string.ascii_lowercase)
-"abcdefghijklmnopqrstuvwxyz"
+# 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
+```
-# This is a method call of the str *class*.
->>> start_text = "my silly sentence for examples."
->>> str.upper(start_text)
-"MY SILLY SENTENCE FOR EXAMPLES."
-# This is a method call of an *instance* of the str *class*.
->>> start_text.upper()
-"MY SILLY SENTENCE FOR EXAMPLES."
-```
+## Comments
[Comments][comments] in Python start with a `#` that is not part of a string, and end at line termination.
-Unlike many other programming languages, Python does not support multi-line comment marks.
+Unlike many other programming languages, Python **does not support** multi-line comment marks.
Each line of a comment block must start with the `#` character.
-Comments are ignored by the interpreter:
-```python
-# This is a single line comment.
-x = "foo" # This is an in-line comment.
-
-# This is a multi-line
-# comment block over multiple lines --
-# these should be used sparingly.
-```
+## Docstrings
The first statement of a function body can optionally be a [_docstring_][docstring], which concisely summarizes the function or object's purpose.
-Docstrings are read by automated documentation tools and are returned by calling `.__doc__()` on the function, method, or class name.
-They can also function as [lightweight unit tests][doctests], which will be covered in a later exercise.
-They are recommended for programs of any size where documentation is needed, and their conventions are laid out in [PEP257][PEP257]:
+Docstrings are read by automated documentation tools such as [Sphinx][sphinx] and are returned by calling the special attribute `.__doc__` on the function, method, or class name.
+General docstring conventions are laid out in [PEP257][pep257], but exact formats will vary by project and team.
+Docstrings are declared using triple double quotes (""") indented at the same level as the code block:
```python
-# An example on a user-defined function.
-def number_to_the_power_of(number_one, number_two):
- """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
-
- Takes number_one and raises it to the power of number_two, returning the result.
+# An example from PEP257 of a multi-line docstring
+# reformatted to use Google style non-type hinted docstrings.
+# Some additional details can be found in the Sphinx documentation:
+# https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#getting-started
+def complex(real=0.0, imag=0.0):
+ """Form a complex number.
+
+ Keyword Arguments:
+ real (float): The real part of the number (default 0.0)
+ imag (float): The imaginary part of the number (default 0.0)
+
"""
- return number_one ** number_two
-
->>> print(number_to_the_power_of.__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
-
- Takes number_one and raises it to the power of number_two, returning the result.
+ if imag == 0.0 and real == 0.0:
+ return complex_zero
+```
-# __doc__() for the built-in type: str.
->>> print(str.__doc__)
-str(object='') -> str
-str(bytes_or_buffer[, encoding[, errors]]) -> str
+Docstrings can also function as [lightweight unit tests][doctests], which will be covered in a later concept.
-Create a new string object from the given object. If encoding or
-errors is specified, then the object must expose a data buffer
-that will be decoded using the given encoding and error handler.
-Otherwise, returns the result of object.__str__() (if defined)
-or repr(object).
-encoding defaults to sys.getdefaultencoding().
-errors defaults to 'strict'.
-```
-[PEP257]: https://www.python.org/dev/peps/pep-0257/
-[assignment statements]: https://docs.python.org/3/reference/simple_stmts.html#assignment-statements
-[calls]: https://docs.python.org/3/reference/expressions.html#calls
[comments]: https://realpython.com/python-comments-guide/#python-commenting-basics
-[default arguments]: https://docs.python.org/3/tutorial/controlflow.html#default-argument-values
[docstring]: https://docs.python.org/3/tutorial/controlflow.html#tut-docstrings
[doctests]: https://docs.python.org/3/library/doctest.html
[duck typing]: https://en.wikipedia.org/wiki/Duck_typing
[dynamic typing in python]: https://stackoverflow.com/questions/11328920/is-python-strongly-typed
+[everythings an object]: https://docs.python.org/3/reference/datamodel.html
+[facts-and-myths-about-python-names]: https://nedbatchelder.com/text/names.html
[function definition]: https://docs.python.org/3/tutorial/controlflow.html#defining-functions
[gradual typing]: https://en.wikipedia.org/wiki/Gradual_typing
-[indentation]: https://docs.python.org/3/reference/lexical_analysis.html#indentation
[module]: https://docs.python.org/3/tutorial/modules.html
-[more on functions]: https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions
-[naming and binding]: https://docs.python.org/3/reference/executionmodel.html#naming-and-binding
[none]: https://docs.python.org/3/library/constants.html
-[object oriented programming]: https://en.wikipedia.org/wiki/Object-oriented_programming
[parameters]: https://docs.python.org/3/glossary.html#term-parameter
-[pep8]: https://www.python.org/dev/peps/pep-0008/
-[python docs]: https://docs.python.org/3/
+[pep257]: https://www.python.org/dev/peps/pep-0257/
[return]: https://docs.python.org/3/reference/simple_stmts.html#return
[significant indentation]: https://docs.python.org/3/reference/lexical_analysis.html#indentation
-[snake case]: https://en.wikipedia.org/wiki/Snake_case
-[the zen of python]: https://www.python.org/dev/peps/pep-0020/
+[sphinx]: https://www.sphinx-doc.org/en/master/usage/index.html
[type hints]: https://docs.python.org/3/library/typing.html
-[variables]: https://realpython.com/python-variables/
-[what is pythonic]: https://blog.startifact.com/posts/older/what-is-pythonic.html
diff --git a/concepts/basics/links.json b/concepts/basics/links.json
index 0c7752279da..1d1d640c9e7 100644
--- a/concepts/basics/links.json
+++ b/concepts/basics/links.json
@@ -1,27 +1,23 @@
[
{
- "url": "https://docs.python.org/3/",
- "description": "Python documentation"
+ "url": "https://lerner.co.il/2019/06/18/understanding-python-assignment/",
+ "description": "Reuven Lerner: Understanding Python Assignment"
},
{
- "url": "https://www.python.org/dev/peps/pep-0020/",
- "description": "The Zen of Python (PEP 20)"
+ "url": "https://www.youtube.com/watch?v=owglNL1KQf0",
+ "description": "Sentdex (YouTube): Python 3 Programming Tutorial - Functions"
},
{
- "url": "https://www.python.org/dev/peps/pep-0008/",
- "description": "PEP 8"
+ "url": "https://realpython.com/documenting-python-code/#commenting-vs-documenting-code",
+ "description": "Real Python: Commenting vs Documenting Code."
},
{
- "url": "https://www.python.org/psf-landing/",
- "description": "Python Software Foundation"
+ "url": "https://www.pythonmorsels.com/everything-is-an-object/",
+ "description": "Python Morsels: Everything is an Object"
},
{
- "url": "https://www.python.org/dev/peps/",
- "description": "Python Enhancement Proposals or PEPs"
- },
- {
- "url": "https://docs.python.org/3/reference/datamodel.html",
- "description": "everything in Python 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",
@@ -36,59 +32,11 @@
"description": "significant indentation"
},
{
- "url": "https://realpython.com/python-variables/",
- "description": "variables in Python"
- },
- {
- "url": "https://docs.python.org/3/reference/simple_stmts.html#assignment-statements",
- "description": "assignment statements in Python"
- },
- {
- "url": "https://docs.python.org/3/reference/executionmodel.html#naming-and-binding",
- "description": "naming and binding in Python"
- },
- {
- "url": "https://docs.python.org/3/tutorial/controlflow.html#defining-functions",
- "description": "function definition"
- },
- {
- "url": "https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions",
- "description": "more on defining functions"
- },
- {
- "url": "https://docs.python.org/3/reference/compound_stmts.html#function",
- "description": "functions in Python"
- },
- {
- "url": "https://docs.python.org/3/reference/datamodel.html#classes",
- "description": "class in Python"
- },
- {
- "url": "https://docs.python.org/3/c-api/method.html#method-objects",
- "description": "methods in Python"
- },
- {
- "url": "https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy",
- "description": "Pythons standard type hierarchy"
- },
- {
- "url": "https://docs.python.org/3/reference/expressions.html#calls",
- "description": "calls"
- },
- {
- "url": "https://docs.python.org/3/tutorial/controlflow.html#default-argument-values",
- "description": "default arguments"
- },
- {
- "url": "https://realpython.com/python-comments-guide/#python-commenting-basics",
- "description": "Comments"
- },
- {
- "url": "https://docs.python.org/3/tutorial/controlflow.html#tut-docstrings",
- "description": "docstring"
+ "url": "https://www.digitalocean.com/community/tutorials/how-to-write-doctests-in-python",
+ "description": "DigitalOcean: How to Write Doctests in Python."
},
{
- "url": "https://docs.python.org/3/library/doctest.html",
- "description": "doctests"
+ "url": "https://nedbatchelder.com/blog/201803/is_python_interpreted_or_compiled_yes.html",
+ "description": "Ned Batchelder: Is Python Interpreted or Compiled? Yes."
}
]
diff --git a/concepts/binary-octal-hexadecimal/.meta/config.json b/concepts/binary-octal-hexadecimal/.meta/config.json
new file mode 100644
index 00000000000..6e2a15b607a
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/.meta/config.json
@@ -0,0 +1,4 @@
+{
+ "blurb": "Other numerical systems in Python: binary (0b11), octal (0o71), and hex (0xFF)",
+ "authors": ["BethanyG", "meatball133"]
+}
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
new file mode 100644
index 00000000000..67646aed2f2
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -0,0 +1,226 @@
+# Binary, Octal, and Hexadecimal
+
+Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+Binary is base 2, octal is base 8, and hexadecimal is base 16.
+Normal integers are base 10 in python.
+Binary, octal, and hexadecimal are all representations of integers.
+Which means that they represent positive and negative numbers (_including zero_) without fractions or decimals, and support all the operations that we can do with integers.
+
+## Binary
+
+[Binary][binary] is a base 2 numeral system, using only the digits 0 and 1.
+It commonly represents the 0 ("off") and 1 ("on") states of electrical flow through transistors and switches in computers, as well as the positive and negative charges in magnetic storage media.
+Binary can represent all the integers that are used in base 10.
+
+A snippet from the base 2 system looks like this, although it continues infinitely and doesn't stop at 128:
+
+| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
+| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
+| 2 \*\* 7 | 2 \*\* 6 | 2 \*\* 5 | 2 \*\* 4 | 2 \*\* 3 | 2 \*\* 2 | 2 \*\* 1 | 2 \*\* 0 |
+
+So if we want to represent the number 6 in binary, it would be 110.
+
+| Place value | 4 | 2 | 1 |
+| ------------- | --- | --- | --- |
+| Binary number | 1 | 1 | 0 |
+
+And the operation would be: `4 + 2 + 0 = 6`
+
+Another example: 19
+
+| Place value | 16 | 8 | 4 | 2 | 1 |
+| ------------- | --- | --- | --- | --- | --- |
+| Binary number | 1 | 0 | 0 | 1 | 1 |
+
+The binary number would be: 10011
+And the operation would be: `16 + 0 + 0 + 2 + 1 = 19`
+
+## Binary in Python
+
+In Python, we can represent binary literals using the `0b` prefix.
+If we write `0b10011`, Python will interpret it as a binary number and convert it to base 10.
+
+```python
+>>> 0b10011
+19
+
+>>> type(0b10011)
+
+```
+
+Binary in Python is just a different way of writing an integer and so the binary representation **is an integer** for all mathematical operations.
+
+If you write a number with a `0b` prefix that is not in the binary system, it will raise a `SyntaxError`.
+
+```python
+Traceback (most recent call last):
+ File "c:\binary.py", line 1, in
+ 0b10211
+SyntaxError: invalid digit '2' in binary literal
+```
+
+### Operations with Binary Numbers
+
+Since binary numbers are integers, we can perform all operations on them that we can with integers.
+
+```python
+# addition
+>>> 0b10011 + 0b10011
+38
+
+# multiplication
+>>> 0b10011 * 0b10011
+361
+```
+
+We can also perform operations between both binary and integer representations.
+However, the usual mathematical operator rules apply: dividing two binary numbers or integer numbers will return a `float`, even if the division does not result in a decimal portion.
+
+```python
+>>> 0b10011 + 19
+38
+
+>>> 0b10011/0b10011
+1.0
+
+>>> 0b10011/3
+6.333333333333333
+```
+
+
+### Converting to and from Binary Representation
+
+Python will automatically convert a binary literal into `int`.
+ To convert an `int` into a binary representation, use the built-in [`bin()`][bin] function.
+`bin()` will return a `str` of the binary equivalent with the prefix `0b` .
+
+```python
+>>> bin(19)
+'0b10011'
+```
+
+To convert a binary literal to an integer, we can use the built-in `int()` function, and pass a string of the binary representation and a base argument:
+
+```python
+>>> int("0b10011", 2)
+19
+```
+
+Giving the wrong base (_or an invalid binary representation_) will raise a `ValueError`:
+
+```python
+Traceback (most recent call last):
+ File "c:\binary.py", line 4, in
+ int("0b10011", 3)
+ValueError: invalid literal for int() with base 3: '0b10011'
+```
+
+### Binary Methods
+
+There are also some special [methods][numeral-systems] that we can use on binary numbers.
+
+
+[`.bit_length()`][bit_length] will return the number of bits that are needed to represent the number:
+
+```python
+>>> 0b11011.bit_length()
+5
+```
+
+
+[`.bit_count()`][bit_count] will return the number of **ones** in the binary number.
+For example, `bit_count()` on '0b11011' will return 4:
+
+```python
+>>> 0b11011.bit_count()
+4
+```
+
+
+~~~~exercism/note
+If you are working locally, `bit_count()` requires at least Python 3.10.
+The Exercism online editor currently supports all features through Python 3.11.
+~~~~
+
+
+## Octal
+
+[Octal][octal] is a base 8 numeral system.
+It uses the digits 0, 1, 2, 3, 4, 5, 6, and 7.
+
+In Python, we can represent octal numbers using the `0o` prefix.
+As with binary, Python automatically converts an octal representation to an `int`.
+
+```python
+>>> 0o123
+83
+```
+
+As with binary, octal numbers **are ints** and support all integer operations.
+Prefixing a number with `0o` that is not in the octal system will raise a `SyntaxError`.
+
+ ### Converting to and from Octal Representation
+
+To convert an `int` into an octal representation, you can use the built-in [`oct()`][oct] function.
+This acts similarly to the `bin()` function, returning a string:
+
+```python
+>>> oct(83)
+'0o123'
+```
+
+
+To convert an octal number to an integer, we can use the `int()` function, passing an octal string representation and the base (8) as arguments:
+
+```python
+>>> int("0o123", 8)
+83
+```
+
+As with binary, giving the wrong base will raise a `ValueError`.
+
+## Hexadecimal
+
+[Hexadecimal][hexadecimal] is a base 16 numeral system.
+It uses the digits 0 - 9 and the letters A, B, C, D, E, and F.
+A is 10, B is 11, C is 12, D is 13, E is 14, and F is 15.
+
+We can represent hexadecimal numbers in Python using the `0x` prefix.
+As with binary and octal, Python will automatically convert hexadecimal literals to `int`s.
+
+```python
+>>> 0x123
+291
+```
+
+As with binary and octal โ hexadecimal literals **are ints**, and you can perform all integer operations with them.
+Prefixing a non-hexadecimal number with `0x` will raise a `SyntaxError`.
+
+
+### Converting to and from Hexadecimal Representation
+
+To convert an `int` into a hexadecimal representation, you can use the built-in [`hex()`][hex] function.
+This acts similarly to the `bin()` function, returning a string:
+
+```python
+>>> hex(291)
+'0x123'
+```
+
+
+To convert a hexadecimal representation to an integer, we can use the `int()` function, passing a hexadecimal string with the base (16) as arguments:
+
+```python
+>>> int("0x123", 16)
+291
+```
+
+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
+[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal
+[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
new file mode 100644
index 00000000000..84ff634263d
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/introduction.md
@@ -0,0 +1,11 @@
+# Binary, Octal, Hexadecimal
+
+Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+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 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/binary-octal-hexadecimal/links.json b/concepts/binary-octal-hexadecimal/links.json
new file mode 100644
index 00000000000..8826182cd48
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/links.json
@@ -0,0 +1,10 @@
+[
+ {
+ "url": "https://towardsdatascience.com/binary-hex-and-octal-in-python-20222488cee1",
+ "description": "Binary, octal, hex in python"
+ },
+ {
+ "url": "https://en.wikipedia.org/wiki/Numeral_system",
+ "description": "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..1cd5a237c29 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 [`two's 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 `&`, `|`, `^`, and the shift operators described below 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` 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 them in Python.
+
+
+```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
+[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..07833339ff2 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][how-transistors-work]: 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.
+Python internals take care of (_eventually_) translating all the higher-level data 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
+[how-transistors-work]: https://www.build-electronic-circuits.com/how-transistors-work/
+[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..ed251fab33a 100644
--- a/concepts/bitwise-operators/links.json
+++ b/concepts/bitwise-operators/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://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": "http://example.com/",
- "description": "TODO: add new link (above) and write a short description here of the resource."
+ "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..7015fdfafa4 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`):
@@ -22,10 +22,10 @@ Each of the operators has a different precedence, where `not` is evaluated befor
Brackets can be used to evaluate one part of the expression before the others:
```python
->>>not True and True
+>>> not True and True
False
->>>not (True and False)
+>>> not (True and False)
True
```
@@ -45,25 +45,25 @@ A few `built-ins` are always considered `False` by definition:
```python
->>>bool(None)
+>>> bool(None)
False
->>>bool(1)
+>>> bool(1)
True
->>>bool(0)
+>>> bool(0)
False
->>>bool([1,2,3])
+>>> bool([1,2,3])
True
->>>bool([])
+>>> bool([])
False
->>>bool({"Pig" : 1, "Cow": 3})
+>>> bool({"Pig" : 1, "Cow": 3})
True
->>>bool({})
+>>> bool({})
False
```
@@ -95,10 +95,10 @@ The `bool` type is implemented as a _sub-type_ of _int_.
```python
->>>1 == True
+>>> 1 == True
True
->>>0 == False
+>>> 0 == False
True
```
@@ -106,14 +106,14 @@ However, `bools` are **still different** from `ints`, as noted when comparing th
```python
->>>1 is True
+>>> 1 is True
False
->>>0 is False
+>>> 0 is False
False
```
-> Note: in python >= 3.8, using a literal (such as 1, '', [], or {}) on the _left side_ of `is` will raise a warning.
+> Note: in python >= 3.8, using a literal (such as `1`, `''`, `[]`, or `{}`) on the _left side_ of `is` will raise a warning.
It is considered a [Python anti-pattern][comparing to true in the wrong way] to use the equality operator to compare a boolean variable to `True` or `False`.
@@ -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..9b6a8a0dfb7 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]
@@ -185,10 +185,10 @@ class Demo:
The moment that `