diff --git a/README.md b/README.md
index fd9be64f715..20c3bd1ce0c 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
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)
@@ -34,9 +34,9 @@ Hi. 👋🏽 👋 **We are happy you are here.** 🎉&nb
**`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.10` - `3.13.5`.
+🌟 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.13.5`.
+🌟 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.
diff --git a/concepts/basics/about.md b/concepts/basics/about.md
index 71f30524c4d..6f932bfd16f 100644
--- a/concepts/basics/about.md
+++ b/concepts/basics/about.md
@@ -64,16 +64,16 @@ For example, `my_first_variable` can be re-assigned many times using `=`, and ca
>>> 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.
+>>> 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.
+>>> 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.
+>>> 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))
@@ -102,7 +102,7 @@ MY_FIRST_CONSTANT = "Some other value"
## 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]_).
+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].
@@ -114,7 +114,7 @@ Statements for the _body_ of the function begin on the line following `def` and
```python
-# The body of a function is indented by 2 spaces, & prints the sum of the numbers.
+# 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)
@@ -126,7 +126,7 @@ def add_two_numbers(number_one, number_two):
# 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 # This was indented by 4 spaces.
-... print(result) #this was only indented by 3 spaces
+... print(result) # <--This was only indented by 3 spaces.
...
...
File "", line 3
@@ -157,7 +157,7 @@ def add_two_numbers(number_one, number_two):
```
Functions that do not have an _explicit_ expression following a `return` will _implicitly_ return the [`None`][none] object.
-The details of `None` will be covered in a later exercise.
+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:
@@ -208,7 +208,7 @@ Dot (`.`) notation is used for calling functions defined inside a class or modul
```python
>>> def raise_to_power(number, power):
- return number ** power
+... return number ** power
...
>>> raise_to_power(3,3) # Invoking the function with the arguments 3 and 3.
@@ -225,12 +225,12 @@ 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.
+>>> str.upper(start_text) # <--Calling the upper() method from the built-in str class on start_text.
'MY SILLY SENTENCE FOR EXAMPLES.'
# Because a string is an instance of the str class, methods can also be called on them "directly".
>>> start_text = "my silly sentence for examples."
->>> start_text.upper() # Calling the upper() method on start_text directly.
+>>> start_text.upper() # <--Calling the upper() method on start_text directly.
'MY SILLY SENTENCE FOR EXAMPLES.'
# Alternatively, we can skip the variable assignment (although this gets messy quick).
@@ -239,9 +239,8 @@ TypeError: raise_to_power() missing 1 required positional argument: 'power'
# Importing the math module
-import math
-
->>> math.pow(2,4) # Calling the pow() function from the math module
+>>> import math
+>>> math.pow(2,4) # <--Calling the pow() function from the math module.
16.0
```
@@ -273,14 +272,18 @@ Docstrings are declared using triple double quotes (""") indented at the same le
```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
-# An example from PEP257 of a multi-line docstring.
def complex(real=0.0, imag=0.0):
"""Form a complex number.
- Keyword arguments:
- real -- the real part (default 0.0)
- imag -- the imaginary part (default 0.0)
+ 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:
@@ -297,31 +300,38 @@ Testing and `doctest` will be covered in a later concept.
```python
-# An example on a user-defined function.
+# 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.
-
- :param number: int the base number.
- :param power: int the power to raise the base number to.
- :return: int - number raised to the specified power.
+ """Raise a number to an arbitrary power.
+
+ Parameters:
+ number (int): The base number.
+ power (int): The power to raise the base number to.
+
+ Returns:
+ int: The number raised to the specified power.
+
+ Takes a number and raises it to the specified power, returning the result.
- Takes a number and raises it to the specified power, returning the result.
- """
+ """
- return number ** power
+ return number ** power
...
# 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: int the base number.
- :param power: int the power to raise the base number to.
- :return: int - number raised to the specified power.
+Parameters:
+ number (int): The base number.
+ power (int): The power to raise the base number to.
- Takes a number and raises it to the specified power, returning the result.
+Returns:
+ int: The number raised to the specified power.
+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__)
@@ -333,10 +343,11 @@ 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'.
```
+
[PEP257]: https://www.python.org/dev/peps/pep-0257/
[calls]: https://docs.python.org/3/reference/expressions.html#calls
[classes]: https://docs.python.org/3/reference/datamodel.html#classes
diff --git a/concepts/basics/introduction.md b/concepts/basics/introduction.md
index d44e6c9fd07..cb61a0184ab 100644
--- a/concepts/basics/introduction.md
+++ b/concepts/basics/introduction.md
@@ -25,8 +25,8 @@ A name can be reassigned (or re-bound) to different values (different object typ
```python
->>> 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.
+>>> 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))
@@ -34,11 +34,11 @@ A name can be reassigned (or re-bound) to different values (different object typ
>>> 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.
+>>> 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.
+>>> 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".
```
@@ -60,7 +60,7 @@ Statements for the _body_ of the function begin on the line following `def` and
```python
-# The body of this function is indented by 2 spaces,& prints the sum of the numbers.
+# The body of this function is indented by 2 spaces & prints the sum of the numbers.
def add_two_numbers(number_one, number_two):
total = number_one + number_two
print(total)
@@ -72,7 +72,7 @@ def add_two_numbers(number_one, number_two):
# 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 # This was indented by 4 spaces.
-... print(result) #this was only indented by 3 spaces
+... print(result) # <--This was only indented by 3 spaces.
...
...
File "", line 3
@@ -104,12 +104,11 @@ def add_two_numbers(number_one, number_two):
Functions that do not have an _explicit_ expression following a `return` will _implicitly_ return the [`None`][none] object.
-The details of `None` will be covered in a later exercise.
+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
-
# This function will return `None`
def square_a_number(number):
square = number * number
@@ -131,8 +130,7 @@ Functions that omit `return` will also _implicitly_ return the [`None`][none] o
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
+# This function omits a return keyword altogether.
def add_two_numbers(number_one, number_two):
result = number_one + number_two
@@ -158,29 +156,35 @@ Each line of a comment block must start with the `#` character.
## Docstrings
The first statement of a function body can optionally be a [_docstring_][docstring], which concisely summarizes the function or object's purpose.
-Docstring 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 from PEP257 of a multi-line docstring.
+# 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 -- the real part (default 0.0)
- imag -- the imaginary part (default 0.0)
+ 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
-
```
-[pep257]: https://www.python.org/dev/peps/pep-0257/
+Docstrings can also function as [lightweight unit tests][doctests], which will be covered in a later concept.
+
+
[comments]: https://realpython.com/python-comments-guide/#python-commenting-basics
[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
@@ -190,6 +194,8 @@ def complex(real=0.0, imag=0.0):
[module]: https://docs.python.org/3/tutorial/modules.html
[none]: https://docs.python.org/3/library/constants.html
[parameters]: https://docs.python.org/3/glossary.html#term-parameter
+[pep257]: https://www.python.org/dev/peps/pep-0257/
[return]: https://docs.python.org/3/reference/simple_stmts.html#return
-[type hints]: https://docs.python.org/3/library/typing.html
[significant indentation]: https://docs.python.org/3/reference/lexical_analysis.html#indentation
+[sphinx]: https://www.sphinx-doc.org/en/master/usage/index.html
+[type hints]: https://docs.python.org/3/library/typing.html
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
index a7fca3714e3..67646aed2f2 100644
--- a/concepts/binary-octal-hexadecimal/about.md
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -18,7 +18,7 @@ A snippet from the base 2 system looks like this, although it continues infinite
| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
| 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, it would in binary be: 110
+So if we want to represent the number 6 in binary, it would be 110.
| Place value | 4 | 2 | 1 |
| ------------- | --- | --- | --- |
@@ -41,7 +41,6 @@ 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
>>> 0b10011
19
@@ -86,6 +85,8 @@ However, the usual mathematical operator rules apply: dividing two binary numbe
>>> 0b10011/3
6.333333333333333
+```
+
### Converting to and from Binary Representation
@@ -133,6 +134,9 @@ 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.
@@ -148,7 +152,6 @@ 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
>>> 0o123
83
```
@@ -157,7 +160,6 @@ 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:
@@ -165,6 +167,8 @@ 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:
@@ -175,22 +179,21 @@ To convert an octal number to an integer, we can use the `int()` function, passi
As with binary, giving the wrong base will raise a `ValueError`.
-### Hexadecimal
+## 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`.
+As with binary and octal, Python will automatically convert hexadecimal literals to `int`s.
```python
-# 0x123
>>> 0x123
291
```
-As with binary and octal - hexadecimal literals **are ints**, and you can perform all integer operations.
+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`.
@@ -202,6 +205,8 @@ 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:
diff --git a/concepts/binary-octal-hexadecimal/introduction.md b/concepts/binary-octal-hexadecimal/introduction.md
index a06ac922faf..84ff634263d 100644
--- a/concepts/binary-octal-hexadecimal/introduction.md
+++ b/concepts/binary-octal-hexadecimal/introduction.md
@@ -1,4 +1,4 @@
-# binary, octal, hexadecimal
+# 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.
diff --git a/concepts/bitwise-operators/about.md b/concepts/bitwise-operators/about.md
index a68e5378f12..1cd5a237c29 100644
--- a/concepts/bitwise-operators/about.md
+++ b/concepts/bitwise-operators/about.md
@@ -112,7 +112,7 @@ See the section below for details.
In decimal representation, we distinguish positive and negative numbers by using a `+` or `-` sign to the left of the digits.
Using these symbols at a binary level proved inefficient for digital computing and raised the problem that `+0` is not the same as `-0`.
-Rather than using `-` and `+`, all modern computers use a [`twos-complement`][twos-complement] representation for negative numbers, right down to the silicon chip level.
+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`.
@@ -145,7 +145,7 @@ This is **not** the `0b10011001` we would see in languages with fixed-size integ
The `~` operator only works as expected with _**unsigned**_ byte or integer types, or with fixed-sized integer types.
These numeric types are supported in third-party packages such as [`NumPy`][numpy], [`pandas`][pandas], and [`sympy`][sympy] but not in core Python.
-In practice, Python programmers quite often use the shift operators described below and `& | ^` with positive numbers only.
+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.
@@ -153,13 +153,13 @@ Another strategy is to work with the [`ctypes`][ctypes-module] module, and use c
## [`Shift operators`][bitwise-shift-operators]
-The left-shift operator `x << y` simply moves all the bits in `x` by `y` places to the left, filling the new gaps with zeros.
-Note that this is arithmetically identical to multiplying a number by `2**y`.
+The 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`.
+This is arithmetically identical to integer division `x // (2**y)`.
-Keep in mind the previous section on negative numbers and their pitfalls when shifting.
+Keep in mind the previous section on negative numbers and their pitfalls when shifting them in Python.
```python
@@ -191,7 +191,7 @@ Keep in mind the previous section on negative numbers and their pitfalls when sh
[symmetric-difference]: https://math.stackexchange.com/questions/84184/relation-between-xor-and-symmetric-difference#:~:text=It%20is%20the%20same%20thing,they%20are%20indeed%20the%20same.
[sympy]: https://docs.sympy.org/latest/modules/codegen.html#predefined-types
[tilde]: https://en.wikipedia.org/wiki/Tilde
-[twos-complement]: https://en.wikipedia.org/wiki/Two%27s_complement#:~:text=Two's%20complement%20is%20the%20most,number%20is%20positive%20or%20negative.
+[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 88aba3a6a7b..07833339ff2 100644
--- a/concepts/bitwise-operators/introduction.md
+++ b/concepts/bitwise-operators/introduction.md
@@ -1,19 +1,19 @@
# Introduction
-Down at the hardware level, transistors can only be on or off: two states that we traditionally represent with `1` and `0`.
+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.
-We let the Python internals take care of (eventually) translating everything to bits.
+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
diff --git a/concepts/bitwise-operators/links.json b/concepts/bitwise-operators/links.json
index 7c103c84630..ed251fab33a 100644
--- a/concepts/bitwise-operators/links.json
+++ b/concepts/bitwise-operators/links.json
@@ -1,8 +1,4 @@
[
- {
- "url": "https://wiki.python.org/moin/BitwiseOperators/",
- "description": "BitwiseOperators on the Python wiki."
- },
{
"url": "https://realpython.com/python-bitwise-operators",
"description": "Real Python: Bitwise Operators in Python."
diff --git a/concepts/bools/about.md b/concepts/bools/about.md
index 0b8354750a3..7015fdfafa4 100644
--- a/concepts/bools/about.md
+++ b/concepts/bools/about.md
@@ -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`.
diff --git a/concepts/classes/about.md b/concepts/classes/about.md
index 11b03643543..9b6a8a0dfb7 100644
--- a/concepts/classes/about.md
+++ b/concepts/classes/about.md
@@ -185,10 +185,10 @@ class Demo:
The moment that `.add_two()` is called, and `self.new_var += 2` is read, `new_var` changes from a class variable to an instance variable of the same name.
This can be useful during initialization when all instances of a class will need some attribute(s) to start with the same value.
-However, the instance variable then shadows* the class variable, making the class variable inaccessible from the instance where it is shadowed.
+However, the instance variable then [_shadows_](https://oznetnerd.com/2017/07/17/python-shadowing/) the class variable, making the class variable inaccessible from the instance where it is shadowed.
Given this situation, it may be safer and clearer to set instance attributes from the `__init__()` method as `self.`.
~~~~
-_*[_shadows_][shadowing]
+
## Methods
@@ -240,7 +240,7 @@ class MyClass:
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
- return self.location_x, self.location_y
+ return self.location_x, self.location_y
# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
@@ -267,7 +267,7 @@ class MyClass:
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
- return self.location_x, self.location_y
+ return self.location_x, self.location_y
# Alter class variable number for all instances from within an instance.
def increment_number(self):
@@ -322,4 +322,3 @@ class MyClass:
[dunder]: https://mathspp.com/blog/pydonts/dunder-methods
[oop]: https://www.educative.io/blog/object-oriented-programming
[dot notation]: https://stackoverflow.com/questions/45179186/understanding-the-dot-notation-in-python
-[shadowing]: https://oznetnerd.com/2017/07/17/python-shadowing/
diff --git a/concepts/comparisons/about.md b/concepts/comparisons/about.md
index 1d2c677d22a..9aa681ff9f5 100644
--- a/concepts/comparisons/about.md
+++ b/concepts/comparisons/about.md
@@ -13,7 +13,7 @@ The table below shows the most common Python comparison operators:
| `<=` | "less than or equal to" | `a <= b` is `True` if `a < b` or `a == b` in value |
| `!=` | "not equal to" | `a != b` is `True` if `a == b` is `False` |
| `is` | "identity" | `a is b` is `True` if **_and only if_** `a` and `b` are the same _object_ |
-| `is not` | "negated identity" | `a is not b` is `True` if `a` and `b` are **not** the same _object_ |
+| `is not` | "negated identity" | `a is not b` is `True` if **_and only if_** `a` and `b` are **not** the same _object_ |
| `in` | "containment test" | `a in b` is `True` if `a` is member, subset, or element of `b` |
| `not in` | "negated containment test" | `a not in b` is `True` if `a` is not a member, subset, or element of `b` |
@@ -146,7 +146,7 @@ True
Comparison operators can be chained _arbitrarily_.
Note that the evaluation of an expression takes place from `left` to `right`.
-For example, `x < y <= z` is equivalent to `x < y` `and` `y <= z`, except that `y` is evaluated **only once**.
+For example, `x < y <= z` is equivalent to `x < y and y <= z`, except that `y` is evaluated **only once**.
In both cases, `z` is _not_ evaluated **at all** when `x < y` is found to be `False`.
This is often called `short-circuit evaluation` - the evaluation stops if the truth value of the expression has already been determined.
@@ -180,7 +180,7 @@ Due to their singleton status, `None` and `NotImplemented` should always be comp
See the Python reference docs on [value comparisons][value comparisons none] and [PEP8][PEP8 programming recommendations] for more details on this convention.
```python
->>>
+
# A list of favorite numbers.
>>> my_fav_numbers = [1, 2, 3]
@@ -218,7 +218,7 @@ The operators `in` and `not in` test for _membership_.
For string and bytes types, ` in ` is `True` _**if and only if**_ `` is a substring of ``.
```python
->>>
+
# A set of lucky numbers.
>>> lucky_numbers = {11, 22, 33}
>>> 22 in lucky_numbers
diff --git a/concepts/comparisons/introduction.md b/concepts/comparisons/introduction.md
index e597063c621..40c40ea8a10 100644
--- a/concepts/comparisons/introduction.md
+++ b/concepts/comparisons/introduction.md
@@ -13,7 +13,7 @@ The table below shows the most common Python comparison operators:
| `<=` | "less than or equal to" | `a <= b` is `True` if `a < b` or `a == b` in value |
| `!=` | "not equal to" | `a != b` is `True` if `a == b` is `False` |
| `is` | "identity" | `a is b` is `True` if **_and only if_** `a` and `b` are the same _object_ |
-| `is not` | "negated identity" | `a is not b` is `True` if `a` and `b` are **not** the same _object_ |
+| `is not` | "negated identity" | `a is not b` is `True` if **_and only if_** `a` and `b` are **not** the same _object_ |
| `in` | "containment test" | `a in b` is `True` if `a` is member, subset, or element of `b` |
| `not in` | "negated containment test" | `a not in b` is `True` if `a` is not a member, subset, or element of `b` |
diff --git a/concepts/complex-numbers/about.md b/concepts/complex-numbers/about.md
index dfe067be4ee..2b0de864e7b 100644
--- a/concepts/complex-numbers/about.md
+++ b/concepts/complex-numbers/about.md
@@ -3,7 +3,7 @@
`Complex numbers` are not complicated.
They just need a less alarming name.
-They are so useful, especially in engineering and science, that Python includes [`complex`][complex] as a standard numeric type alongside integers ([`int`s][ints]) and floating-point numbers ([`float`s][floats]).
+They are so useful — especially in engineering and science — that Python includes [`complex`][complex] as a standard numeric type, alongside integers ([`int`s][ints]) and floating-point numbers ([`float`s][floats]).
## Basics
@@ -143,7 +143,7 @@ Any [mathematical][math-complex] or [electrical engineering][engineering-complex
Alternatively, Exercism has a `Complex Numbers` practice exercise where you can implement a complex number class with these operations from first principles.
-Integer division is ___not___ possible on complex numbers, so the `//` and `%` operators and `divmod()` functions will fail for the complex number type.
+Integer division is ___not___ possible on complex numbers, so the `//` and `%` operators and the `divmod()` function will fail for the complex number type.
There are two functions implemented for numeric types that are very useful when working with complex numbers:
@@ -235,13 +235,13 @@ If you are reading this on any sort of screen, you are utterly dependent on some
1. __Semiconductor chips__.
- These make no sense in classical physics and can only be explained (and designed) by quantum mechanics (QM).
- - In QM, everything is complex-valued by definition. (_its waveforms all the way down_)
+ - In QM, everything is complex-valued by definition. (_it's waveforms all the way down_)
-2. __The Fast Fourier Transform algorithm__.
+2. __The Fast Fourier Transform (FFT) algorithm__.
- FFT is an application of complex numbers, and it is in _everything_ connected to sound transmission, audio processing, photos, and video.
- -MP3 and other audio formats use FFT for compression, ensuring more audio can fit within a smaller storage space.
- - JPEG compression and MP4 video, among many other image and video formats also use FTT for compression.
+ - MP3 and other audio formats use FFT for compression, ensuring more audio can fit within a smaller storage space.
+ - JPEG compression and MP4 video, among many other image and video formats, also use FTT for compression.
- FFT is also deployed in the digital filters that allow cellphone towers to separate your personal cell signal from everyone else's.
diff --git a/concepts/complex-numbers/introduction.md b/concepts/complex-numbers/introduction.md
index a82f47cb6cb..419c3f3d486 100644
--- a/concepts/complex-numbers/introduction.md
+++ b/concepts/complex-numbers/introduction.md
@@ -3,7 +3,9 @@
`Complex numbers` are not complicated.
They just need a less alarming name.
-They are so useful, especially in engineering and science (_everything from JPEG compression to quantum mechanics_), that Python includes [`complex`][complex] as a standard numeric type alongside integers ([`int`s][ints]) and floating-point numbers ([`float`s][floats]).
+
+They are so useful — especially in engineering and science — that Python includes [`complex`][complex] as a standard numeric type, alongside integers ([`int`s][ints]) and floating-point numbers ([`float`s][floats]).
+
A `complex` value in Python is essentially a pair of floating-point numbers:
@@ -74,13 +76,13 @@ There are two common ways to create complex numbers.
Most of the [`operators`][operators] used with floats and ints also work with complex numbers.
-Integer division is _**not**_ possible on complex numbers, so the `//` and `%` operators and `divmod()` functions will fail for the complex number type.
+Integer division is _**not**_ possible on complex numbers, so the `//` and `%` operators and the `divmod()` function will fail for the complex number type.
Explaining the rules for complex number multiplication and division is out of scope for this concept (_and you are unlikely to have to perform those operations "by hand" very often_).
Any [mathematical][math-complex] or [electrical engineering][engineering-complex] introduction to complex numbers will cover these scenarios, should you want to dig into the topic.
-The Python standard library has a [`math`][math-module] module full of useful functionality for working with real numbers and the [`cmath`][cmath] module is its equivalent for working with complex numbers.
+The Python standard library has a [`math`][math-module] module full of useful functionality for working with real numbers, and the [`cmath`][cmath] module is its equivalent for working with complex numbers.
[cmath]: https://docs.python.org/3/library/cmath.html
diff --git a/concepts/conditionals/about.md b/concepts/conditionals/about.md
index 2060905b335..8891683f791 100644
--- a/concepts/conditionals/about.md
+++ b/concepts/conditionals/about.md
@@ -56,20 +56,20 @@ else:
[Boolean operations][boolean operations] and [comparisons][comparisons] can be combined with conditionals for more complex testing:
-```python
+```python
>>> def classic_fizzbuzz(number):
- if number % 3 == 0 and number % 5 == 0:
- say = 'FizzBuzz!'
- elif number % 5 == 0:
- say = 'Buzz!'
- elif number % 3 == 0:
- say = 'Fizz!'
- else:
- say = str(number)
+... if number % 3 == 0 and number % 5 == 0:
+... say = 'FizzBuzz!'
+... elif number % 5 == 0:
+... say = 'Buzz!'
+... elif number % 3 == 0:
+... say = 'Fizz!'
+... else:
+... say = str(number)
+...
+... return say
- return say
-
>>> classic_fizzbuzz(15)
'FizzBuzz!'
@@ -83,14 +83,14 @@ However, re-writing in this way might obscure that the conditions are intended t
```python
>>> def classic_fizzbuzz(number):
- if number % 3 == 0 and number % 5 == 0:
- return 'FizzBuzz!'
- if number % 5 == 0:
- return 'Buzz!'
- if number % 3 == 0:
- return 'Fizz!'
-
- return str(number)
+... if number % 3 == 0 and number % 5 == 0:
+... return 'FizzBuzz!'
+... if number % 5 == 0:
+... return 'Buzz!'
+... if number % 3 == 0:
+... return 'Fizz!'
+...
+... return str(number)
>>> classic_fizzbuzz(15)
'FizzBuzz!'
@@ -102,19 +102,20 @@ However, re-writing in this way might obscure that the conditions are intended t
Conditionals can also be nested.
+
```python
>>> def driving_status(driver_age, test_score):
- if test_score >= 80:
- if 18 > driver_age >= 16:
- status = "Student driver, needs supervision."
- elif driver_age == 18:
- status = "Permitted driver, on probation."
- elif driver_age > 18:
- status = "Fully licensed driver."
- else:
- status = "Unlicensed!"
-
- return status
+... if test_score >= 80:
+... if 18 > driver_age >= 16:
+... status = "Student driver, needs supervision."
+... elif driver_age == 18:
+... status = "Permitted driver, on probation."
+... elif driver_age > 18:
+... status = "Fully licensed driver."
+... else:
+... status = "Unlicensed!"
+...
+... return status
>>> driving_status(63, 78)
@@ -130,13 +131,13 @@ Conditionals can also be nested.
## Conditional expressions or "ternary operators"
While Python has no specific `?` ternary operator, it is possible to write single-line `conditional expressions`.
-These take the form of `` if `` else ``.
+These take the form of ` if else `.
Since these expressions can become hard to read, it's recommended to use this single-line form only if it shortens code and helps readability.
```python
-def just_the_buzz(number):
- return 'Buzz!' if number % 5 == 0 else str(number)
+>>> def just_the_buzz(number):
+... return 'Buzz!' if number % 5 == 0 else str(number)
>>> just_the_buzz(15)
'Buzz!'
@@ -152,29 +153,29 @@ Objects that are evaluated in this fashion are considered "truthy" or "falsy", a
```python
>>> def truthy_test(thing):
- if thing:
- print('This is Truthy.')
- else:
- print("Nope. It's Falsey.")
+... if thing:
+... print('This is Truthy.')
+... else:
+... print("Nope. It's Falsy.")
-# Empty container objects are considered Falsey.
+# Empty container objects are considered Falsy.
>>> truthy_test([])
-Nope. It's Falsey.
+"Nope. It's Falsy."
>>> truthy_test(['bear', 'pig', 'giraffe'])
-This is Truthy.
+'This is Truthy.'
-# Empty strings are considered Falsey.
+# Empty strings are considered Falsy.
>>> truthy_test('')
-Nope. It's Falsey.
+"Nope. It's Falsy."
>>> truthy_test('yes')
-This is Truthy.
+'This is Truthy.'
-# 0 is also considered Falsey.
+# 0 is also considered Falsy.
>>> truthy_test(0)
-Nope. It's Falsey.
+"Nope. It's Falsy."
```
[boolean operations]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
diff --git a/concepts/conditionals/introduction.md b/concepts/conditionals/introduction.md
index ee1d4336207..ba4f098493d 100644
--- a/concepts/conditionals/introduction.md
+++ b/concepts/conditionals/introduction.md
@@ -58,16 +58,15 @@ else:
```python
>>> def classic_fizzbuzz(number):
- if number % 3 == 0 and number % 5 == 0:
- say = 'FizzBuzz!'
- elif number % 5 == 0:
- say = 'Buzz!'
- elif number % 3 == 0:
- say = 'Fizz!'
- else:
- say = str(number)
-
- return say
+... if number % 3 == 0 and number % 5 == 0:
+... say = 'FizzBuzz!'
+... elif number % 5 == 0:
+... say = 'Buzz!'
+... elif number % 3 == 0:
+... say = 'Fizz!'
+... else:
+... say = str(number)
+... return say
>>> classic_fizzbuzz(15)
'FizzBuzz!'
@@ -76,6 +75,7 @@ else:
'13'
```
+
[boolean operations]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
[comparisons]: https://docs.python.org/3/library/stdtypes.html#comparisons
[control flow tools]: https://docs.python.org/3/tutorial/controlflow.html#more-control-flow-tools
diff --git a/concepts/dict-methods/about.md b/concepts/dict-methods/about.md
index 6dcf9b4ae7a..7af90a77145 100644
--- a/concepts/dict-methods/about.md
+++ b/concepts/dict-methods/about.md
@@ -1,28 +1,29 @@
# Dictionary Methods in Python
The `dict` class in Python provides many useful [methods][dict-methods] for working with dictionaries.
-Some were introduced in the concept for `dicts`.
+Some were introduced in the concept for `dict`s.
Here we cover a few more - along with some techniques for iterating through and manipulating dictionaries.
-- `dict.setdefault()` automatically adds keys without throwing a KeyError.
-- `dict.fromkeys(iterable, )` creates a new `dict` from any number of iterables.
-- `.keys()`, `.values()`, and `.items()` provide convenient iterators.
-- `sorted(.items())`. can easily re-order entries in a `dict`.
-- `dict_one.update()` updates one `dict` with overlapping values from another `dict`.
-- `dict | other_dict` and `dict |= other_dict` merges or updates two `dict`s via operators.
-- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` produce _reversed_ views.
+- `.setdefault()` automatically adds keys without throwing a KeyError.
+- `.fromkeys(, )` creates a new `dict` from any number of iterables.
+- `.keys()`, `.values()`, and `.items()` provide convenient iterators.
+- `sorted(.items())` can easily re-order entries in a `dict`.
+- `.update()` updates one `dict` with overlapping values from another `dict`.
+- ` | ` and ` |= ` merges or updates two `dict`s via operators.
+- `reversed(.keys())`, `reversed(.values())`, or `reversed(.items())` produce _reversed_ views.
- `.popitem()` removes and returns a `key`, `value` pair.
## `setdefault()` for Error-Free Insertion
-The dictionary concept previously covered that `.get(key, )` returns an existing `value` or the `default value` if a `key` is not found in a dictionary, thereby avoiding a `KeyError`.
+The dictionary concept previously covered that `.get(, )` returns an existing `value` or the `default value` if a `key` is not found in a dictionary, thereby avoiding a `KeyError`.
This works well in situations where you would rather not have extra error handling but cannot trust that a looked-for `key` will be present.
-For a similarly "safe" (_without KeyError_) insertion operation, there is the `.setdefault(key, )` method.
-`setdefault(key, )` will return the `value` if the `key` is found in the dictionary.
+For a similarly "safe" (_without `KeyError`_) insertion operation, there is the `.setdefault(, )` method.
+`.setdefault(, )` will return the `value` if the `key` is found in the dictionary.
If the key is **not** found, it will _insert_ the (`key`, `default value`) pair and return the `default value` for use.
+
```python
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
@@ -38,7 +39,7 @@ If the key is **not** found, it will _insert_ the (`key`, `default value`) pair
## `fromkeys()` to Populate a Dictionary from an Iterable
-To quickly populate a dictionary with various `keys` and default values, the _class method_ [`fromkeys(iterable, )`][fromkeys] will iterate through an iterable of `keys` and create a new `dict`.
+To quickly populate a dictionary with various `keys` and default values, the _class method_ [`fromkeys(, )`][fromkeys] will iterate through an iterable of `keys` and create a new `dict`.
All `values` will be set to the `default value` provided:
```python
@@ -71,13 +72,12 @@ If the dictionary is empty, calling `popitem()` will raise a `KeyError`:
# All (key, value) pairs have been removed.
>>> palette_I.popitem()
Traceback (most recent call last):
-
line 1, in
palette_I.popitem()
-
KeyError: 'popitem(): dictionary is empty'
```
+
## Iterating Over Entries in a Dictionary Via Views
The `.keys()`, `.values()`, and `.items()` methods return [_iterable views_][dict-views] of a dictionary.
@@ -136,7 +136,7 @@ This allows keys, values, or (`key`, `value`) pairs to be iterated over in Last-
('Purple baseline', '#161748')
>>> for item in reversed(palette_II.items()):
-... print (item)
+... print(item)
...
('Purple baseline', '#161748')
('Green Treeline', '#478559')
@@ -166,12 +166,12 @@ This method will take the (`key`,`value`) pairs of `` and write them i
Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be _overwritten_ by the corresponding `value` from `dict_two`:
```python
->>> palette_I = {'Grassy Green': '#9bc400',
- 'Purple Mountains Majesty': '#8076a3',
- 'Misty Mountain Pink': '#f9c5bd',
- 'Factory Stone Purple': '#7c677f',
- 'Green Treeline': '#478559',
- 'Purple baseline': '#161748'}
+>>> palette_I = {'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd',
+ 'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559',
+ 'Purple baseline': '#161748'}
>>> palette_III = {'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
@@ -188,7 +188,7 @@ Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be
'Green Treeline': '#478559', 'Purple baseline': '#161748'}
```
-## Merge or Update Dictionaries Via the Union (`|`) Operators
+## Merge or Update Dictionaries Using Union (`|` and `|=`) Operators
Python 3.9 introduces a different means of merging `dicts`: the `union` operators.
`dict_one | dict_two` will create a **new dictionary**, made up of the (`key`, `value`) pairs of `dict_one` and `dict_two`.
@@ -271,7 +271,7 @@ Unless a _sort key_ is specified, the default sort is over dictionary `keys`.
'Misty Mountain Pink': '#f9c5bd'}
```
-## Transposing a Dictionaries Keys and Values
+## Transposing Dictionary Keys and Values
Swapping keys and values reliably in a dictionary takes a little work, but can be accomplished via a `loop` using `dict.items()` or in a dictionary comprehension.
Safe swapping assumes that `dict` keys and values are both _hashable_.
@@ -333,10 +333,10 @@ If the values stored in the `dict` are not unique, extra checks become necessary
# Iterating over (key, value) pairs using .items()
>>> for key, value in extended_color_reference.items():
-... if value in consolidated_colors: #Check if key has already been created.
+... if value in consolidated_colors: # <--Check if key has already been created.
... consolidated_colors[value].append(key)
... else:
-... consolidated_colors[value] = [key] #Create a value list with the former key in it.
+... consolidated_colors[value] = [key] # <--Create a value list with the former key in it.
>>> consolidated_colors
{'Purple Mountains Majesty': ['#8076a3', (128, 118, 163), (21, 28, 0, 36)],
diff --git a/concepts/dict-methods/introduction.md b/concepts/dict-methods/introduction.md
index c15fbc113de..b1e8eb8f20a 100644
--- a/concepts/dict-methods/introduction.md
+++ b/concepts/dict-methods/introduction.md
@@ -4,13 +4,13 @@ The `dict` class in Python provides many useful [methods][dict-methods], some of
This concept tackles a few more:
-- `dict.setdefault()` automatically adds keys without throwing a `KeyError`.
-- `dict.fromkeys(iterable, )` creates a new `dict` from any number of iterables.
-- `.keys()`, `.values()`, and `.items()` provide convenient iterators.
-- `sorted(.items())`. can easily re-order entries in a `dict`.
-- `dict_one.update()` updates one `dict` with overlapping values from another `dict`.
-- `dict | other_dict` and `dict |= other_dict` merges or updates two `dict`s via operators.
-- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` produce _reversed_ views.
+- `.setdefault()` automatically adds keys without throwing a KeyError.
+- `.fromkeys(, )` creates a new `dict` from any number of iterables.
+- `.keys()`, `.values()`, and `.items()` provide convenient iterators.
+- `sorted(.items())` can easily re-order entries in a `dict`.
+- `.update()` updates one `dict` with overlapping values from another `dict`.
+- ` | ` and ` |= ` merges or updates two `dict`s via operators.
+- `reversed(.keys())`, `reversed(.values())`, or `reversed(.items())` produce _reversed_ views.
- `.popitem()` removes and returns a `key`, `value` pair.
[dict-methods]: https://docs.python.org/3/library/stdtypes.html#dict
diff --git a/concepts/dicts/about.md b/concepts/dicts/about.md
index 1f83fe6e30a..a525f6248c7 100644
--- a/concepts/dicts/about.md
+++ b/concepts/dicts/about.md
@@ -3,9 +3,9 @@
A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
-`Keys` must be hashable and unique across the dictionary.
-Key types can include `numbers`, `str`, or `tuples` (of _immutable_ values).
-They cannot contain _mutable_ data structures such as `lists`, `dict`s, or `set`s.
+`keys` must be hashable and unique across the dictionary.
+Key types can include `number`s, `str`s, or `tuple`s (of _immutable_ values).
+They cannot contain _mutable_ data structures such as `list`s, `dict`s, or `set`s.
As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
`values` can be of any data type or structure.
@@ -20,10 +20,10 @@ Dictionaries are especially useful in scenarios where the collection of items is
## Dictionary Construction
Dictionaries can be created in many different ways, including:
- - Using the [`fromkeys()`][fromkeys] classmethod
- - Creating [dictionary comprehensions][dict-comprehensions]
- - Merging two dictionaries via unpacking (`**`)
- - Merging dictionaries via the `|` (_update_) operator
+ - Using the [`fromkeys()`][fromkeys] class method.
+ - Using [dictionary comprehensions][dict-comprehensions].
+ - Merging two dictionaries via unpacking (`**`).
+ - Merging dictionaries via the `|` (_update_) operator.
- Using a loop to iteratively add entries to a previously created empty `dict`.
The two most straightforward methods are the dictionary _constructor_ and the dictionary _literal_.
@@ -35,17 +35,20 @@ The two most straightforward methods are the dictionary _constructor_ and the di
```python
# Passing a list of key,value tuples.
->>> wombat = dict([('name', 'Wombat'),('speed', 23),
- ('land_animal', True)])
+>>> wombat = dict([('name', 'Wombat'),
+... ('speed', 23),
+... ('land_animal', True)])
{'name': 'Wombat', 'speed': 23, 'land_animal': True}
# Using key=value arguments.
->>> bear = dict(name="Black Bear", speed=40, land_animal=True)
+>>> bear = dict(name="Black Bear",
+... speed=40,
+... land_animal=True)
{'name': 'Black Bear', 'speed': 40, 'land_animal': True}
```
-The [documentation on `dicts`][dicts-docs] outlines additional variations and options in constructor use.
+The [documentation on `dict`s][dicts-docs] outlines additional variations and options in constructor use.
### Dictionary Literals
@@ -74,35 +77,35 @@ Dictionaries can be arbitrarily nested:
```python
animals = {
- "Real" : {
- "Winged" : {
- "Sparrow" : {'name': 'sparrow','speed': 12, 'land_animal': True},
- "Kestrel" : {'name': 'kestrel', 'speed': 15, 'land_animal': True}
- },
- "Legged" : {
- "Wombat" : {'name': 'Wombat', 'speed': 23, 'land_animal': True},
- "Black Bear": {'name': 'Black Bear', 'speed': 40, 'land_animal': True},
- "Polecat" : {'name': 'Polecat', 'speed': 15, 'land_animal': True}
- },
- "Other" : {
- "Whale" : {'name': 'Blue Whale', 'speed': 35, 'land_animal': False},
- "Orca" : {'name': 'Orca', 'speed': 45, 'land_animal': False},
- "Snake" : {'name': 'Python', 'speed': 25, 'land_animal': True}
- }
- },
+ "Real" : {
+ "Winged" : {
+ "Sparrow" : {'name': 'sparrow','speed': 12, 'land_animal': True},
+ "Kestrel" : {'name': 'kestrel', 'speed': 15, 'land_animal': True}
+ },
+ "Legged" : {
+ "Wombat" : {'name': 'Wombat', 'speed': 23, 'land_animal': True},
+ "Black Bear": {'name': 'Black Bear', 'speed': 40, 'land_animal': True},
+ "Polecat" : {'name': 'Polecat', 'speed': 15, 'land_animal': True}
+ },
+ "Other" : {
+ "Whale" : {'name': 'Blue Whale', 'speed': 35, 'land_animal': False},
+ "Orca" : {'name': 'Orca', 'speed': 45, 'land_animal': False},
+ "Snake" : {'name': 'Python', 'speed': 25, 'land_animal': True}
+ }
+ },
- "Imaginary": {
- "Winged" : {
- "Dragon" : {'name': 'Fire Dragon','speed': 100, 'land_animal': True},
- "Phoenix" : {'name': 'Phoenix', 'speed': 1500, 'land_animal': True}
- },
- "Legged" : {
- "Sphinx" : {'name': 'Sphinx','speed': 10, 'land_animal': True},
- "Minotaur" : {'name': 'Minotaur', 'speed': 5, 'land_animal': True}
- },
- "Other" : {}
- }
- }
+ "Imaginary": {
+ "Winged" : {
+ "Dragon" : {'name': 'Fire Dragon','speed': 100, 'land_animal': True},
+ "Phoenix" : {'name': 'Phoenix', 'speed': 1500, 'land_animal': True}
+ },
+ "Legged" : {
+ "Sphinx" : {'name': 'Sphinx','speed': 10, 'land_animal': True},
+ "Minotaur" : {'name': 'Minotaur', 'speed': 5, 'land_animal': True}
+ },
+ "Other" : {}
+ }
+ }
```
## Accessing Values in a `dict`
@@ -183,9 +186,9 @@ New `key`:`value` pairs can be _added_ in the same fashion:
## Removing (Pop-ing and del) Dictionary Entries
-You can use the `.pop()` method to delete a dictionary entry.
-`.pop()` removes the (`key`, `value`) pair and returns the `value` for use.
-Like `.get()`, `.pop()` accepts second argument (_`dict.pop(, )`_) that will be returned if the `key` is not found.
+You can use the `.pop()` method to delete a dictionary entry.
+`.pop()` removes the (`key`, `value`) pair and returns the `value` for use.
+Like `.get()`, `.pop()` accepts second argument (_`.pop(, )`_) that will be returned if the `key` is not found.
This prevents a `KeyError` being raised:
```python
@@ -208,8 +211,8 @@ KeyError: 'name'
'Unknown'
```
-You can also use the `del` statement to remove a single or multiple entries.
-A `KeError` is raised if the entry to be removed is not found in the dictionary:
+You can also use the `del` statement to remove one or more entries.
+A `KeyError` is raised if the entry to be removed is not found in the dictionary:
```python
>>> wombat = {'name': 'Wombat',
@@ -245,7 +248,7 @@ You can access _values_ within the same loop by using _square brackets_:
```python
>>> for key in bear:
->>> print((key, bear[key])) #this prints a tuple of (key, value)
+... print((key, bear[key])) # <--This prints a tuple of (key, value).
('name', 'Black Bear')
('speed', 40)
('land_animal', True)
@@ -257,7 +260,7 @@ You can also use the `.items()` method, which returns (`key`, `value`) tuples:
# dict.items() forms (key, value tuples) that can be
# unpacked and iterated over.
>>> for key, value in whale.items():
->>> print(key, ":", value)
+... print(key, ":", value)
name : Blue Whale
speed : 25
land_animal : False
@@ -271,11 +274,11 @@ For a detailed explanation of dictionaries in Python, the [official documentatio
## Extending Dictionary Functionality: The Collections Module
-The [`collections`][collections-docs] module adds specialized functionality to Python's standard collection-based datatypes (`dictionary`, `set`, `list`, `tuple`).
+The [`collections`][collections-docs] module adds specialized functionality to Python's standard collection-based datatypes (`dict`, `set`, `list`, `tuple`).
Three of the most useful dictionary-based classes are:
- [`Counter`][counter-dicts] automatically counts items and returns them in a `dict` with the items as keys and their counts as values.
-- [`OrderedDict`][ordered-dicts-docs], has methods specialized for arranging the order of dictionary entries.
+- [`OrderedDict`][ordered-dicts-docs] has methods specialized for arranging the order of dictionary entries.
- [`defaultdict`][default-dicts] uses a factory method to set a default value if a `key` is not found when trying to retrieve or assign to a dictionary entry.
[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
diff --git a/concepts/dicts/introduction.md b/concepts/dicts/introduction.md
index 5c8a772480b..676bf11066d 100644
--- a/concepts/dicts/introduction.md
+++ b/concepts/dicts/introduction.md
@@ -4,9 +4,9 @@ A dictionary (`dict`) in Python is a data structure that associates [hashable][t
Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
-`Keys` must be hashable and unique across the dictionary.
-Key types can include `numbers`, `str`, or `tuples` (of _immutable_ values).
-They cannot contain _mutable_ data structures such as `lists`, `dict`s, or `set`s.
+`keys` must be hashable and unique across the dictionary.
+Key types can include `number`s, `str`s, or `tuple`s (of _immutable_ values).
+They cannot contain _mutable_ data structures such as `list`s, `dict`s, or `set`s.
As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
`values` can be of any data type or structure.
diff --git a/concepts/enums/about.md b/concepts/enums/about.md
index 27b264c22e1..dacd7bfd755 100644
--- a/concepts/enums/about.md
+++ b/concepts/enums/about.md
@@ -1,14 +1,19 @@
# About
-In Python, [an enum][enum-docs] is a set of unique names that are bound unique, **constant** values. Enums are defined by inheriting an `Enum` class. Built-in enum types are available in the module `enum` and the class `Enum` can be imported using `from enum import Enum`.
+In Python, [an enum][enum-docs] is a set of unique names that are bound unique, **constant** values.
+`Enums` are defined by inheriting an `Enum` class.
+Built-in enum types are available in the module `enum` and the class `Enum` can be imported using `from enum import Enum`.
+
```python
+from enum import Enum
+
class Color(Enum):
RED = 1
GREEN = 2
```
-Note that the values of the enum members can be any data types such as str, tuple, float, etc.
+Note that the values of the `enum` members can be any data types such as `str`, `tuple`, `float`, etc.
```python
class Color(Enum):
@@ -16,9 +21,11 @@ class Color(Enum):
GREEN = 'green'
```
-Enums can also be created via the following [functional API][enum-functional-api].
+`Enums` can also be created using [function-call syntax][enum-functional-example].
```python
+from enum import Enum
+
Animal = Enum('Animal', 'ANT BEE CAT DOG')
list(Animal)
#=> [, , , ]
@@ -27,7 +34,7 @@ Animal.ANT.value
#=> 1
```
-When assigning the same value to two members in an enum, the latter assigned member will be an alias to the formed one. It is not allowed to use the same name for two members of an enum.
+When assigning the same value to two members in an `enum`, the latter assigned member will be an alias to the former one. It is not allowed to use the same name for two different members of an `enum`.
```python
class Color(Enum):
@@ -50,12 +57,13 @@ for member in Color:
# __members__.items() helps you to loop through alias as well
for member in Color.__members__.items():
print(member)
-#=>('RED', )
-#=>('GREEN', )
-#=>('ALIAS_OF_RED', )
+#=> ('RED', )
+#=> ('GREEN', )
+#=> ('ALIAS_OF_RED', )
```
-Enum members can be compared using [`is` (_identity operator_)][identity-keyword] or `is not`. The `==` or `!=` (_equality operators_) work likewise.
+`Enum` members can be compared using [`is` (_identity operator_)][identity-keyword] or `is not`. The `==` or `!=` (_equality operators_) work likewise:
+
```python
a = Color.RED
@@ -76,10 +84,10 @@ class Shape(Enum):
OVAL = auto()
```
-To disallow aliasing (_preventing duplicate values with different names_), the `@unique` decorator may be used.
+To disallow aliasing (_preventing duplicate values with different names_), the [class decorator][class-decorator] [`@enum.unique`][enum-unique-decorator] decorator may be used.
```python
-@unique
+@enum.unique
class Shape(Enum):
CIRCLE = 1
SQUARE = 2
@@ -87,7 +95,7 @@ class Shape(Enum):
#=> ValueError: duplicate values found in : TRIANGLE -> CIRCLE
```
-To access an enum member for a given value, this notation can be used: `EnumName(value)`.
+To access an `enum` member for a given value, this notation can be used: `()`.
```python
g = Color(2)
@@ -99,15 +107,23 @@ g
#=>
```
-A custom [restricted `Enum`][restricted-enums] can be written by subclassing `Enum` with any mix-in or data-type. For example:
+A custom [restricted `Enum`][restricted-enums] can be written by subclassing the `Enum` class with any mix-in or data-type.
+For example:
+
```python
class StrEnum(str, Enum):
pass
```
-[enum-docs]: https://docs.python.org/3/library/enum.html
-[enum-auto-docs]: https://docs.python.org/3/library/enum.html#using-auto
-[enum-functional-api]: https://docs.python.org/3/library/enum.html#functional-api
-[restricted-enums]: https://docs.python.org/3/library/enum.html#restricted-enum-subclassing
+Subclassing `Enum` is only allowed if the `enum` does **not** define any members.
+See the [`enum` how-to][enum-docs] and the [`enum` cookbook][cookbook] for more details and explanations.
+
+[class-decorator]: https://docs.python.org/3/reference/compound_stmts.html#class-definitions
+[cookbook]: https://docs.python.org/3/howto/enum.html#enum-cookbook
+[enum-auto-docs]: https://docs.python.org/3/library/enum.html#enum.auto
+[enum-docs]: https://docs.python.org/3/howto/enum.html#enum-basic-tutorial
+[enum-functional-example]: https://docs.python.org/3/library/enum.html
[identity-keyword]: https://www.w3schools.com/python/ref_keyword_is.asp
+[restricted-enums]: https://docs.python.org/3/howto/enum.html#restricted-enum-subclassing
+[enum-unique-decorator]: https://docs.python.org/3/library/enum.html#enum.unique
diff --git a/concepts/enums/introduction.md b/concepts/enums/introduction.md
index ea9c9000e07..bf4e8b9d043 100644
--- a/concepts/enums/introduction.md
+++ b/concepts/enums/introduction.md
@@ -1,14 +1,19 @@
# Introduction
-In Python, [an enum](https://docs.python.org/3/library/enum.html) is a set of names that are bound to unique `literal`, or `constant` values. Enums are defined by inheriting an `Enum` class. Built-in enum types are available in the module `enum` and the class `Enum` can be imported using `from enum import Enum`.
+In Python, [an `enum`][enum-docs] is a set of names that are bound to unique `literal`, or `constant` values.
+`Enums` are defined by inheriting from or subclassing an `Enum` class.
+Built-in `enum` types are available in the module `enum` and the class `Enum` can be imported using `from enum import Enum`.
+
```python
+from enum import Enum
+
class Color(Enum):
RED = 1
GREEN = 2
```
-Note that the values of the enum members can be any data types such as str, tuple, float, etc.
+Note that the values of the `enum` members can be any data types such as `str`, `tuple`, `float`, etc.
```python
class Color(Enum):
@@ -16,7 +21,7 @@ class Color(Enum):
GREEN = 'green'
```
-When assigning the same value to two members in an enum, the latter assigned member will be an alias to the formed one. It is not allowed to use the same name for two members of an enum.
+When assigning the same value to two members in an `enum`, the latter assigned member will be an alias to the former one. It is not allowed to use the same name for two different members of an `enum`.
```python
class Color(Enum):
@@ -31,7 +36,7 @@ Color.ALIAS_OF_RED.value
#=> 1
```
-Iterating through the members of the enum can be done with the standard `for member in` syntax:
+Iterating through the members of the `enum` can be done with the standard `for member in` syntax:
```python
for member in Color:
@@ -40,7 +45,7 @@ for member in Color:
#=> (GREEN, 2)
```
-Enum members can be compared using [`is` (_identity operator_)](https://www.w3schools.com/python/ref_keyword_is.asp) or `is not`. The `==` or `!=` (_equality_operators_) work likewise.
+`Enum` members can be compared using [`is` (_identity operator_)][identity-keyword] or `is not`. The `==` or `!=` (_equality operators_) work likewise:
```python
a = Color.RED
@@ -52,7 +57,7 @@ a == Color.RED
#=> True
```
-To access an enum member for a given value, `EnumName(value)` can be used:
+To access an `enum` member for a given value, `()` can be used:
```python
g = Color(2)
@@ -63,3 +68,6 @@ g is Color.GREEN
g
#=>
```
+
+[enum-docs]: https://docs.python.org/3/library/enum.html
+[identity-keyword]: https://www.w3schools.com/python/ref_keyword_is.asp
diff --git a/concepts/fractions/about.md b/concepts/fractions/about.md
index d41124c39c4..e582c53141a 100644
--- a/concepts/fractions/about.md
+++ b/concepts/fractions/about.md
@@ -1,6 +1,6 @@
# About
-The [`Fractions`][fractions] module allows us to create and work with [`rational numbers`][rational]: fractions with an integer numerator divided by an integer denominator.
+The [`fractions`][fractions] module allows us to create and work with [`rational numbers`][rational]: fractions with an integer numerator divided by an integer denominator.
For example, we can store `2/3` as an exact fraction instead of the approximate `float` value `0.6666...`
@@ -8,10 +8,10 @@ For example, we can store `2/3` as an exact fraction instead of the approximate
Unlike `int`, `float`, and `complex` numbers, fractions do not have a literal form.
-However, the fractions constructor is quite flexible.
+However, the fraction constructor is quite flexible.
-Most obviously, it can take take two integers.
-Common factors are automatically removed, converting the fraction to its "lowest form": the smallest integers that accurately represent the fraction.
+Most obviously, it can take two integers.
+Common factors are automatically removed, converting the fraction to its "lowest form" (_the smallest integers that accurately represent the fraction_):
```python
@@ -29,7 +29,7 @@ Fraction(2, 3) # automatically simplified
True
```
-The fractions constructor can also parse a string representation:
+The fraction constructor can also parse a string representation:
```python
diff --git a/concepts/fractions/introduction.md b/concepts/fractions/introduction.md
index 437ccbbeb07..156aa3ff280 100644
--- a/concepts/fractions/introduction.md
+++ b/concepts/fractions/introduction.md
@@ -1,13 +1,13 @@
# Introduction
-The [`Fractions`][fractions] module allows us to create and work with [`rational numbers`][rational]: fractions with an integer numerator divided by an integer denominator.
+The [`fractions`][fractions] module allows us to create and work with [`rational numbers`][rational]: fractions with an integer numerator divided by an integer denominator.
For example, we can store `2/3` as an exact fraction instead of the approximate `float` value `0.6666...`.
Unlike `int`, `float`, and `complex` numbers, fractions do not have a literal form.
-However, the fractions constructor is quite flexible.
+However, the fraction constructor is quite flexible.
-Most obviously, it can take take two integers as arguments.
-Common factors are automatically removed, converting the fraction to its "lowest form": the smallest integers that accurately represent the fraction:
+Most obviously, it can take two integers as arguments.
+Common factors are automatically removed, converting the fraction to its "lowest form" (_the smallest integers that accurately represent the fraction_):
```python
>>> from fractions import Fraction
@@ -24,7 +24,7 @@ Fraction(2, 3) # automatically simplified
True
```
-The fractions constructor can also parse a string representation:
+The fraction constructor can also parse a string representation:
```python
>>> f3 = Fraction('2/3')
diff --git a/concepts/functools/about.md b/concepts/functools/about.md
index e5afb577d39..cbc5cd89d96 100644
--- a/concepts/functools/about.md
+++ b/concepts/functools/about.md
@@ -1,312 +1 @@
-# About
-
-The functools module is for higher-order functions: functions that act on or return other ***[functions](https://docs.python.org/3/tutorial/controlflow.html#defining-functions)***. It provides functions for working with other functions and callable objects to use or extend them without completely rewriting them.
-
-## Memoizing the function calls
-
-**Memoizing:** Storing the result of some expensive function, which is called with the same input again and again. So, we don't have to run the function repeatedly.
-
-### ```@functools.lru_cache(maxsize=128, typed=False)```
-
-***[@functools.lru_cache(maxsize=128, typed=False)](https://docs.python.org/3/library/functools.html#functools.lru_cache)*** Decorator to wrap a function with a memoizing callable that saves up to the maxsize most recent calls. It can save time when an expensive or I/O bound function is periodically called with the same arguments.
-
-Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable.
-
-Here ```maxsize = 128``` means that it is going to memoize latest 128 function calls at max.
-
-The lru_cache works the same way but it can cache at max maxsize calls and if type = True, then the function arguments of different types will be cached separately i.e. 5 and 5.0 will be cached differently.
-
-### ```@functools.cache(user_function)```
-
-***[@functools.cache(user_function)](https://docs.python.org/3/library/functools.html#functools.cache)*** the same as lru_cache(maxsize=None), creating a thin wrapper around a dictionary lookup for the function arguments. Because it never needs to evict old values, this is smaller and faster than ```lru_cache()``` with a size limit.
-
-```python
-
->>> @cache
->>> def factorial(n):
->>> return n * factorial(n-1) if n else 1
-
->>> factorial(10) # no previously cached result, makes 11 recursive calls
-3628800
->>> factorial(5) # just looks up cached value result
-120
->>> factorial(12) # makes two new recursive calls, the other 10 are cached
-479001600
-
-# The lru_cache works the same way but it can cache at max maxsize calls and if type = True, then the function arguments of different types will be cached separately.
-
-# Some types such as str and int may be cached separately even when typed is false.
-
->>> @lru_cache(maxsize = 128)
->>> def factorial(n):
->>> return n * factorial(n-1) if n else 1
-
->>> factorial(10)
-3628800
-
-# by the Following we can fetch the information about the cache.
->>> factorial.cache_info()
-CacheInfo(hits=0, misses=11, maxsize=128, currsize=11)
-```
-
-## Generic functions
-
-***[Generic functions](https://pymotw.com/3/functools/#generic-functions)*** are those which perform the operation based on the argument given to them. In statically typed languages it can be done by function overloading.
-
-In python functools provides the `singledispatch()` decorator to register a set of generic functions for automatic switching based on the type of the first argument to a function.
-
-The ```register()``` attribute of the function serves as another decorator for registering alternative implementations.To add overloaded implementations to the function, use the ```register(type)``` attribute of the generic function.
-
-When user is going to call the function with the integer argument, then it will be redirected to the function decorated with ```register(int)``` decorator.
-
-The first function wrapped with singledispatch() is the default implementation if no other type-specific function is found, default implementation will be called.
-
-```python
-
->>> from functools import singledispatch
-
->>> @singledispatch
- def fun(arg):
- print("default argument string: ", arg)
-
-
->>> fun.register(int)
- def _(arg):
- print("This is an integer: ", arg)
-
->>> fun.register(list)
- def _(arg):
- print("This is a list: ", arg)
-
->>> fun("Hello")
-"default argument string: Hello"
-
->>> fun(10)
-"This is an integer: 10"
-
->>> fun([1,2,3])
-"This is a list: [1,2,3]"
-
-# This will call the default function as we didn't registered any function with float.
->>> fun(2.45)
-"default argument string: 2.45"
-
-```
-
-For class methods we can use ***[singledispatchmethod(func)](https://docs.python.org/3/library/functools.html#functools.singledispatchmethod)*** to register a set of generic methods for automatic switching based on the type of the first non-self or non-class argument to a function.
-
-```python
-
->>> class Negator:
- @singledispatchmethod
- def neg(self, arg):
- raise NotImplementedError("Cannot negate a")
-
- @neg.register(int)
- def _(self, arg):
- return -arg
-
- @neg.register(bool)
- def _(self, arg):
- return not arg
-
->>> obj = Negator()
-
-# Going to call function which is register with bool datatype.
->>> obj.neg(True)
-False
-
-# Going to call function which is register with int datatype.
->>> obj.neg(10)
--10
-
-# Going to call default function and will display an error message.
->>> obj.neg("String")
-
-```
-
-## Partial
-
-`functools.partial(func, /, *args, **keywords)` return a new ***[partial object](https://docs.python.org/3/library/functools.html#partial-objects)*** which when called will behave like func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they are appended to args.The ***[partial](https://docs.python.org/3/library/functools.html#functools.partial)*** is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.
-
-```python
-
->>> def add(a, b):
- print(f"got a={a}, b={b}")
- print(a+b)
-
->>> a = partial(add, 10)
->>> a(4)
-"got a=10, b=4"
-14
-
-# 10 got assigned to a because partial start assigning arguments from the left.
-
->>> a = partial(add, b=10)
->>> a(4)
-"got a=4, b=10"
-14
-
-# But By using the keywords we can assign the value to the arguments at right
-
-```
-
-### partial Objects
-
-partial objects are callable objects created by partial(). They have three read-only attributes:
-
-```partial.func```
-
-A callable object or function. Calls to the partial object will be forwarded to func with new arguments and keywords.
-
-```partial.args```
-
-The leftmost positional arguments that will be prepended to the positional arguments provided to a partial object call.
-
-```partial.keywords```
-
-The keyword arguments that will be supplied when the partial object is called.
-
-```python
-
->>> from functools import partial
-
->>> pow_2 = partial(pow, exp = 2)
-
->>> pow_2.func == pow
-True
-
->>> pow_2.args
-()
-
->>> pow_2.keywords
-{'exp': 2}
-
->>> two_pow = partial(pow, 2)
-
->>> two_pow(3) # 2(frezzed) ^ 3 = 8 == pow(2 [fixed] ,3 [passed by user])
-8
-
->>> pow_2.args
-(2,)
-
-```
-
-The ```pow_2.func``` is same as the ```pow``` function.
-
-Here ```pow_2.args``` returns an empty tuple because we do not pass any positional argument to our partial object call.
-
-```pow_2.keywords``` returns a dictionary of keywords argument which will be supplied when the partial object is called.
-
-Here ```two_pow.args``` returns a ```(2,)``` tuple because we passed 2 as an argument while creating the partial object, which fixed the value of ```base``` argument as ```2```.
-
-### ```partialmethod```
-
-***[functools.partialmethod(func, /, *args, **keywords)](https://docs.python.org/3/library/functools.html#functools.partialmethod)*** Return a new partialmethod descriptor which behaves like partial except that it is designed to be used as a method definition rather than being directly callable.
-
-```python
-
->>> class Cell:
- def __init__(self):
- self.alive = False
-
- def set_state(self, state):
- self.alive = bool(state)
-
- # going to return a method set_state with argument state = True
- set_alive = partialmethod(set_state, True)
- # going to return a method set_state with argument state = False
- set_dead = partialmethod(set_state, False)
-
->>> c = Cell()
->>> c.alive
-False
->>> c.set_alive()
->>> c.alive
-True
-
-```
-
-## Wraps
-
-### `functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`
-
-***[functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)](https://docs.python.org/3/library/functools.html#functools.update_wrapper)*** Update a wrapper function to look like the wrapped function. The optional arguments are tuples to specify which attributes of the original function are assigned directly to the matching attributes on the wrapper function and which attributes of the wrapper function are updated with the corresponding attributes from the original function.
-
-WRAPPER_ASSIGNMENTS (which assigns to the wrapper function’s `__module__`, `__name__`, `__qualname__`, `__annotations__` and `__doc__`, the documentation string)
-
-WRAPPER_UPDATES (which updates the wrapper function’s `__dict__`, i.e. the instance dictionary).
-
-```python
-
-# without update_wrapper()
-
->>> def decorator(func):
- def wrapper(name):
- """Going to say Hello"""
- print("hello",name)
- func(name)
- return wrapper
-
-
->>> @decorator
- def fun(name):
- """Going to Wish"""
- print("good morning",name)
-
-# In bigger python code base this will cause problem while debugging the code.
->>> fun.__name__
-'wrapper'
->>> fun.__doc__
-'Going to say Hello'
-
-# with update_wrapper()
-
->>> def decorator(func):
- def wrapper(name):
- """Going to say Hello"""
- print("hello",name)
- func(name)
- update_wrapper(wrapper, func)
- return wrapper
-
-
->>> @decorator
- def fun(name):
- """Going to Wish"""
- print("good morning",name)
-
-# Now the wrapper function just look like the wrapped(fun) function
->>> fun.__name__
-'fun'
->>> fun.__doc__
-'Going to Wish'
-```
-
-### `functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`
-
-***[functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)](https://docs.python.org/3/library/functools.html#functools.wraps)*** is a convenience function for invoking update_wrapper() as a function decorator when defining a wrapper function. It is equivalent to partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated).
-
-```python
-
-# This going to work same as the above where we are using the update_wrapper() function
->>> def decorator(func):
- @wraps(fun)
- def wrapper(name):
- """Going to say Hello"""
- print("hello",name)
- func(name)
- return wrapper
-
-
->>> @decorator
- def fun(name):
- """Going to Wish"""
- print("good morning",name)
-
-# Now the wrapper function just look like the wrapped(fun) function
->>> fun.__name__
-'fun'
->>> fun.__doc__
-'Going to Wish'
-```
+#TODO: Add about for this concept.
diff --git a/concepts/functools/introduction.md b/concepts/functools/introduction.md
index c91aedc81bd..bbe12ffd5e9 100644
--- a/concepts/functools/introduction.md
+++ b/concepts/functools/introduction.md
@@ -1,43 +1 @@
-# Introduction
-
-The functools module is for higher-order functions: functions that act on or return other ***[functions](https://docs.python.org/3/tutorial/controlflow.html#defining-functions)***. It provides functions for working with other functions and callable objects to use or extend them without completely rewriting them.
-
-## Memoizing the function calls
-
-**Memoizing:** Storing the result of some expensive function, which is called with the same input again and again. So, we don't have to run the function repeatedly.
-
-### ```@functools.lru_cache(maxsize=128, typed=False)```
-
-***[@functools.lru_cache(maxsize=128, typed=False)](https://docs.python.org/3/library/functools.html#functools.lru_cache)*** Decorator to wrap a function with a memoizing callable that saves up to the maxsize most recent calls. It can save time when an expensive or I/O bound function is periodically called with the same arguments.
-
-Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable.
-
-Here ```maxsize = 128``` means that it is going to memoize latest 128 function calls at max.
-
-### ```@functools.cache(user_function)```
-
-***[@functools.cache(user_function)](https://docs.python.org/3/library/functools.html#functools.cache)*** the same as lru_cache(maxsize=None), creating a thin wrapper around a dictionary lookup for the function arguments. Because it never needs to evict old values, this is smaller and faster than ```lru_cache()``` with a size limit.
-
-## Generic functions
-
-***[Generic functions](https://pymotw.com/3/functools/#generic-functions)*** are those which preform the operation based on the argument given to them.
-
-In statically typed languages it can be done by function overloading, In python functools provides the ```singledispatch(func)``` decorator to register a set of generic functions for automatic switching based on the type of the first argument to a function.
-
-For class methods we can use ***[singledispatchmethod(func)](https://docs.python.org/3/library/functools.html#functools.singledispatchmethod)*** to register a set of generic methods for automatic switching based on the type of the first non-self or non-class argument to a function.
-
-## Partial
-
-`functools.partial(func, /, *args, **keywords)` return a new ***[partial object](https://docs.python.org/3/library/functools.html#partial-objects)*** which when called will behave like func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they are appended to args.The ***[partial](https://docs.python.org/3/library/functools.html#functools.partial)*** is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.
-
-***[functools.partialmethod(func, /, *args, **keywords)](https://docs.python.org/3/library/functools.html#functools.partialmethod)*** Return a new partialmethod descriptor which behaves like partial except that it is designed to be used as a method definition rather than being directly callable.
-
-## Wraps
-
-### `functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`
-
-***[functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)](https://docs.python.org/3/library/functools.html#functools.update_wrapper)*** Update a wrapper function to look like the wrapped function. The optional arguments are tuples to specify which attributes of the original function are assigned directly to the matching attributes on the wrapper function and which attributes of the wrapper function are updated with the corresponding attributes from the original function.
-
-### `functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)`
-
-***[functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)](https://docs.python.org/3/library/functools.html#functools.wraps)*** is a convenience function for invoking update_wrapper() as a function decorator when defining a wrapper function. It is equivalent to partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated).
+#TODO: Add introduction for this concept.
diff --git a/concepts/generators/about.md b/concepts/generators/about.md
index 59b5035d6b9..4b2e74cbad2 100644
--- a/concepts/generators/about.md
+++ b/concepts/generators/about.md
@@ -35,9 +35,33 @@ The rationale behind this is that you use a generator when you do not need all t
This saves memory and processing power, since only the value you are _currently working on_ is calculated.
+
## Using a generator
-Generators may be used in place of most `iterables` in Python. This includes _functions_ or _objects_ that require an `iterable`/`iterator` as an argument.
+Generators (_technically [`generator-iterator`s][generator-iterator] — see the note below._) are a type of `iterator` and can be used anywhere in Python where an `iterator` or `iterable` is expected.
+This includes _functions_ or _objects_ that require an `iterable`/`iterator` as an argument.
+For a deeper dive, see [How to Make an Iterator in Python][how-to-iterator].
+
+
+~~~~exercism/note
+
+Generator-iterators are a special sub-set of [iterators][iterator].
+`Iterators` are the mechanism/protocol that enables looping over _iterables_.
+Generator-iterators and the iterators returned by common Python [`iterables`][iterables] act very similarly, but there are some important differences to note:
+
+- They are _[lazily evaluated][lazy evaluation]_; iteration is _one-way_ and there is no "backing up" to a previous value.
+- They are _consumed_ by iterating over the returned values; there is no resetting or saving in memory.
+- They are not sortable and cannot be reversed.
+- They are not sequence types, and _do not_ have `indexes`.
+ You cannot reference a previous or future value using addition or subtraction and you cannot use bracket (`[]`) notation or slicing.
+- They cannot be used with the `len()` function, as they have no length.
+- They can be _finite_ or _infinite_ - be careful when collecting all values from an _infinite_ `generator-iterator`!
+
+[iterator]: https://docs.python.org/3.11/glossary.html#term-iterator
+[iterables]: https://wiki.python.org/moin/Iterator
+[lazy evaluation]: https://en.wikipedia.org/wiki/Lazy_evaluation
+~~~~
+
To use the `squares_generator()` generator:
@@ -140,7 +164,8 @@ Generators are also very helpful when a process or calculation is _complex_, _ex
Now whenever `__next__()` is called on the `infinite_sequence` object, it will return the _previous number_ + 1.
-[generator-iterator]: https://docs.python.org/3.11/glossary.html#term-generator-iterator
+[generator-iterator]: https://docs.python.org/3/glossary.html#term-generator-iterator
+[how-to-iterator]: https://treyhunner.com/2018/06/how-to-make-an-iterator-in-python/#Generators:_the_easy_way_to_make_an_iterator
[iterables]: https://wiki.python.org/moin/Iterator
[iterator]: https://docs.python.org/3.11/glossary.html#term-iterator
[lazy iterator]: https://en.wikipedia.org/wiki/Lazy_evaluation
diff --git a/concepts/list-methods/about.md b/concepts/list-methods/about.md
index 1c9686360d4..00b41f325e5 100644
--- a/concepts/list-methods/about.md
+++ b/concepts/list-methods/about.md
@@ -11,7 +11,7 @@ Lists support both [common][common sequence operations] and [mutable][mutable se
Python provides many useful [methods][list-methods] for working with lists.
Because lists are mutable, list-methods **alter the original list object** passed into the method.
-If mutation is undesirable, a `shallow copy` (_at minimum__) of the original `list` needs to be made via `slice` or `.copy()`.
+If mutation is undesirable, a `shallow copy` (_at minimum_) of the original `list` needs to be made via `slice` or `.copy()`.
## Adding Items
@@ -47,7 +47,8 @@ If `` is greater than the final index on the list, the item will be added
```
An `iterable` can be _combined_ with an existing list (concatenating the two) via `.extend()`.
-`.extend()` will _unpack_ the supplied iterable, adding its elements in the same order to the end of the target list (_using `.append(- )` in this circumstance would add the entire iterable as a **single item**._).
+`
.extend()` will _unpack_ the supplied iterable, adding its elements in the same order to the end of the target list.
+Using `.append(- )` in this circumstance would add the entire iterable as a _**single item**_.
```python
diff --git a/concepts/lists/about.md b/concepts/lists/about.md
index f7d4054eef0..51c44060d5c 100644
--- a/concepts/lists/about.md
+++ b/concepts/lists/about.md
@@ -49,15 +49,15 @@ For readability, line breaks can be used when there are many elements or nested
```python
>>> lots_of_entries = [
- "Rose",
- "Sunflower",
- "Poppy",
- "Pansy",
- "Tulip",
- "Fuchsia",
- "Cyclamen",
- "Lavender"
- ]
+... "Rose",
+... "Sunflower",
+... "Poppy",
+... "Pansy",
+... "Tulip",
+... "Fuchsia",
+... "Cyclamen",
+... "Lavender"
+... ]
>>> lots_of_entries
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']
@@ -65,10 +65,10 @@ For readability, line breaks can be used when there are many elements or nested
# Each data structure is on its own line to help clarify what they are.
>>> nested_data_structures = [
- {"fish": "gold", "monkey": "brown", "parrot": "grey"},
- ("fish", "mammal", "bird"),
- ['water', 'jungle', 'sky']
- ]
+... {"fish": "gold", "monkey": "brown", "parrot": "grey"},
+... ("fish", "mammal", "bird"),
+... ['water', 'jungle', 'sky']
+... ]
>>> nested_data_structures
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]
@@ -174,7 +174,7 @@ Indexes can be from **`left`** --> **`right`** (_starting at zero_) or **`right`
'Toast'
```
-A section of a list can be accessed via _slice notation_ (`
[start:stop]`).
+A section of a list can be accessed via _slice notation_ (`[:]`).
A _slice_ is defined as an element sequence at position `index`, such that `start <= index < stop`.
[_Slicing_][slice notation] returns a copy of the "sliced" items and does not modify the original `list`.
@@ -207,7 +207,7 @@ Lists supply an [_iterator_][iterator], and can be looped through/over in the sa
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for item in colors:
... print(item)
-...
+
Orange
Green
Grey
@@ -218,7 +218,7 @@ Blue
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for index, item in enumerate(colors):
... print(item, ":", index)
-...
+
Orange : 0
Green : 1
Grey : 2
@@ -229,7 +229,7 @@ Blue : 3
>>> numbers_to_cube = [5, 13, 12, 16]
>>> for number in numbers_to_cube:
... print(number**3)
-...
+
125
2197
1728
@@ -335,7 +335,7 @@ This reference complication becomes exacerbated when working with nested or mult
from pprint import pprint
# This will produce a game grid that is 8x8, pre-populated with zeros.
->>> game_grid = [[0]*8] *8
+>>> game_grid = [[0]*8]*8
>>> pprint(game_grid)
[[0, 0, 0, 0, 0, 0, 0, 0],
diff --git a/concepts/loops/about.md b/concepts/loops/about.md
index 0f39e733d0c..e3322af0e3b 100644
--- a/concepts/loops/about.md
+++ b/concepts/loops/about.md
@@ -31,7 +31,6 @@ The keywords `break`, `continue`, and `else` help customize loop behavior.
The basic [`for`][for statement] `loop` in Python is better described as a _`for each`_ which cycles through the values of any [iterable object][iterable], terminating when there are no values returned from calling [`next()`][next built-in] (_raising a [`StopIteration`][stopiteration]_).
```python
-
>>> word_list = ["bird", "chicken", "barrel", "bongo"]
>>> for word in word_list:
@@ -95,7 +94,6 @@ Interestingly, `range()` [_is not an iterator_][range is not an iterator], and c
If both values and indexes are needed, the built-in [`enumerate()`][enumerate] will return an [`iterator`][iterator] over (`index`, `value`) pairs:
```python
-
>>> word_list = ["bird", "chicken", "barrel", "apple"]
# *index* and *word* are the loop variables.
@@ -152,15 +150,15 @@ The `enumerate()` function can also be set to `start` the index count
The [`continue`][continue statement] keyword can be used to skip forward to the next iteration cycle:
```python
-word_list = ["bird", "chicken", "barrel", "bongo", "sliver", "apple", "bear"]
-
-# This will skip *bird*, at index 0
-for index, word in enumerate(word_list):
- if index == 0:
- continue
- if word.startswith("b"):
- print(f"{word.title()} (at index {index}) starts with a b.")
-
+>>> word_list = ["bird", "chicken", "barrel", "bongo", "sliver", "apple", "bear"]
+...
+... # This will skip *bird*, at index 0
+... for index, word in enumerate(word_list):
+... if index == 0:
+... continue
+... if word.startswith("b"):
+... print(f"{word.title()} (at index {index}) starts with a b.")
+...
'Barrel (at index 2) starts with a b.'
'Bongo (at index 3) starts with a b.'
'Bear (at index 6) starts with a b.'
@@ -176,9 +174,9 @@ The [`break`][break statement] (_like in many C-related languages_) keyword can
... if word.startswith("b"):
... print(f"{word.title()} (at index {index}) starts with a B.")
... elif word == "sliver":
-... break
+... break
... else:
-... print(f"{word.title()} doesn't start with a B.")
+... print(f"{word.title()} doesn't start with a B.")
... print("loop broken.")
...
'Bird (at index 0) starts with a B.'
@@ -202,11 +200,11 @@ The loop [`else` clause][loop else] is unique to Python and can be used for "wra
... word = word.title()
... if word.startswith("B"):
... print(f"{word} (at index {index}) starts with a B.")
-
-...# This executes once *StopIteration* is raised and
-...# there are no more items to iterate through.
-...# Note the indentation, which lines up with the for keyword.
-...else:
+...
+... # This executes once *StopIteration* is raised and
+... # There are no more items to iterate through.
+... # Note the indentation, which lines up with the for keyword.
+... else:
... print(f"Found the above b-words, out of {len(word_list)} words in the word list.")
...
'Bird (at index 0) starts with a B.'
@@ -227,7 +225,7 @@ The loop [`else` clause][loop else] is unique to Python and can be used for "wra
... # This statement does not run, because a *break* was triggered.
... else:
-... print(f"Found the above b-words, out of {len(word_list)} words in the word list.")
+... print(f"Found the above b-words, out of {len(word_list)} words in the word list.")
...
'Bird (at index 0) starts with a B.'
'Barrel (at index 2) starts with a B.'
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 1155bcf7a5c..3fa63c140d9 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -135,7 +135,7 @@ Numbers can be converted from `int` to `floats` and `floats` to `int` using the
## Round
-Python provides a built-in function [`round(number, )`][round] to round off a floating point number to a given number of decimal places.
+Python provides a built-in function [`round(, )`][round] to round off a floating point number to a given number of decimal places.
If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
```python
diff --git a/concepts/sets/about.md b/concepts/sets/about.md
index 2c011c14471..944f98d1b52 100644
--- a/concepts/sets/about.md
+++ b/concepts/sets/about.md
@@ -3,16 +3,16 @@
A [`set`][type-set] is a _mutable_ and _unordered_ collection of [_hashable_][hashable] objects.
Set members must be distinct — duplicate items are not allowed.
They can hold multiple different data types and even nested structures like a `tuple` of `tuples` — as long as all elements can be _hashed_.
-Sets also come in an immutable [`frozensets`][type-frozenset] flavor.
+Sets also come in an immutable [`frozenset`][type-frozenset] flavor.
Sets are most commonly used to quickly remove duplicates from other data structures or item groupings.
They are also used for efficient comparisons when sequencing and duplicate tracking are not needed.
Like other collection types (_dictionaries, lists, tuples_), `sets` support:
-- Iteration via `for item in `
+- Iteration via `for item in `,
- Membership checking via `in` and `not in`,
- Length calculation through `len()`, and
-- Shallow copies through `copy()`
+- Shallow copies through `copy()`.
`sets` do not support:
- Indexing of any kind
@@ -174,12 +174,12 @@ Traceback (most recent call last):
Sets have methods that generally mimic [mathematical set operations][mathematical-sets].
Most (_not all_) of these methods have an [operator][operator] equivalent.
-Methods generally take any `iterable` as an argument, while operators require that both sides of the operation are `sets` or `frozensets`.
+Methods generally take any `iterable` as an argument, while operators require that both sides of the operation are `set`s or `frozenset`s.
### Membership Testing Between Sets
-The `.isdisjoint()` method is used to test if a `sets` elements have any overlap with the elements of another.
+The `.isdisjoint()` method is used to test if a `set`'s elements have any overlap with the elements of another.
The method will accept any `iterable` or `set` as an argument.
It will return `True` if the two sets have **no elements in common**, `False` if elements are **shared**.
@@ -275,9 +275,9 @@ True
### 'Proper' Subsets and Supersets
` < ` and ` > ` are used to test for _proper subsets_.
-A `set` is a proper subset if (`` <= ``) **AND** (`` != ``) for the `<` operator.
+A `set` is a proper subset if (` <= `) **AND** (` != `) for the `<` operator.
-A `set is a proper superset if `(`` >= ``) **AND** (`` != ``) for the `>` operator.
+A `set` is a proper superset if (` >= `) **AND** (` != `) for the `>` operator.
These operators have no method equivalent:
```python
@@ -336,7 +336,7 @@ The operator form of this method is ` | | | ...
### Set Differences
`.difference(*)` returns a new `set` with elements from the original `` that are not in ``.
-The operator version of this method is ` - - - ...`.
+The operator version of this method is ` - - - ... - `.
```python
>>> berries_and_veggies = {'Asparagus',
@@ -370,7 +370,7 @@ The operator version of this method is ` - - -
### Set Intersections
`.intersection(*)` returns a new `set` with elements common to the original `set` and all `` (in other words, the `set` where everything [intersects][intersection]).
-The operator version of this method is ` & & & ... `
+The operator version of this method is ` & & & ... & `
```python
>>> perennials = {'Annatto','Asafetida','Asparagus','Azalea',
diff --git a/concepts/sets/introduction.md b/concepts/sets/introduction.md
index 5d66b6a8ad8..551e295e6a0 100644
--- a/concepts/sets/introduction.md
+++ b/concepts/sets/introduction.md
@@ -3,16 +3,16 @@
A [`set`][type-set] is a _mutable_ and _unordered_ collection of [_hashable_][hashable] objects.
Set members must be distinct — duplicate items are not allowed.
They can hold multiple different data types and even nested structures like a `tuple` of `tuples` — as long as all elements can be _hashed_.
-Sets also come in an immutable [`frozensets`][type-frozenset] flavor.
+Sets also come in an immutable [`frozenset`][type-frozenset] flavor.
Sets are most commonly used to quickly remove duplicates from other data structures or item groupings.
They are also used for efficient comparisons when sequencing and duplicate tracking are not needed.
Like other collection types (_dictionaries, lists, tuples_), `sets` support:
-- Iteration via `for item in `
+- Iteration via `for item in `,
- Membership checking via `in` and `not in`,
- Length calculation through `len()`, and
-- Shallow copies through `copy()`
+- Shallow copies through `copy()`.
`sets` do not support:
- Indexing of any kind
diff --git a/concepts/string-formatting/.meta/config.json b/concepts/string-formatting/.meta/config.json
index 4e3955fc293..9f4edb07327 100644
--- a/concepts/string-formatting/.meta/config.json
+++ b/concepts/string-formatting/.meta/config.json
@@ -1,5 +1,11 @@
{
"blurb": "There are four main string formatting methods. A '%' formatting mini-language is supported, but is considered outdated. String interpolation (f-strings) and 'str.format()'are newer, and can be used for complex or conditional substitution. 'string.template()' substitution is used for internationalization, where f-strings will not translate.",
- "authors": ["valentin-p"],
- "contributors": ["j08k", "BethanyG"]
+ "authors": [
+ "valentin-p"
+ ],
+ "contributors": [
+ "j08k",
+ "BethanyG",
+ "BNAndras"
+ ]
}
diff --git a/concepts/string-formatting/about.md b/concepts/string-formatting/about.md
index f3b2756b768..be2ac0a1220 100644
--- a/concepts/string-formatting/about.md
+++ b/concepts/string-formatting/about.md
@@ -30,57 +30,94 @@ In this example, we insert two variable values in the sentence: one `str` and on
The expressions evaluated can be almost anything.
Some of the (wide range) of possibilities that can be evaluated: `str`, `numbers`, variables, arithmetic expressions, conditional expressions, built-in types, slices, functions, lambdas, comprehensions or **any** objects with either `__str__` or `__repr__` methods defined.
-Some examples:
+Going from simple to complex:
+
+**Inserting a variable** — the simplest use of a f-string is to place a variable directly into the string.
+
+```python
+# Assigning a variable
+>>> name = "World"
+
+# Inserting that variable
+>>> f'Hello, {name}!'
+'Hello, World!'
+```
+
+**Expressions inside `{}`** — any valid Python expression can be evaluated inside the braces.
+Note that using double quotes inside a single-quoted f-string (or vice versa) avoids the need for escape sequences:
```python
-# A dictionary of key:value pairs.
+# A dictionary of key:value pairs
>>> waves = {'water': 1, 'light': 3, 'sound': 5}
-# Using the name waves in an f-string.
->>> f'"A dict can be represented with f-string: {waves}."'
-'"A dict can be represented with f-string: {\'water\': 1, \'light\': 3, \'sound\': 5}."'
+# Inserting the whole dict
+>>> f'Wave ranks: {waves}'
+"Wave ranks: {'water': 1, 'light': 3, 'sound': 5}"
+
+# An expression can be evaluated inline
+>>> f"Tenfold the value of 'light' is {waves['light'] * 10}."
+"Tenfold the value of 'light' is 30."
+
+# A method call can also be evaluated inline
+>>> f'{"hello world!".title()} is a classic greeting.'
+'Hello World! is a classic greeting.'
-# Here, we pull a value from the dictionary by using the key
->>> f'Tenfold the value of "light" is {waves["light"] * 10}.'
-'Tenfold the value of "light" is 30.'
+# An f-string can be nested inside another f-string
+>>> f"{f'hello world!'.title()} is a classic greeting."
+'Hello World! is a classic greeting.'
```
-Replacement fields (_the `{}` in the f-string_) support output control mechanisms such as width, alignment, precision.
-This specification is started in the [format specification mini-language][format-mini-language].
+**Output formatting** — the [format specification mini-language][format-mini-language] can be used to control alignment, numeric precision, and much more.
+The format specification goes after the value, separated by a `:`.
-A more complex example of an `f-string` that includes output control:
+```python
+# Right-align a value to ten characters, rounding it to 3 decimal places.
+>>> value = 1 / 7
+>>> f'One seventh is {value:10.3f}.'
+'One seventh is 0.143.'
+
+# A format specification can be set using variables as well.
+>>> padding = 10
+>>> precision = 3
+>>> f'One seventh is {value:{padding}.{precision}f}.'
+'One seventh is 0.143.'
+```
+
+**Putting it all together** — variables, expressions, function calls, and output formatting:
```python
-# Assigning variables
>>> precision = 3
->>> verb = "see"
->>> the_end = ['end', 'of', 'transmission']
+>>> f"{30e8 * 111_000:6.{precision}e}"
+'3.330e+14'
-# Reassigning verb to 'meet'.
>>> verb = 'meet'
+>>> the_end = ['end', 'of', 'transmission']
+>>> f'"Have a {"NICE".lower()} day, I will {verb} you after {30e8 * 111_000:6.{precision}e} light-years."{the_end}'
+'"Have a nice day, I will meet you after 3.330e+14 light-years."[\'end\', \'of\', \'transmission\']'
-# This example includes a function, str, a nested f-string, an arithmetic expression,
-# precision formatting, bracket escaping and object formatting.
->>> f'"Have a {"NICE".lower()} day, I will {verb} you after {f"{30e8 * 111_000:6.{precision}e}"} light-years."{{{the_end}}}'
-'"Have a nice day, I will meet you after 3.330e+14 light-years."{[\'end\', \'of\', \'transmission\']}'
+# Did you notice the escaped single-quotes in the previous example?
+# Using double quotes instead of single quotes for the f-string means the list's single-quoted strings print cleanly.
+>>> f"Have a nice day. {the_end}"
+"Have a nice day. ['end', 'of', 'transmission']"
```
-There are a few limitations to be aware of.
-`f-string` expressions cannot be empty, they cannot contain comments.
+There are two main limitations to be aware of.
+`f-string` expressions can not be empty.
+[Additionally, before Python 3.12, they could not contain comments.][pep-0701]
```python
>>> f"An empty expression will error: {}"
SyntaxError: f-string: empty expression not allowed
>>> word = 'word'
->>> f"""A comment in a triple quoted f-string will error: {
+>>> f"""A comment in a triple quoted f-string: {
word # I chose a nice variable
}"""
-SyntaxError: f-string expression part cannot include '#'
+'A comment in a triple quoted f-string: word'
```
~~~~exercism/caution
-String interpolation cannot be used together with the [GNU gettext API][gnu-gettext-api] for internationalization (I18N) and localization (L10N), so it is recommended that the `string.Template(template)` class or the `str.format()` method outlined below be used instead of an `f-string` in any "string wrapping" translation scenarios.
+String interpolation can not be used together with the [GNU gettext API][gnu-gettext-api] for internationalization (I18N) and localization (L10N), so it is recommended that the `string.Template(template)` class or the `str.format()` method outlined below be used instead of an `f-string` in any "string wrapping" translation scenarios.
Also keep in mind that using expressions inside the `f-string` brackets `{}` is similar to using `eval()` or `exec()`, so it isn't very safe and should be used sparingly.
~~~~
@@ -105,7 +142,7 @@ The complete formatting specifier pattern is `{[][!][:` can be a named placeholder or a number or empty.
- `!` is optional and should be one of this three conversions: `!s` for [`str()`][str-conversion], `!r` for [`repr()`][repr-conversion] or `!a` for [`ascii()`][ascii-conversion].
By default, `str()` is used.
-- `:` is optional and has a lot of options, which we are [listed here][format-specifiers].
+- `:` is optional and controls how the value is displayed. More information about possible options can be [found here][format-specifiers].
Example of conversions for a diacritical letter:
@@ -132,13 +169,39 @@ Example of conversions for a diacritical letter:
"She said her name is not Chloe but 'Zoë'."
```
-Example of using format specifiers:
+Examples of common format specifiers:
```python
-# Formats the object at index 0 as a decimal with zero places,
-# then as a right-aligned binary number in an 8 character wide field.
->>> "The number {0:d} has a representation in binary: '{0: >8b}'.".format(42)
-"The number 42 has a representation in binary: ' 101010'."
+# Integer and binary/hex representations of the same number
+>>> my_num = 42
+>>> f"{my_num} in binary is {my_num:b}. In hex, it is {my_num:x}"
+"42 in binary is 101010. In hex, it is 2a"
+
+# Alignment: left (<), right (>), and center (^) using up to ten characters total
+>>> f"[{"left":<10}] [{"right":>10}] [{"center":^10}]"
+"[left ] [ right] [ center ]"
+
+# Float precision and scientific notation up to three decimal places
+>>> pi = 3.141592653589793
+>>> f"fixed: {pi:.3} scientific: {pi:.3e}"
+"fixed: 3.142 scientific: 3.142e+00"
+
+# Thousands separator and percentage
+>>> balance = 1000
+>>> rate = 0.0225
+>>> f"Balance: ${balance:,.0f} Interest rate: {rate:.1%}"
+"Balance: $1,000 Interest rate: 2.2%"
+
+# Putting it all together
+>>> items = [("Widget", 1250, 9.991), ("Gadget", 37, 24.503), ("Doohickey", 4, 149.002)]
+>>> header = f"{"Item":<12} {"Qty":>6} {"Price":>9}"
+>>> print(header)
+Item Qty Price
+>>> for name, qty, price in items:
+... print(f"{name:<12} {qty:>6} {price:>9.2f}")
+Widget 1250 9.99
+Gadget 37 24.50
+Doohickey 4 149.00
```
More examples are shown at the end of [this documentation][summary-string-format].
@@ -177,8 +240,10 @@ If you want to add multiple variables to a string, you need to supply a [tuple][
## Template Strings
-[`string.Template()`][string.Template()] is a class from the `string` module (_as opposed to the built-in `str` type_), which is part of the Python standard library, but has to be imported for use.
-Template strings support `$`-based substitution and are much simpler and less capable than the other options mentioned here, but can be very useful for when complicated internationalization is needed, or outside inputs need to be sanitized.
+[`string.Template()`][string.Template()] (_not to be confused with Python 3.14 [t-strings]_) is a class from the `string` module (_as opposed to the built-in `str` type_), which is part of the Python standard library, but has to be imported for use.
+Template strings support `$`-based substitution and are much simpler and less capable than the other options mentioned here.
+However, they can be very useful for when complicated internationalization is needed, or outside inputs need to be sanitized.
+`string.Template` is considered safer for untrusted user input because it prevents evaluating arbitrary expressions or accessing object attributes, which mitigates format-string injection attacks.
```python
>>> from string import Template
@@ -204,8 +269,8 @@ A few quick guidelines:
If you don't need to internationalize, they should be the Python 3.6+ preferred method.
2. `str.format()` is versatile, very powerful and compatible with both `gnu gettext` and most versions of Python.
3. If simplicity, safety, and/or heavy internationalization is what you need, `string.Template()` can be used to mitigate risks when inputs from users need to be handled, and for wrapping translation strings.
-4. The `%` operator is not supported in some newer distributions of Python and should mostly be used for compatibility with old code.
-`%` formatting` can lead to issues displaying non-ascii and unicode characters and has more errors and less functionality than other methods.
+4. The `%` operator is generally considered deprecated for new code, though it still works in modern Python. It should mostly be used for compatibility with older codebases.
+`%` formatting can lead to issues displaying non-ASCII and Unicode characters and has more errors and less functionality than other methods. Check your specific Python distribution for support details if you intend to use it.
If you want to go further: [all about formatting][all-about-formatting] and [Python String Formatting Best Practices][formatting best practices] are good places to start.
@@ -216,6 +281,7 @@ If you want to go further: [all about formatting][all-about-formatting] and [Pyt
[format-specifiers]: https://www.python.org/dev/peps/pep-3101/#standard-format-specifiers
[formatting best practices]: https://realpython.com/python-string-formatting/
[pep-0498]: https://peps.python.org/pep-0498
+[pep-0701]: https://peps.python.org/pep-0701/
[printf-style-docs]: https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
[repr-conversion]: https://www.w3resource.com/python/built-in-function/repr.php
[str-conversion]: https://www.w3resource.com/python/built-in-function/str.php
@@ -224,5 +290,6 @@ If you want to go further: [all about formatting][all-about-formatting] and [Pyt
[string.Template()]: https://docs.python.org/3/library/string.html#template-strings
[summary-string-format]: https://www.w3schools.com/python/ref_string_format.asp
[template-string]: https://docs.python.org/3/library/string.html#template-strings
+[t-strings]: https://realpython.com/python-t-strings/
[tuples]: https://www.w3schools.com/python/python_tuples.asp
[zen-of-python]: https://www.python.org/dev/peps/pep-0020/
diff --git a/concepts/string-formatting/introduction.md b/concepts/string-formatting/introduction.md
index aa476de9ca0..ed88cc4d01c 100644
--- a/concepts/string-formatting/introduction.md
+++ b/concepts/string-formatting/introduction.md
@@ -3,18 +3,20 @@
## String Formatting in Python
The [Zen of Python][zen-of-python] asserts there should be "one _obvious_ way to do something in Python".
-But when it comes to string formatting, things are a little .... _less zen_.
-It can be surprising to find out that there are **four** main ways to perform string formatting in Python - each for a different scenario.
-Some of this is due to Python's long history and some of it is due to considerations like internationalization or input sanitation.
+For Python 3.6+, **literal string interpolation** (**`f-strings`**) is often the obvious and preferred way to format strings:
-With 4 different paths to take, how do you decide what to use?
+```python
+>>> adjective = "easy"
+>>> f"This is an {adjective} way to format strings!"
+'This is an easy way to format strings!'
+```
-1. `f-strings` are the newest and easiest to read.
-If you don't need to internationalize, they should be the Python 3.6+ preferred method.
-2. `str.format()` is versatile, very powerful and compatible with both `gnu gettext` and most versions of Python.
-3. If simplicity, safety, and/or heavy internationalization is what you need, `string.Template()` can be used to mitigate risks when inputs need to be handled and for wrapping translation strings.
-4. The `%` operator should mostly be used for compatibility with old code.
-`%` formatting` can lead to issues displaying non-ascii and unicode characters and has more errors and less functionality than other methods.
+However, given Python's long history and different use-cases, it might not be surprising that there are **three** other common ways to perform string formatting in Python:
+
+1. `str.format()` is versatile, very powerful and compatible with both `gnu gettext` and most versions of Python.
+2. If simplicity, safety, and/or heavy internationalization is what you need, `string.Template()` can be used to mitigate risks when inputs need to be handled and for wrapping translation strings.
+3. The `%` operator is generally considered deprecated for new code, though it still works in modern Python.
+It should mostly be used for compatibility with older codebases. `%` formatting can lead to issues displaying non-ASCII and Unicode characters and has more errors and less functionality than other methods.Check your specific Python distribution for support details if you intend to use it.
If you want to go further: [all about formatting][all-about-formatting] and [Python String Formatting Best Practices][formatting best practices] are good places to start.
diff --git a/concepts/string-methods/about.md b/concepts/string-methods/about.md
index 4f9dcde5dd0..6c9c843b97c 100644
--- a/concepts/string-methods/about.md
+++ b/concepts/string-methods/about.md
@@ -6,13 +6,14 @@ This may include letters, diacritical marks, positioning characters, numbers, cu
Strings implement all [common sequence operations][common sequence operations] and can be iterated through using `for item in ` or `for index, item in enumerate()` syntax.
Individual code points (_strings of length 1_) can be referenced by `0-based index` number from the left, or `-1-based index` number from the right.
- Strings can be concatenated using ` + ` or `.join()`, split via `.split()`, and offer multiple formatting and assembly options.
+ Strings can be concatenated using ` + ` or `.join()` and split via `.split()`.
+ They also offer multiple other formatting and assembly options.
To further work with strings, Python provides a rich set of [string methods][str-methods] for searching, cleaning, transforming, translating, and many other operations.
Some of the more commonly used `str` methods include:
-- Checking for prefixes/suffixes with `startswith()` and `endswith()`
+- Checking for prefixes/suffixes with `.startswith()` and `.endswith()`
- Altering string casing with methods like `.title()`, `.upper()`/`.lower()`, and `.swapcase()`
- Removing leading or trailing characters from a string using `.strip()`, `.lstrip()`, or `.rstrip()`
- Replacing substrings with the `.replace(, )` method
@@ -33,7 +34,7 @@ True
>>> 'Do you want to 💃?'.endswith('💃')
False
->> 'The quick brown fox jumped over the lazy dog.'.endswith('dog')
+>>> 'The quick brown fox jumped over the lazy dog.'.endswith('dog')
False
```
@@ -117,7 +118,7 @@ Just the place for a Snark! I have said it thrice:
'book keeper'
```
-:star:**Newly added in Python `3.9`**
+🌟**Newly added in Python `3.9`**
Python `3.9` introduces two new string methods that make removing prefixes and suffixes much easier.
@@ -144,7 +145,7 @@ Python `3.9` introduces two new string methods that make removing prefixes and s
For more examples and methods the [informal tutorial][informal tutorial] is a nice jumping-off point.
[How to Unicode][howto unicode] in the Python docs offers great detail on Unicode, encoding, bytes, and other technical considerations for working with strings in Python.
-Python also supports regular expressions via the `re` module, which will be covered in a future exercise.
+Python also supports regular expressions via the `re` module, which will be covered in a future concept.
[Lewis Carroll]: https://www.poetryfoundation.org/poets/lewis-carroll
diff --git a/concepts/strings/about.md b/concepts/strings/about.md
index fe49f6208a5..19920adad31 100644
--- a/concepts/strings/about.md
+++ b/concepts/strings/about.md
@@ -4,12 +4,13 @@ A `str` in Python is an [immutable sequence][text sequence] of [Unicode code poi
These may include letters, diacritical marks, positioning characters, numbers, currency symbols, emoji, punctuation, space and line break characters, and more.
For a deep dive on what information a string encodes (or, _"how does a computer know how to translate zeroes and ones into letters?"_), [this blog post is enduringly helpful][joel-on-text].
-The Python docs also provide a very detailed [unicode HOWTO][unicode how-to] that discusses Pythons support for the Unicode specification in the `str`, `bytes` and `re` modules, considerations for locales, and some common issues with encoding and translation.
+The Python docs also provide a very detailed [unicode HOWTO][unicode how-to] that discusses Python's support for the Unicode specification in the `str`, `bytes` and `re` modules, considerations for locales, and some common issues with encoding and translation.
Strings implement all [common sequence operations][common sequence operations] and can be iterated through using `for item in ` or `for index, item in enumerate()` syntax.
Individual code points (_strings of length 1_) can be referenced by `0-based index` number from the left, or `-1-based index` number from the right.
-Strings can be concatenated with ` + `, or `.join()`, split via `.split()`, and offer multiple formatting, assembly, and templating options.
+Strings can be concatenated with ` + ` or `.join()` and split via `.split()`.
+They also offer multiple additional formatting, assembly, and templating options.
A `str` literal can be declared using single `'` or double `"` quotes. The escape `\` character is available as needed.
@@ -101,7 +102,7 @@ There is no separate “character” or "rune" type in Python, so indexing a str
True
```
-Substrings can be selected via _slice notation_, using [`[:stop:]`][common sequence operations] to produce a new string.
+Substrings can be selected via _slice notation_, using [`[::]`][common sequence operations] to produce a new string.
Results exclude the `stop` index.
If no `start` is given, the starting index will be 0.
If no `stop` is given, the `stop` index will be the end of the string.
diff --git a/concepts/strings/introduction.md b/concepts/strings/introduction.md
index e213b0fbea4..22770fde4c8 100644
--- a/concepts/strings/introduction.md
+++ b/concepts/strings/introduction.md
@@ -5,12 +5,13 @@ These could include letters, diacritical marks, positioning characters, numbers,
Strings implement all [common sequence operations][common sequence operations], and can be iterated through using `for item in ` or `for index, item in enumerate()` syntax.
-Strings can be concatenated with ` + `, or `.join()`, split via `.split(