Skip to content

Commit 39821ab

Browse files
BethanyGYrahcaz7
andauthored
[Leap Approaches]: Typos & Fixes per issue 4197 (#4252)
* Typos and fixes per issue 4197. * Apply suggestions from code review Applied suggestions from review. Co-authored-by: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> * Changed datetime snippet and datetime content per code review. --------- Co-authored-by: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com>
1 parent 4d03daf commit 39821ab

8 files changed

Lines changed: 106 additions & 73 deletions

File tree

exercises/practice/leap/.approaches/boolean-chain/content.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ def leap_year(year):
88

99
This might be considered the "most idiomatic" or "most Pythonic" solution, as it is exactly the same as the code implemented by the maintainers of the Python language for the [`calendar.isleap()`][isleap-source] method.
1010

11-
The first boolean expression uses the [modulo operator][modulo-operator] to check if the year is evenly divided by `4`.
12-
- If the year is _not_ evenly divisible by `4`, then the chain will [short circuit][short-ciruiting] due to the next operator being a [logical AND][logical-and] {`and`), and will return `False`.
11+
The first boolean expression uses the [modulo operator][modulo-operator] to check if the year is evenly divisible by `4`.
12+
- If the year is _not_ evenly divisible by `4`, then the chain will [short circuit][short-ciruiting] due to the next operator being a [logical AND][logical-and] (`and`), and will return `False`.
1313
- If the year _is_ evenly divisible by `4`, then the year is checked to _not_ be evenly divisible by `100`.
14-
- If the year is not evenly divisible by `100`, then the expression is `True` and the interpreter will stop the evaluation to return `True`, since the next operator is a [logical OR][logical-or] (`or`).
15-
- If the year _is_ evenly divisible by `100`, then the expression is `False`, and the returned value from the chain will be if the year is evenly divisible by `400`.
14+
- If the year is not evenly divisible by `100`, the expression is `True` and the chain will short circuit and return `True`, since the next operator is a [logical OR][logical-or] (`or`).
15+
- If the year _is_ evenly divisible by `100`, then the expression is `False`, and the returned value from the chain will be `True` if the year is evenly divisible by `400`.
1616

1717

1818
| year | year % 4 == 0 | year % 100 != 0 | year % 400 == 0 | is leap year |
@@ -37,10 +37,10 @@ In Python, `a and b or c` is interpreted as `(a and b) or c`, which would give t
3737
If in doubt, it is always permissible to add extra parentheses for clarity.
3838

3939

40-
## Refactoring
40+
## Variation 1
41+
42+
By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`:
4143

42-
By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
43-
For example:
4444

4545
```python
4646
def leap_year(year):
@@ -50,10 +50,10 @@ def leap_year(year):
5050

5151
It can be thought of as the expression _not_ having a remainder.
5252

53-
[modulo-operator]: https://realpython.com/python-modulo-operator/
53+
[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/#falsy-and-truthy-values
54+
[isleap-source]: https://github.com/python/cpython/blob/3.13/Lib/calendar.py#L143-L145
5455
[logical-and]: https://realpython.com/python-and-operator/
5556
[logical-or]: https://realpython.com/python-or-operator/
56-
[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
57+
[modulo-operator]: https://realpython.com/python-modulo-operator/
5758
[not-operator]: https://realpython.com/python-not-operator/
5859
[short-ciruiting]: https://mathspp.com/blog/pydonts/boolean-short-circuiting#short-circuiting-in-plain-english
59-
[isleap-source]: https://github.com/python/cpython/blob/main/Lib/calendar.py#L141-L143
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
11
# The `calendar.isleap()` function
22

3-
```pythoon
3+
```python
44
from calendar import isleap
55

66
def leap_year(year):
77
return isleap(year)
88
```
99

1010
~~~~exercism/caution
11-
This approach may be considered a "cheat" for this exercise, which is intended to practice Boolean operators and logic.
11+
This approach may be considered a "cheat" for this exercise, which is intended to practice Boolean operators and Boolean logic.
1212
~~~~
1313

1414

1515
The Python standard library includes a [`calendar`][calendar] module for working with many aspects of dates in the [Gregorian calendar][gregorian-calendar].
16-
1716
One of the methods provided is [`isleap()`][isleap], which implements exactly the same functionality as this exercise.
1817

1918
This is not a good way to practice the use of Booleans, as the exercise intends.
20-
However, it may be convenient (_and better tested_) if you are working with calendar functions more broadly.
19+
However, it may be convenient (_and better tested_) if you are working with `calendar` functions more broadly.
20+
2121

2222
## The library function
2323

24-
This is the [implementation][implementation]:
24+
This is the actual [implementation][implementation] in the CPython code:
2525

2626
```python
2727
def isleap(year):
2828
"""Return True for leap years, False for non-leap years."""
2929
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
3030
```
3131

32-
We can see that `calendar.isleap()` is just syntactic sugar for the `boolean-chain` approach.
33-
32+
We can see that `calendar.isleap()` is just syntactic sugar for the [`boolean-chain`][approach-boolean-chain] approach.
3433

34+
[approach-boolean-chain]: https://exercism.org/tracks/python/exercises/leap/approaches/boolean-chain
3535
[calendar]: https://docs.python.org/3/library/calendar.html
3636
[gregorian-calendar]: https://en.wikipedia.org/wiki/Gregorian_calendar
37-
[implementation]: https://github.com/python/cpython/blob/main/Lib/calendar.py
37+
[implementation]: https://github.com/python/cpython/blob/3.13/Lib/calendar.py#L143-L145
3838
[isleap]: https://docs.python.org/3/library/calendar.html
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,41 @@
11
{
22
"introduction": {
33
"authors": ["bobahop"],
4-
"contributors": ["colinleach"]
4+
"contributors": ["colinleach", "Yrahcaz7", "Bethanyg"]
55
},
66
"approaches": [
77
{
88
"uuid": "5d42dc83-2473-425a-90bd-bf03f92b8c8b",
99
"slug": "boolean-chain",
1010
"title": "Boolean chain",
1111
"blurb": "Use a chain of boolean expressions.",
12-
"authors": ["bobahop"]
12+
"authors": ["bobahop"],
13+
"contributors": ["colinleach", "Yrahcaz7", "Bethanyg"]
1314
},
1415
{
15-
"uuid": "9952fef5-9f2f-4575-94fc-bc4e96593cd6",
16+
"uuid": "37193c94-1b5f-4891-a685-11def9204839",
1617
"slug": "ternary-operator",
1718
"title": "Ternary operator",
1819
"blurb": "Use a ternary operator of boolean expressions.",
19-
"authors": ["bobahop"]
20+
"authors": ["bobahop"],
21+
"contributors": ["colinleach", "Yrahcaz7", "Bethanyg"]
2022
},
2123
{
2224
"uuid": "66302791-0770-4f08-beaa-251c49e280a2",
2325
"slug": "datetime-addition",
2426
"title": "datetime addition",
2527
"blurb": "Use datetime addition.",
26-
"authors": ["bobahop"]
28+
"authors": ["bobahop"],
29+
"contributors": ["colinleach", "Yrahcaz7", "Bethanyg"]
2730
},
2831
{
2932
"uuid": "d85be356-211a-4d2f-8af0-fa92e390b0b3",
3033
"slug": "calendar-isleap",
3134
"title": "calendar.isleap() function",
3235
"blurb": "Use the calendar module.",
33-
"authors": ["colinleach",
34-
"BethanyG"]
36+
"authors": ["colinleach", "BethanyG"],
37+
"contributors": ["Yrahcaz7"]
3538
}
3639
]
3740
}
41+
Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,30 @@
11
# `datetime` addition
22

33
```python
4-
import datetime
4+
from datetime import datetime, timedelta
55

66

77
def leap_year(year):
8-
return (datetime.datetime(year, 2, 28)
9-
+ datetime.timedelta(days=1)).day == 29
10-
8+
return (datetime(year, 2, 28)
9+
+ timedelta(days=1)).day == 29
1110
```
1211

13-
~~~~exercism/caution
14-
This approach may be considered a "cheat" for this exercise, which is intended to practice Boolean operators and logic.
15-
It also adds a tremendous amount of overhead in both performance and memory, as it imports all of the `datetime` module and requires the instantiation of both a `datetime` object and a `datetime.timedelta` object.
12+
~~~~exercism/note
13+
This approach may be considered a "cheat" for this exercise, which is intended to practice Boolean operators and boolean logic.
14+
It also adds overhead in both performance and memory, as it imports methods from the `datetime` module and requires the instantiation of both a `datetime` object and a `timedelta` object.
1615
17-
For more information, see this exercises performance article.
16+
For more information, see the performance article for this exercise.
1817
~~~~
1918

20-
By adding a day to February 28th for a given year, you can see if the new day falls on the 29th of February, or the 1st of March.
19+
20+
By adding a day to February 28th for a given year, you can see if the new day falls on the 29th of February or the 1st of March.
2121
If it is February 29th, then the function returns `True` for the year being a leap year.
22+
The exact steps are as follows:
2223

23-
- A new [datetime][datetime] object is created for February 28th of the year.
24-
- Then the [timedelta][timedelta] of one day is added to that `datetime`,
25-
and the function returns if the [day][day] property of the resulting `datetime` object is the 29th.
24+
- A new [`datetime`][datetime] object is created for February 28th of the year.
25+
- A [`timedelta`][timedelta] of one day is added to that `datetime`.
26+
- The function returns if the [`day`][day] property of the resulting `datetime` object is the 29th.
2627

27-
[timedelta]: https://docs.python.org/3/library/datetime.html#timedelta-objects
28-
[day]: https://docs.python.org/3/library/datetime.html#datetime.datetime.day
2928
[datetime]: https://docs.python.org/3/library/datetime.html#datetime-objects
29+
[day]: https://docs.python.org/3/library/datetime.html#datetime.datetime.day
30+
[timedelta]: https://docs.python.org/3/library/datetime.html#timedelta-objects
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import datetime
1+
from datetime import datetime, timedelta
22

33

44
def leap_year(year):
5-
return (datetime.datetime(year, 2, 28)
6-
+ datetime.timedelta(days=1)).day == 29
5+
return (datetime(year, 2, 28) + timedelta(days=1)).day == 29

exercises/practice/leap/.approaches/introduction.md

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
# Introduction
22

3-
There are multiple idiomatic approaches to solving the Leap exercise.
4-
You can use a chain of boolean expressions to test the conditions, a [ternary operator][ternary-operator], or built-in methods from the `datetime` or `calendar` modules.
3+
There are multiple approaches to solving the Leap exercise in Python.
4+
You can use a chain of boolean expressions or a [`ternary operator`][ternary-operator] to test conditions.
5+
You can also utilize built-in methods from the `datetime` or `calendar` modules.
56

67

78
## General Guidance
89

910
The key to efficiently solving Leap is to calculate if the year is evenly divisible by `4`, `100` and `400`.
10-
For determining that, you will use the [modulo operator][modulo-operator].
11+
Using the [modulo operator][modulo-operator] is a good way of determining that.
1112

1213

1314
## Approach: Chain of Boolean Expressions
1415

16+
1517
```python
1618
def leap_year(year):
1719
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
@@ -34,45 +36,67 @@ For more information, see the [Ternary operator approach][approach-ternary-opera
3436

3537
## Other Approaches
3638

37-
Besides the aforementioned, idiomatic approaches, you could also approach the exercise as follows:
39+
Besides the aforementioned idiomatic approaches, you could also approach the exercise as follows:
3840

3941

40-
### Approach: `datetime` Addition
42+
### Approach: Using `datetime` Addition
4143

4244
Add a day to February 28th for the year and see if the new day is the 29th.
4345
However, this approach may trade speed for convenience.
46+
47+
```python
48+
import datetime
49+
50+
def leap_year(year):
51+
return (datetime.datetime(year, 2, 28) + datetime.timedelta(days=1)).day == 29
52+
```
53+
4454
For more information, see the [`datetime` addition approach][approach-datetime-addition].
4555

4656

47-
### Approach: The `calendar` module
57+
### Approach: Using the `calendar` module
4858

49-
It is possible to use `calendar.isleap(year)` from the standard library, which solves this exact problem.
59+
It is possible to use [`calendar.isleap(<year>)`][isleap] from the standard library, which solves this exact problem:
5060

51-
This is self-defeating in an Exercism practice exercise intended to explore ways to use booleans.
52-
In a wider context, anyone testing for leap years may already be using `calendar` or related modules, and it is good to know what library functions are available.
61+
62+
```python
63+
from calendar import isleap
64+
65+
def leap_year(year):
66+
return isleap(year)
67+
```
68+
69+
This is self-defeating in the context of Exercism.
70+
This practice exercise was designed to explore ways to use `booleans` and boolean logic.
71+
The point is not _really_ leap year determination.
72+
In a wider context, anyone testing for leap years is likely using `calendar` or related modules (_hand rolling your own test is likely to introduce bugs_), so it is good to know what library functions are available.
73+
74+
For more discussion, see the [`calendar.isleap()` approach][approach-calendar-isleap].
5375

5476

5577
## Which approach to use?
5678

57-
- The chain of boolean expressions should be the most efficient, as it proceeds from the most to least likely conditions and takes advantage of short-circuiting.
79+
- The chain of `boolean expressions` should be the most efficient, as it proceeds from most to least likely conditions and takes advantage of [`short-circuiting`][short-circuting].
5880
It has a maximum of three checks.
5981
It is the fastest approach when testing a year that is not evenly divisible by `100` that is not a leap year.
6082
Since most years fit those conditions, it is overall the most efficient approach.
6183
It also happens to be the approach taken by the maintainers of the Python language in [implementing `calendar.isleap()`][calendar_isleap-code].
6284

63-
64-
- The ternary operator approach has a maximum of only two checks, but it starts from a less likely condition.
65-
The ternary operator was faster in benchmarking when the year was a leap year or was evenly divisible by `100`,
66-
but those are the _least likely_ conditions.
67-
- Using `datetime` addition may be considered a "cheat" for the exercise, and it was slower by far than the other approaches in benchmarking.
85+
- The `ternary operator` approach has a maximum of only two checks, but it starts from a less likely condition.
86+
The `ternary operator` was faster in benchmarking when the year was a leap year or was evenly divisible by `100`,
87+
but those are the _least likely_ conditions!
88+
- Using `datetime` addition may be considered a "cheat" for the exercise, and it was slower by far than the other approaches in benchmarking, due to import and method overhead.
6889

6990
For more information, check out the [Performance article][article-performance].
7091

92+
7193
[approach-boolean-chain]: https://exercism.org/tracks/python/exercises/leap/approaches/boolean-chain
94+
[approach-calendar-isleap]: https://exercism.org/tracks/python/exercises/leap/approaches/calendar-isleap
7295
[approach-datetime-addition]: https://exercism.org/tracks/python/exercises/leap/approaches/datetime-addition
7396
[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/leap/approaches/ternary-operator
7497
[article-performance]: https://exercism.org/tracks/python/exercises/leap/articles/performance
75-
[calendar_isleap-code]: https://github.com/python/cpython/blob/3.9/Lib/calendar.py#L100-L102
98+
[calendar_isleap-code]: https://github.com/python/cpython/blob/3.13/Lib/calendar.py#L143-L145
99+
[isleap]: https://docs.python.org/3/library/calendar.html#calendar.isleap
76100
[modulo-operator]: https://realpython.com/python-modulo-operator/
101+
[short-circuting]: https://www.pythonmorsels.com/short-circuit-evaluation/
77102
[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
78-

exercises/practice/leap/.approaches/ternary-operator/content.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ def leap_year(year):
66

77
```
88

9-
A [ternary operator][ternary-operator] uses a maximum of two checks to determine if a year is a leap year.
9+
A [`ternary operator`][ternary-operator] uses a maximum of two checks to determine if a year is a leap year.
1010

1111
It starts by testing the outlier condition of the year being evenly divisible by `100`.
1212
It does this by using the [modulo operator][modulo-operator].
1313
Also, by using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
1414

15-
- If the year is evenly divisible by `100`, then the expression is `True`, and the ternary operator returns if the year is evenly divisible by `400`.
16-
- If the year is _not_ evenly divisible by `100`, then the expression is `False`, and the ternary operator returns if the year is evenly divisible by `4`.
15+
- If the year is evenly divisible by `100`, then the ternary operator returns `True` if the year is evenly divisible by `400`.
16+
- If the year is _not_ evenly divisible by `100`, the ternary operator returns `True` if the year is evenly divisible by `4`.
17+
1718

1819
| year | year % 100 == 0 | year % 400 == 0 | year % 4 == 0 | is leap year |
1920
| ---- | --------------- | --------------- | -------------- | ------------ |
@@ -22,13 +23,14 @@ Also, by using the [falsiness][falsiness] of `0`, the [`not` operator][not-opera
2223
| 2000 | True | True | not evaluated | True |
2324
| 1900 | True | False | not evaluated | False |
2425

26+
2527
Although it uses a maximum of only two checks, the ternary operator tests an outlier condition first,
26-
making it less efficient than another approach that would first test if the year is evenly divisible by `4`,
27-
which is more likely than the year being evenly divisible by `100`.
28+
making it less efficient than the [boolean chain approach][approach-boolean-chain] that first tests if the year is evenly divisible by `4` (_which is more likely than the year being evenly divisible by `100`_).
2829
The ternary operator was fastest in benchmarking when the year was a leap year or was evenly divisible by `100`,
2930
but those are the least likely conditions.
3031

31-
[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
32+
[approach-boolean-chain]: https://exercism.org/tracks/python/exercises/leap/approaches/boolean-chain
33+
[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/#falsy-and-truthy-values
3234
[modulo-operator]: https://realpython.com/python-modulo-operator/
33-
[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
3435
[not-operator]: https://realpython.com/python-not-operator/
36+
[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/

0 commit comments

Comments
 (0)