From 826ce614ed6c7851155a3aa2c57c3a7a4b75acbc Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:16:21 -0700 Subject: [PATCH 01/47] Updated docstring to follow Google Style instead of ReText. (#4154) --- exercises/concept/black-jack/black_jack.py | 71 ++++++++++++++-------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/exercises/concept/black-jack/black_jack.py b/exercises/concept/black-jack/black_jack.py index 9d99e11a8f..fd5cedb20a 100644 --- a/exercises/concept/black-jack/black_jack.py +++ b/exercises/concept/black-jack/black_jack.py @@ -8,12 +8,15 @@ def value_of_card(card): """Determine the scoring value of a card. - :param card: str - given card. - :return: int - value of a given card. See below for values. + Parameters: + card (str): The given card. - 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 - 2. 'A' (ace card) = 1 - 3. '2' - '10' = numerical value. + Returns: + int: The value of a given card. See below for values. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 1 + 3. '2' - '10' = numerical value. """ pass @@ -22,12 +25,16 @@ def value_of_card(card): def higher_card(card_one, card_two): """Determine which card has a higher value in the hand. - :param card_one, card_two: str - cards dealt in hand. See below for values. - :return: str or tuple - resulting Tuple contains both cards if they are of equal value. + Parameters: + card_one (str): First card dealt in the hand. See below for values. + card_two (str): Second card dealt in the hand. See below for values. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 1 + 3. '2' - '10' = numerical value. - 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 - 2. 'A' (ace card) = 1 - 3. '2' - '10' = numerical value. + Returns: + str or tuple: The resulting Tuple contains both cards if they are of equal value. """ pass @@ -36,12 +43,16 @@ def higher_card(card_one, card_two): def value_of_ace(card_one, card_two): """Calculate the most advantageous value for an upcoming ace card. - :param card_one, card_two: str - card dealt. See below for values. - :return: int - either 1 or 11 value of the upcoming ace card. + Parameters: + card_one (str): First card dealt in the hand. See below for values. + card_two (str): Second card dealt in the hand. See below for values. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. - 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 - 2. 'A' (ace card) = 11 (if already in hand) - 3. '2' - '10' = numerical value. + Returns: + int: Either 1 or 11, which is the value of the upcoming ace card. """ pass @@ -50,12 +61,16 @@ def value_of_ace(card_one, card_two): def is_blackjack(card_one, card_two): """Determine if the hand is a 'natural' or 'blackjack'. - :param card_one, card_two: str - card dealt. See below for values. - :return: bool - is the hand is a blackjack (two cards worth 21). + Parameters: + card_one (str): First card dealt in the hand. See below for values. + card_two (str): Second card dealt in the hand. See below for values. - 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 - 2. 'A' (ace card) = 11 (if already in hand) - 3. '2' - '10' = numerical value. + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. + + Returns: + bool: Is the hand is a blackjack (two cards worth 21). """ pass @@ -64,8 +79,12 @@ def is_blackjack(card_one, card_two): def can_split_pairs(card_one, card_two): """Determine if a player can split their hand into two hands. - :param card_one, card_two: str - cards dealt. - :return: bool - can the hand be split into two pairs? (i.e. cards are of the same value). + Parameters: + card_one (str): First card in the hand. + card_two (str): Second card in the hand. + + Returns: + bool: Can the hand be split into two pairs? (i.e. cards are of the same value). """ pass @@ -74,8 +93,12 @@ def can_split_pairs(card_one, card_two): def can_double_down(card_one, card_two): """Determine if a blackjack player can place a double down bet. - :param card_one, card_two: str - first and second cards in hand. - :return: bool - can the hand can be doubled down? (i.e. totals 9, 10 or 11 points). + Parameters: + card_one (str): First card in the hand. + card_two (str): Second card in the hand. + + Returns: + bool: Can the hand can be doubled down? (i.e. totals 9, 10 or 11 points). """ pass From c277620ba8e39a6785c1b7d570f1dfbf0796a7a3 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:17:52 -0700 Subject: [PATCH 02/47] [List Methods Concept Exercise]: Update docstrings for `chaitanas-collossal-coaster` (#4144) * Updated stub docstrings to match google style. * Reformatted docstrings in stub to align with Google Style guide. * Reverting change to currency exchange since this is a different exercise. --- .../list_methods.py | 65 ++++++++++++------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/exercises/concept/chaitanas-colossal-coaster/list_methods.py b/exercises/concept/chaitanas-colossal-coaster/list_methods.py index 6603b5adf6..5a83088e3a 100644 --- a/exercises/concept/chaitanas-colossal-coaster/list_methods.py +++ b/exercises/concept/chaitanas-colossal-coaster/list_methods.py @@ -4,11 +4,14 @@ def add_me_to_the_queue(express_queue, normal_queue, ticket_type, person_name): """Add a person to the 'express' or 'normal' queue depending on the ticket number. - :param express_queue: list - names in the Fast-track queue. - :param normal_queue: list - names in the normal queue. - :param ticket_type: int - type of ticket. 1 = express, 0 = normal. - :param person_name: str - name of person to add to a queue. - :return: list - the (updated) queue the name was added to. + Parameters: + express_queue (list): The names in the Fast-track queue. + normal_queue (list): The names in the normal queue. + ticket_type (int): Type of ticket. 1 = express, 0 = normal. + person_name (str): The name of person to add to a queue. + + Returns: + list: The (updated) queue the name was added to. """ pass @@ -17,9 +20,12 @@ def add_me_to_the_queue(express_queue, normal_queue, ticket_type, person_name): def find_my_friend(queue, friend_name): """Search the queue for a name and return their queue position (index). - :param queue: list - names in the queue. - :param friend_name: str - name of friend to find. - :return: int - index at which the friends name was found. + Parameters: + queue (list): The names in the queue. + friend_name (str): The name of friend to find. + + Returns: + int: The index at which the friends name was found. """ pass @@ -28,10 +34,13 @@ def find_my_friend(queue, friend_name): def add_me_with_my_friends(queue, index, person_name): """Insert the late arrival's name at a specific index of the queue. - :param queue: list - names in the queue. - :param index: int - the index at which to add the new name. - :param person_name: str - the name to add. - :return: list - queue updated with new name. + Parameters: + queue (list): The names in the queue. + index (int): The index at which to add the new name. + person_name (str): The name to add. + + Returns: + list: The queue updated with new name. """ pass @@ -40,9 +49,12 @@ def add_me_with_my_friends(queue, index, person_name): def remove_the_mean_person(queue, person_name): """Remove the mean person from the queue by the provided name. - :param queue: list - names in the queue. - :param person_name: str - name of mean person. - :return: list - queue update with the mean persons name removed. + Parameters: + queue (list): The names in the queue. + person_name (str): The name of mean person. + + Returns: + list: The queue updated with the mean persons name removed. """ pass @@ -51,9 +63,12 @@ def remove_the_mean_person(queue, person_name): def how_many_namefellows(queue, person_name): """Count how many times the provided name appears in the queue. - :param queue: list - names in the queue. - :param person_name: str - name you wish to count or track. - :return: int - the number of times the name appears in the queue. + Parameters: + queue (list): The names in the queue. + person_name (str): The name you wish to count or track. + + Returns: + int: The number of times the name appears in the queue. """ pass @@ -62,8 +77,11 @@ def how_many_namefellows(queue, person_name): def remove_the_last_person(queue): """Remove the person in the last index from the queue and return their name. - :param queue: list - names in the queue. - :return: str - name that has been removed from the end of the queue. + Parameters: + queue (list): The names in the queue. + + Returns: + str: The name that has been removed from the end of the queue. """ pass @@ -72,8 +90,11 @@ def remove_the_last_person(queue): def sorted_names(queue): """Sort the names in the queue in alphabetical order and return the result. - :param queue: list - names in the queue. - :return: list - copy of the queue in alphabetical order. + Parameters: + queue (list): The names in the queue. + + Returns: + list: A copy of the queue in alphabetical order. """ pass From 071fc465b5823bc61a37c8d94b29edb7408e6629 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:18:23 -0700 Subject: [PATCH 03/47] Updated stub docstrings to follow Google style. (#4145) --- .../meltdown-mitigation/conditionals.py | 66 +++++++++++-------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/exercises/concept/meltdown-mitigation/conditionals.py b/exercises/concept/meltdown-mitigation/conditionals.py index ff5769d835..01bbf5af4d 100644 --- a/exercises/concept/meltdown-mitigation/conditionals.py +++ b/exercises/concept/meltdown-mitigation/conditionals.py @@ -4,14 +4,19 @@ def is_criticality_balanced(temperature, neutrons_emitted): """Verify criticality is balanced. - :param temperature: int or float - temperature value in kelvin. - :param neutrons_emitted: int or float - number of neutrons emitted per second. - :return: bool - is criticality balanced? - - A reactor is said to be balanced in criticality if it satisfies the following conditions: - - The temperature is less than 800 K. - - The number of neutrons emitted per second is greater than 500. - - The product of temperature and neutrons emitted per second is less than 500000. + Parameters: + temperature (int or float): The temperature value in kelvin. + neutrons_emitted (int or float): The number of neutrons emitted per second. + + Returns: + bool: Is criticality balanced? + + Note: + A reactor is said to be balanced in criticality if it satisfies the following conditions: + - The temperature is less than 800 K. + - The number of neutrons emitted per second is greater than 500. + - The product of temperature and neutrons emitted per second is less than 500000. + """ pass @@ -20,21 +25,24 @@ def is_criticality_balanced(temperature, neutrons_emitted): def reactor_efficiency(voltage, current, theoretical_max_power): """Assess reactor efficiency zone. - :param voltage: int or float - voltage value. - :param current: int or float - current value. - :param theoretical_max_power: int or float - power that corresponds to a 100% efficiency. - :return: str - one of ('green', 'orange', 'red', or 'black'). + Parameters: + voltage (int or float): Voltage value. + current (int or float): Current value. + theoretical_max_power (int or float): The power level that corresponds to a 100% efficiency. - Efficiency can be grouped into 4 bands: + Returns: + str: One of ('green', 'orange', 'red', or 'black'). - 1. green -> efficiency of 80% or more, - 2. orange -> efficiency of less than 80% but at least 60%, - 3. red -> efficiency below 60%, but still 30% or more, - 4. black -> less than 30% efficient. + Note: + Efficiency can be grouped into 4 bands: + 1. green -> efficiency of 80% or more, + 2. orange -> efficiency of less than 80% but at least 60%, + 3. red -> efficiency below 60%, but still 30% or more, + 4. black -> less than 30% efficient. - The percentage value is calculated as - (generated power/ theoretical max power)*100 - where generated power = voltage * current + The percentage value is calculated as + (generated power/ theoretical max power)*100 + where generated power = voltage * current """ pass @@ -43,14 +51,18 @@ def reactor_efficiency(voltage, current, theoretical_max_power): def fail_safe(temperature, neutrons_produced_per_second, threshold): """Assess and return status code for the reactor. - :param temperature: int or float - value of the temperature in kelvin. - :param neutrons_produced_per_second: int or float - neutron flux. - :param threshold: int or float - threshold for category. - :return: str - one of ('LOW', 'NORMAL', 'DANGER'). + Parameters: + temperature (int or float): The value of the temperature in kelvin. + neutrons_produced_per_second (int or float): The neutron flux. + threshold (int or float): The threshold for the category. + + Returns: + str: One of ('LOW', 'NORMAL', 'DANGER'). - 1. 'LOW' -> `temperature * neutrons per second` < 90% of `threshold` - 2. 'NORMAL' -> `temperature * neutrons per second` +/- 10% of `threshold` - 3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges + Note: + 1. 'LOW' -> `temperature * neutrons per second` < 90% of `threshold` + 2. 'NORMAL' -> `temperature * neutrons per second` +/- 10% of `threshold` + 3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges """ pass From f2e19887213f12ebf7ed1ded705355616b9f2208 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:18:49 -0700 Subject: [PATCH 04/47] Updated docstrings in stub to use Google Style. (#4146) --- .../ghost-gobble-arcade-game/arcade_game.py | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/exercises/concept/ghost-gobble-arcade-game/arcade_game.py b/exercises/concept/ghost-gobble-arcade-game/arcade_game.py index b2848e0c71..adb546aa1c 100644 --- a/exercises/concept/ghost-gobble-arcade-game/arcade_game.py +++ b/exercises/concept/ghost-gobble-arcade-game/arcade_game.py @@ -4,9 +4,13 @@ def eat_ghost(power_pellet_active, touching_ghost): """Verify that Pac-Man can eat a ghost if he is empowered by a power pellet. - :param power_pellet_active: bool - does the player have an active power pellet? - :param touching_ghost: bool - is the player touching a ghost? - :return: bool - can a ghost be eaten? + Parameters: + power_pellet_active (bool): Does the player have an active power pellet? + touching_ghost (bool): Is the player touching a ghost? + + Returns: + bool: Can a ghost be eaten? + """ pass @@ -15,9 +19,13 @@ def eat_ghost(power_pellet_active, touching_ghost): def score(touching_power_pellet, touching_dot): """Verify that Pac-Man has scored when a power pellet or dot has been eaten. - :param touching_power_pellet: bool - is the player touching a power pellet? - :param touching_dot: bool - is the player touching a dot? - :return: bool - has the player scored or not? + Parameters: + touching_power_pellet (bool): Is the player touching a power pellet? + touching_dot (bool): Is the player touching a dot? + + Returns: + bool: Has the player scored or not? + """ pass @@ -26,9 +34,12 @@ def score(touching_power_pellet, touching_dot): def lose(power_pellet_active, touching_ghost): """Trigger the game loop to end (GAME OVER) when Pac-Man touches a ghost without his power pellet. - :param power_pellet_active: bool - does the player have an active power pellet? - :param touching_ghost: bool - is the player touching a ghost? - :return: bool - has the player lost the game? + Parameters: + power_pellet_active (bool): Does the player have an active power pellet? + touching_ghost (bool): Is the player touching a ghost? + + Returns: + bool: Has the player lost the game? """ pass @@ -37,10 +48,13 @@ def lose(power_pellet_active, touching_ghost): def win(has_eaten_all_dots, power_pellet_active, touching_ghost): """Trigger the victory event when all dots have been eaten. - :param has_eaten_all_dots: bool - has the player "eaten" all the dots? - :param power_pellet_active: bool - does the player have an active power pellet? - :param touching_ghost: bool - is the player touching a ghost? - :return: bool - has the player won the game? + Parameters: + has_eaten_all_dots (bool): Has the player "eaten" all the dots? + power_pellet_active (bool): Does the player have an active power pellet? + touching_ghost (bool): Is the player touching a ghost? + + Returns: + bool: Has the player won the game? """ pass From b2f2172e7935804b19d77a89477169f149c979fe Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:19:14 -0700 Subject: [PATCH 05/47] Updated docstrings in stub to follow Google Style. (#4147) --- .../concept/ellens-alien-game/classes.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/exercises/concept/ellens-alien-game/classes.py b/exercises/concept/ellens-alien-game/classes.py index a9a3d1edae..f3fbe84383 100644 --- a/exercises/concept/ellens-alien-game/classes.py +++ b/exercises/concept/ellens-alien-game/classes.py @@ -4,22 +4,22 @@ class Alien: """Create an Alien object with location x_coordinate and y_coordinate. - Attributes - ---------- - (class)total_aliens_created: int - x_coordinate: int - Position on the x-axis. - y_coordinate: int - Position on the y-axis. - health: int - Number of health points. - - Methods - ------- - hit(): Decrement Alien health by one point. - is_alive(): Return a boolean for if Alien is alive (if health is > 0). - teleport(new_x_coordinate, new_y_coordinate): Move Alien object to new coordinates. - collision_detection(other): Implementation TBD. + Attributes: + (class) total_aliens_created (int): Total number of Alien instances. + x_coordinate (int): Position on the x-axis. + y_coordinate (int): Position on the y-axis. + health (int): Number of health points. + + Methods: + hit(): Decrement Alien health by one point. + is_alive(): Return a boolean for if Alien is alive (if health is > 0). + teleport(new_x_coordinate, new_y_coordinate): Move Alien object to new coordinates. + collision_detection(other): Implementation TBD. + """ pass -#TODO: create the new_aliens_collection() function below to call your Alien class with a list of coordinates. +#TODO (Student): Create the new_aliens_collection() function below to call your Alien class with a list of coordinates + From a8c73eebaad5d6ddb764909de2b6ab429803571c Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:19:51 -0700 Subject: [PATCH 06/47] Updated docstrings in stub file to use Google style. (#4148) --- .../concept/tisbury-treasure-hunt/tuples.py | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/exercises/concept/tisbury-treasure-hunt/tuples.py b/exercises/concept/tisbury-treasure-hunt/tuples.py index 92336d88ec..4bd18224fd 100644 --- a/exercises/concept/tisbury-treasure-hunt/tuples.py +++ b/exercises/concept/tisbury-treasure-hunt/tuples.py @@ -4,8 +4,11 @@ def get_coordinate(record): """Return coordinate value from a tuple containing the treasure name, and treasure coordinate. - :param record: tuple - with a (treasure, coordinate) pair. - :return: str - the extracted map coordinate. + Parameters: + record (tuple): A (treasure, coordinate) pair. + + Returns: + str: The extracted map coordinate. """ pass @@ -14,8 +17,11 @@ def get_coordinate(record): def convert_coordinate(coordinate): """Split the given coordinate into tuple containing its individual components. - :param coordinate: str - a string map coordinate - :return: tuple - the string coordinate split into its individual components. + Parameters: + coordinate (str): A string map coordinate. + + Returns: + tuple: The string coordinate split into its individual components. """ pass @@ -24,9 +30,12 @@ def convert_coordinate(coordinate): def compare_records(azara_record, rui_record): """Compare two record types and determine if their coordinates match. - :param azara_record: tuple - a (treasure, coordinate) pair. - :param rui_record: tuple - a (location, tuple(coordinate_1, coordinate_2), quadrant) trio. - :return: bool - do the coordinates match? + Parameters: + azara_record (tuple): A (treasure, coordinate) pair. + rui_record (tuple): A (location, tuple(coordinate_1, coordinate_2), quadrant) trio. + + Returns: + bool: Do the coordinates match? """ pass @@ -35,9 +44,12 @@ def compare_records(azara_record, rui_record): def create_record(azara_record, rui_record): """Combine the two record types (if possible) and create a combined record group. - :param azara_record: tuple - a (treasure, coordinate) pair. - :param rui_record: tuple - a (location, coordinate, quadrant) trio. - :return: tuple or str - the combined record (if compatible), or the string "not a match" (if incompatible). + Parameters: + azara_record (tuple): A (treasure, coordinate) pair. + rui_record (tuple): A (location, coordinate, quadrant) trio. + + Returns: + tuple or str: The combined record (if compatible), or the string "not a match" (if incompatible). """ pass @@ -46,12 +58,16 @@ def create_record(azara_record, rui_record): def clean_up(combined_record_group): """Clean up a combined record group into a multi-line string of single records. - :param combined_record_group: tuple - everything from both participants. - :return: str - everything "cleaned", excess coordinates and information are removed. + Parameters: + combined_record_group (tuple): Everything from both participants. + + Returns: + str: Everything "cleaned", excess coordinates and information are removed. - The return statement should be a multi-lined string with items separated by newlines. + Note: + The return statement is a multi-lined string with items separated by newlines. + (see HINTS.md for an example). - (see HINTS.md for an example). """ pass From 2ed15decd28bdcf5f1067467f7758b166596be91 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:20:16 -0700 Subject: [PATCH 07/47] Updated stub docstrings to use Google style. (#4149) --- exercises/concept/plane-tickets/generators.py | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/exercises/concept/plane-tickets/generators.py b/exercises/concept/plane-tickets/generators.py index 2f88c0619a..db7b327055 100644 --- a/exercises/concept/plane-tickets/generators.py +++ b/exercises/concept/plane-tickets/generators.py @@ -4,13 +4,16 @@ def generate_seat_letters(number): """Generate a series of letters for airline seats. - :param number: int - total number of seat letters to be generated. - :return: generator - generator that yields seat letters. + Parameters: + number (int): Total number of seat letters to be generated. - Seat letters are generated from A to D. - After D it should start again with A. + Returns: + generator: A generator that yields seat letters. - Example: A, B, C, D + Note: + Seat letters are generated from A to D. + After D the sequence starts again with A. + For example: A, B, C, D, A, B """ @@ -20,17 +23,18 @@ def generate_seat_letters(number): def generate_seats(number): """Generate a series of identifiers for airline seats. - :param number: int - total number of seats to be generated. - :return: generator - generator that yields seat numbers. + Parameters: + number (int): The total number of seats to be generated. - A seat number consists of the row number and the seat letter. + Returns: + generator: A generator that yields seat numbers. - There is no row 13. - Each row has 4 seats. + Note: + A seat number consists of the row number and the seat letter. + There is no row 13, and each row has 4 seats. - Seats should be sorted from low to high. - - Example: 3C, 3D, 4A, 4B + Seats should be sorted from low to high. + For exampl: 3C, 3D, 4A, 4B """ @@ -39,10 +43,12 @@ def generate_seats(number): def assign_seats(passengers): """Assign seats to passengers. - :param passengers: list[str] - a list of strings containing names of passengers. - :return: dict - with the names of the passengers as keys and seat numbers as values. + Parameters: + passengers (list[str]): A list of strings containing names of passengers. - Example output: {"Adele": "1A", "Björk": "1B"} + Returns: + dict: With passenger names as keys and seat numbers as values. + Example output: {"Adele": "1A", "Björk": "1B"} """ @@ -51,9 +57,12 @@ def assign_seats(passengers): def generate_codes(seat_numbers, flight_id): """Generate codes for a ticket. - :param seat_numbers: list[str] - list of seat numbers. - :param flight_id: str - string containing the flight identifier. - :return: generator - generator that yields 12 character long ticket codes. + Parameters: + seat_numbers (list[str]): A list of seat numbers. + flight_id (str): A string containing the flight identifier. + + Returns: + generator: A generator that yields 12 character long ticket codes. """ From 7de4681b51b8ffb3da647e15c45c354ee43dd927 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:20:41 -0700 Subject: [PATCH 08/47] Updated stub docstrings to Google Style. (#4150) --- .../concept/currency-exchange/exchange.py | 136 ++++++++++++++---- 1 file changed, 110 insertions(+), 26 deletions(-) diff --git a/exercises/concept/currency-exchange/exchange.py b/exercises/concept/currency-exchange/exchange.py index b58df5cbe4..36d972f798 100644 --- a/exercises/concept/currency-exchange/exchange.py +++ b/exercises/concept/currency-exchange/exchange.py @@ -8,68 +8,152 @@ def exchange_money(budget, exchange_rate): - """ + """Calculate estimated value after exchange. + + Parameters: + budget (float): Tthe amount of money you are planning to exchange. + exchange_rate (float): The unit value of the foreign currency. + + Returns: + float: The exchanged value of the foreign currency you can receive. + + Examples: + >>> exchange_money(127.5, 1.2) + 106.25 + + >>> exchange_money(200, 1.10) + 181.82 + + This function calculates and returns the (estimated) value of the exchanged currency. - :param budget: float - amount of money you are planning to exchange. - :param exchange_rate: float - unit value of the foreign currency. - :return: float - exchanged value of the foreign currency you can receive. """ pass def get_change(budget, exchanging_value): - """ + """Calculate currency left after an exchange. + + Parameters: + budget (float): The amount of money you own. + exchanging_value (float): The amount of your money you want to exchange now. + + Returns: + float: The amount left of your starting currency after the exchange + + Examples: + .>>> get_change(127.5, 120.0) + 7.5 + + >>> get_change(300.75, 150.25) + 150.50 + + Tthis function calcultes and returns the amount of money left over from the budget + after an exchange. - :param budget: float - amount of money you own. - :param exchanging_value: float - amount of your money you want to exchange now. - :return: float - amount left of your starting currency after exchanging. """ pass def get_value_of_bills(denomination, number_of_bills): - """ + """Calculate the total value of currency at current denomination. + + Parameters: + denomination (int): The value of a single unit (bill). + number_of_bills (int): The total number of units (bills). + + Returns: + int: Calculated value of the units (bills). + + Examples: + >>> get_value_of_bills(5, 128) + 640 + + >>> get_value_of_bills(15.13, 16) + 242 + + This function calculates and returns the total value of the bills (excluding fractionaal amounts). - :param denomination: int - the value of a bill. - :param number_of_bills: int - total number of bills. - :return: int - calculated value of the bills. """ pass def get_number_of_bills(amount, denomination): - """ + """Calculate the number of currency units (bills) within the amount. + + Parameters: + amount (float): The total starting value. + denomination (int): The value of a single unit (bill). + + Returns: + int: The number of units (bills) that can be obtained from the amount. + + Examples: + >>> get_number_of_bills(127.5, 5) + 25 + + >>> get_number_of_bills(35.16, 10) + 3 + + This function calcluates and returns the number pf currency units (bills) that can + be obtained from the given amount. Whole bills only - no fractioal amounts. - :param amount: float - the total starting value. - :param denomination: int - the value of a single bill. - :return: int - number of bills that can be obtained from the amount. """ pass def get_leftover_of_bills(amount, denomination): - """ + """Calculate leftover amount after exchanging into bills. + + Parameters: + amount (float): The total starting value. + denomination (int): The value of a single unit (bill). + + Returns: + float: The amount that is "leftover", given the current denomination. + + Examples: + >>> get_leftover_of_bills(127.5, 20) + 7.5 + + >>> get_leftover_of_bills(153.2, 10) + 3.20 + + This function calculates and returns the leftover amount that cannot be + returned from starting amount, due to the currency denomination. - :param amount: float - the total starting value. - :param denomination: int - the value of a single bill. - :return: float - the amount that is "leftover", given the current denomination. """ pass def exchangeable_value(budget, exchange_rate, spread, denomination): - """ + """Calculate the maximum value of the new currency. + + Parameters: + budget (float): The amount of your money you are planning to exchange. + exchange_rate (float): The unit value of the foreign currency. + spread (int): The percentage that is taken as an exchange fee. + denomination (int) The value of a single unit (bill). + + Returns: + int: The maximum value you can get in the new currency. + + Examples: + >>> exchangeable_value(127.25, 1.20, 10, 20) + 80 + + >>> exchangeable_value(127.25, 1.20, 10, 5) + 95 + + Note: + The currency denomination is a whole number and cannot be sub-divided. - :param budget: float - the amount of your money you are planning to exchange. - :param exchange_rate: float - the unit value of the foreign currency. - :param spread: int - percentage that is taken as an exchange fee. - :param denomination: int - the value of a single bill. - :return: int - maximum value you can get. + This function calculates and returns the maximum value of the new currency after + determining the exchange rate plus the spread. """ pass From c9635fbdaec07879e0ae07ddf6d0af6251e526b8 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:22:01 -0700 Subject: [PATCH 09/47] [Basics Concept Exercise]: Update docstrings and documents for `guidos-gorgeous-lasagna` (#4151) * Updated stub docstrings to Google-style docstrings. * Updated docstring explanations and refs to use Google style. --- .../guidos-gorgeous-lasagna/.docs/hints.md | 2 + .../.docs/instructions.md | 12 +++-- .../.docs/introduction.md | 51 ++++++++++++------- .../guidos-gorgeous-lasagna/lasagna.py | 17 ++++--- 4 files changed, 53 insertions(+), 29 deletions(-) diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md index 96668ffbd0..fca46fc65f 100644 --- a/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md +++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md @@ -38,6 +38,7 @@ ## 5. Update the recipe with notes - Clearly [commenting][comments] and [documenting][docstrings] your code according to [PEP257][pep257] is always recommended. +- Some examples of Google-style docstrings can be found in the Sphinx documentation for the [napoleon module][napoleon]. [assignment]: https://docs.python.org/3/reference/simple_stmts.html#grammar-token-assignment-stmt [comments]: https://realpython.com/python-comments-guide/ @@ -46,6 +47,7 @@ [docstrings]: https://docs.python.org/3/tutorial/controlflow.html#tut-docstrings [magic-numbers]: https://en.wikipedia.org/wiki/Magic_number_(programming) [naming]: https://realpython.com/python-variables/ +[napoleon]: https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#module-sphinx.ext.napoleon [numbers]: https://docs.python.org/3/tutorial/introduction.html#numbers [pep257]: https://www.python.org/dev/peps/pep-0257/ [python as a calculator]: https://docs.python.org/3/tutorial/introduction.html#using-python-as-a-calculator diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md index c566db9112..f3acc137c1 100644 --- a/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md +++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md @@ -78,14 +78,18 @@ Go back through the recipe, adding "notes" in the form of [function docstrings][ ```python def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time): """Calculate the elapsed cooking time. - - :param number_of_layers: int - the number of layers in the lasagna. - :param elapsed_bake_time: int - time the lasagna has been baking in the oven. - :return: int - total time elapsed (in minutes) preparing and baking. + + Parameters: + number_of_layers (int): The number of layers in the lasagna. + elapsed_bake_time (int): Time the lasagna has been baking in the oven. + + Returns: + int: The total time elapsed (in minutes) preparing and baking. This function takes two integers representing the number of lasagna layers and the time already spent baking the lasagna. It calculates the total elapsed minutes spent cooking (preparing + baking). + """ ``` diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md index eb755bcfe2..166063057d 100644 --- a/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md +++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md @@ -209,13 +209,18 @@ Docstrings are declared using triple double quotes (""") indented at the same le ```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: @@ -224,36 +229,45 @@ def complex(real=0.0, imag=0.0): ``` -Docstrings are read by automated documentation tools and are returned by calling the special attribute `.__doc__` on the function, method, or class name. -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. +Exercism concept exercises try to follow the Google style for un-type hinted code. Docstrings can also function as [lightweight unit tests][doctests], which will be covered in a later exercise. ```python # An example on a user-defined function. +# This uses Google style docstrings. >>> 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. ``` [calls]: https://docs.python.org/3/reference/expressions.html#calls @@ -271,4 +285,5 @@ Raise a number to an arbitrary power. [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 +[sphinx]: https://www.sphinx-doc.org/en/master/usage/index.html [type hints]: https://docs.python.org/3/library/typing.html diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna.py index 0e1a50d571..bdf8ca9b77 100644 --- a/exercises/concept/guidos-gorgeous-lasagna/lasagna.py +++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna.py @@ -8,15 +8,18 @@ """ -#TODO: define your EXPECTED_BAKE_TIME (required) and PREPARATION_TIME (optional) constants below. +#TODO (student): define your EXPECTED_BAKE_TIME (required) and PREPARATION_TIME (optional) constants below. -#TODO: Remove 'pass' and complete the 'bake_time_remaining()' function below. +#TODO (student): Remove 'pass' and complete the 'bake_time_remaining()' function below. def bake_time_remaining(): """Calculate the bake time remaining. - :param elapsed_bake_time: int - baking time already elapsed. - :return: int - remaining bake time (in minutes) derived from 'EXPECTED_BAKE_TIME'. + Parameters: + elapsed_bake_time (int): The baking time already elapsed. + + Returns: + int: The remaining bake time (in minutes) derived from 'EXPECTED_BAKE_TIME'. Function that takes the actual minutes the lasagna has been in the oven as an argument and returns how many minutes the lasagna still needs to bake @@ -26,16 +29,16 @@ def bake_time_remaining(): pass -#TODO: Define the 'preparation_time_in_minutes()' function below. +#TODO (student): Define the 'preparation_time_in_minutes()' function below. # To avoid the use of magic numbers (see: https://en.wikipedia.org/wiki/Magic_number_(programming)), you should define a PREPARATION_TIME constant. # You can do that on the line below the 'EXPECTED_BAKE_TIME' constant. # This will make it easier to do calculations, and make changes to your code. -#TODO: define the 'elapsed_time_in_minutes()' function below. +#TODO (student): define the 'elapsed_time_in_minutes()' function below. -# TODO: Remember to go back and add docstrings to all your functions +# TODO (student): Remember to go back and add docstrings to all your functions # (you can copy and then alter the one from bake_time_remaining.) From 4f5cc678b066dd036aa4c9cd9e07c7e086744af8 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:22:41 -0700 Subject: [PATCH 10/47] Reformatted docstrings in stub to be Google Style. (#4152) --- exercises/concept/cater-waiter/sets.py | 61 +++++++++++++++++--------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/exercises/concept/cater-waiter/sets.py b/exercises/concept/cater-waiter/sets.py index e726e3d664..44f1918019 100644 --- a/exercises/concept/cater-waiter/sets.py +++ b/exercises/concept/cater-waiter/sets.py @@ -13,9 +13,12 @@ def clean_ingredients(dish_name, dish_ingredients): """Remove duplicates from `dish_ingredients`. - :param dish_name: str - containing the dish name. - :param dish_ingredients: list - dish ingredients. - :return: tuple - containing (dish_name, ingredient set). + Parameters: + dish_name (str): The name of the dish. + dish_ingredients (list): The ingredients for the dish. + + Returns: + tuple: Containing (dish_name, ingredient set). This function should return a `tuple` with the name of the dish as the first item, followed by the de-duped `set` of ingredients as the second item. @@ -27,13 +30,15 @@ def clean_ingredients(dish_name, dish_ingredients): def check_drinks(drink_name, drink_ingredients): """Append "Cocktail" (alcohol) or "Mocktail" (no alcohol) to `drink_name`, based on `drink_ingredients`. - :param drink_name: str - name of the drink. - :param drink_ingredients: list - ingredients in the drink. - :return: str - drink_name appended with "Mocktail" or "Cocktail". + Parameters: + drink_name (str): Name of the drink. + drink_ingredients (list): Ingredients in the drink. + + Returns: + str: drink_name appended with "Mocktail" or "Cocktail". The function should return the name of the drink followed by "Mocktail" (non-alcoholic) and drink name followed by "Cocktail" (includes alcohol). - """ pass @@ -42,14 +47,16 @@ def check_drinks(drink_name, drink_ingredients): def categorize_dish(dish_name, dish_ingredients): """Categorize `dish_name` based on `dish_ingredients`. - :param dish_name: str - dish to be categorized. - :param dish_ingredients: set - ingredients for the dish. - :return: str - the dish name appended with ": ". + Parameters: + dish_name (str): The dish to be categorized. + dish_ingredients (set): The ingredients for the dish. + + Returns: + str: TThe dish name appended with ": ". This function should return a string with the `dish name: ` (which meal category the dish belongs to). `` can be any one of (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE). All dishes will "fit" into one of the categories imported from `sets_categories_data.py` - """ pass @@ -58,8 +65,11 @@ def categorize_dish(dish_name, dish_ingredients): def tag_special_ingredients(dish): """Compare `dish` ingredients to `SPECIAL_INGREDIENTS`. - :param dish: tuple - of (dish name, list of dish ingredients). - :return: tuple - containing (dish name, dish special ingredients). + Parameters: + dish (tuple): (dish name, list of dish ingredients). + + Returns: + tuple: Containing (dish name, dish special ingredients). Return the dish name followed by the `set` of ingredients that require a special note on the dish description. For the purposes of this exercise, all allergens or special ingredients that need to be tracked are in the @@ -72,8 +82,11 @@ def tag_special_ingredients(dish): def compile_ingredients(dishes): """Create a master list of ingredients. - :param dishes: list - of dish ingredient sets. - :return: set - of ingredients compiled from `dishes`. + Parameters: + dishes (list): Dish ingredient sets. + + Returns: + set: Ingredients compiled from `dishes`. This function should return a `set` of all ingredients from all listed dishes. """ @@ -84,9 +97,12 @@ def compile_ingredients(dishes): def separate_appetizers(dishes, appetizers): """Determine which `dishes` are designated `appetizers` and remove them. - :param dishes: list - of dish names. - :param appetizers: list - of appetizer names. - :return: list - of dish names that do not appear on appetizer list. + Parameters: + dishes (list): Group of dish names. + appetizers (list): Group of appetizer names. + + Returns: + list: Group of dish names that do not appear on appetizer list. The function should return the list of dish names with appetizer names removed. Either list could contain duplicates and may require de-duping. @@ -98,9 +114,12 @@ def separate_appetizers(dishes, appetizers): def singleton_ingredients(dishes, intersection): """Determine which `dishes` have a singleton ingredient (an ingredient that only appears once across dishes). - :param dishes: list - of ingredient sets. - :param intersection: constant - can be one of `_INTERSECTIONS` constants imported from `sets_categories_data.py`. - :return: set - containing singleton ingredients. + Parameters: + dishes (list): Group of ingredient sets. + intersection (constant): Can be one of `_INTERSECTIONS` constants imported from `sets_categories_data.py`. + + Returns: + set: Containing singleton ingredients. Each dish is represented by a `set` of its ingredients. From 2aef6c1e657c830092d1930ca113c563206e463f Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 9 May 2026 13:23:11 -0700 Subject: [PATCH 11/47] Updated docstring in stub to Google docstring standard. (#4153) --- exercises/concept/card-games/lists.py | 53 +++++++++++++++++++-------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/exercises/concept/card-games/lists.py b/exercises/concept/card-games/lists.py index 03fb417330..f5a4b5fc44 100644 --- a/exercises/concept/card-games/lists.py +++ b/exercises/concept/card-games/lists.py @@ -7,8 +7,11 @@ def get_rounds(number): """Create a list containing the current and next two round numbers. - :param number: int - current round number. - :return: list - current round and the two that follow. + Parameters: + number (int): The current round number. + + Returns: + list: The current round number and the two that follow. """ pass @@ -17,9 +20,12 @@ def get_rounds(number): def concatenate_rounds(rounds_1, rounds_2): """Concatenate two lists of round numbers. - :param rounds_1: list - first rounds played. - :param rounds_2: list - second set of rounds played. - :return: list - all rounds played. + Parameters: + rounds_1 (list): The first rounds played. + rounds_2 (list): The second group of rounds played. + + Returns: + list: All rounds played. """ pass @@ -28,9 +34,12 @@ def concatenate_rounds(rounds_1, rounds_2): def list_contains_round(rounds, number): """Check if the list of rounds contains the specified number. - :param rounds: list - rounds played. - :param number: int - round number. - :return: bool - was the round played? + Parameters: + rounds (list): The rounds played. + number (int): The round number. + + Returns: + bool: Was the round played? """ pass @@ -39,8 +48,11 @@ def list_contains_round(rounds, number): def card_average(hand): """Calculate and returns the average card value from the list. - :param hand: list - cards in hand. - :return: float - average value of the cards in the hand. + Parameters: + hand (list): The cards in the hand. + + Returns: + float: The average value of the cards in the hand. """ pass @@ -49,8 +61,11 @@ def card_average(hand): def approx_average_is_average(hand): """Return if the (average of first and last card values) OR ('middle' card) == calculated average. - :param hand: list - cards in hand. - :return: bool - does one of the approximate averages equal the `true average`? + Parameters: + hand (list): The cards in the hand. + + Returns: + bool: Does one of the approximate averages equal the `true average`? """ pass @@ -59,8 +74,11 @@ def approx_average_is_average(hand): def average_even_is_average_odd(hand): """Return if the (average of even indexed card values) == (average of odd indexed card values). - :param hand: list - cards in hand. - :return: bool - are even and odd averages equal? + Parameters: + hand (list): The cards in the hand. + + Returns: + bool: Are the even and odd averages equal? """ pass @@ -69,8 +87,11 @@ def average_even_is_average_odd(hand): def maybe_double_last(hand): """Multiply a Jack card value in the last index position by 2. - :param hand: list - cards in hand. - :return: list - hand with Jacks (if present) value doubled. + Parameters: + hand (list): The cards in the hand. + + Returns: + list: The hand with Jacks (if present) value doubled. """ pass From 6f50d7769e915dd711335ef276cf997cbe4d7ebb Mon Sep 17 00:00:00 2001 From: BethanyG Date: Mon, 11 May 2026 18:03:26 -0700 Subject: [PATCH 12/47] Updated exemplar and stub docstrings to use Google style. (#4156) --- .../inventory-management/.meta/exemplar.py | 41 ++++++++++++------ .../concept/inventory-management/dicts.py | 42 ++++++++++++------- 2 files changed, 56 insertions(+), 27 deletions(-) diff --git a/exercises/concept/inventory-management/.meta/exemplar.py b/exercises/concept/inventory-management/.meta/exemplar.py index ac02bad30d..140c76d832 100644 --- a/exercises/concept/inventory-management/.meta/exemplar.py +++ b/exercises/concept/inventory-management/.meta/exemplar.py @@ -4,8 +4,11 @@ def create_inventory(items): """Create a dict that tracks the amount (count) of each element on the `items` list. - :param items: list - list of items to create an inventory from. - :return: dict - the inventory dictionary. + Parameters: + items (list): Items to create an inventory from. + + Returns: + dict: The inventory dictionary. """ inventory = {} @@ -16,9 +19,12 @@ def create_inventory(items): def add_items(inventory, items): """Add or increment items in inventory using elements from the items `list`. - :param inventory: dict - dictionary of existing inventory. - :param items: list - list of items to update the inventory with. - :return: dict - the inventory updated with the new items. + Parameters: + inventory (dict): Dictionary of existing inventory. + items (list): List of items to update the inventory with. + + Returns: + dict: The inventory updated with the new items. """ for item in items: @@ -30,9 +36,12 @@ def add_items(inventory, items): def decrement_items(inventory, items): """Decrement items in inventory using elements from the `items` list. - :param inventory: dict - inventory dictionary. - :param items: list - list of items to decrement from the inventory. - :return: dict - updated inventory with items decremented. + Parameters: + inventory (dict): Inventory dictionary. + items (list): List of items to decrement from the inventory. + + Returns: + dict: Updated inventory with items decremented. """ for item in items: @@ -44,9 +53,12 @@ def decrement_items(inventory, items): def remove_item(inventory, item): """Remove item from inventory if it matches `item` string. - :param inventory: dict - inventory dictionary. - :param item: str - item to remove from the inventory. - :return: dict - updated inventory with item removed. Current inventory if item does not match. + Parameters: + inventory (dict): Inventory dictionary. + item (str): Item to remove from the inventory. + + Returns: + dict: Updated inventory with item removed. Current inventory if item does not match. """ if item in inventory: @@ -57,8 +69,11 @@ def remove_item(inventory, item): def list_inventory(inventory): """Create a list containing only available (item_name, item_count > 0) pairs in inventory. - :param inventory: dict - an inventory dictionary. - :return: list of tuples - list of key, value pairs from the inventory dictionary. + Parameters: + inventory (dict): An inventory dictionary. + + Returns: + list[tuple]: List of key, value tuples from the inventory dictionary. """ output = [] diff --git a/exercises/concept/inventory-management/dicts.py b/exercises/concept/inventory-management/dicts.py index 2600eceb27..ae17da80e1 100644 --- a/exercises/concept/inventory-management/dicts.py +++ b/exercises/concept/inventory-management/dicts.py @@ -4,8 +4,11 @@ def create_inventory(items): """Create a dict that tracks the amount (count) of each element on the `items` list. - :param items: list - list of items to create an inventory from. - :return: dict - the inventory dictionary. + Parameters: + items (list): Items to create an inventory from. + + Returns: + dict: The inventory dictionary. """ pass @@ -14,9 +17,12 @@ def create_inventory(items): def add_items(inventory, items): """Add or increment items in inventory using elements from the items `list`. - :param inventory: dict - dictionary of existing inventory. - :param items: list - list of items to update the inventory with. - :return: dict - the inventory updated with the new items. + Parameters: + inventory (dict): Dictionary of existing inventory. + items (list): List of items to update the inventory with. + + Returns: + dict: The inventory updated with the new items. """ pass @@ -25,9 +31,12 @@ def add_items(inventory, items): def decrement_items(inventory, items): """Decrement items in inventory using elements from the `items` list. - :param inventory: dict - inventory dictionary. - :param items: list - list of items to decrement from the inventory. - :return: dict - updated inventory with items decremented. + Parameters: + inventory (dict): Inventory dictionary. + items (list): List of items to decrement from the inventory. + + Returns: + dict: Updated inventory with items decremented. """ pass @@ -36,9 +45,12 @@ def decrement_items(inventory, items): def remove_item(inventory, item): """Remove item from inventory if it matches `item` string. - :param inventory: dict - inventory dictionary. - :param item: str - item to remove from the inventory. - :return: dict - updated inventory with item removed. Current inventory if item does not match. + Parameters: + inventory (dict): Inventory dictionary. + item (str): Item to remove from the inventory. + + Returns: + dict: Updated inventory with item removed. Current inventory if item does not match. """ pass @@ -47,9 +59,11 @@ def remove_item(inventory, item): def list_inventory(inventory): """Create a list containing only available (item_name, item_count > 0) pairs in inventory. - :param inventory: dict - an inventory dictionary. - :return: list of tuples - list of key, value pairs from the inventory dictionary. + Parameters: + inventory (dict): An inventory dictionary. + + Returns: + list[tuple]: List of key, value tuples from the inventory dictionary. """ pass - From 7b42eaa5b28e675b92bc31579856694b3ad13b0e Mon Sep 17 00:00:00 2001 From: BethanyG Date: Mon, 11 May 2026 18:03:51 -0700 Subject: [PATCH 13/47] [Loops Concept Exercise]: Docstring & exemplar update to `makimg-the-grade` (#4157) * Updated docstrings in stub file to Google style. * Updated docstrings in exemplar to use Google style. --- .../making-the-grade/.meta/exemplar.py | 52 +++++++++++++------ exercises/concept/making-the-grade/loops.py | 50 ++++++++++++------ 2 files changed, 69 insertions(+), 33 deletions(-) diff --git a/exercises/concept/making-the-grade/.meta/exemplar.py b/exercises/concept/making-the-grade/.meta/exemplar.py index 5aa7508400..c490dbf1c6 100644 --- a/exercises/concept/making-the-grade/.meta/exemplar.py +++ b/exercises/concept/making-the-grade/.meta/exemplar.py @@ -4,8 +4,11 @@ def round_scores(student_scores): """Round all provided student scores. - :param student_scores: list - float or int of student exam scores. - :return: list - student scores *rounded* to nearest integer value. + Parameters: + student_scores (list[float|int]): Student exam scores. + + Returns: + list[int]: Student scores *rounded* to the nearest integer value. """ rounded = [] @@ -17,8 +20,11 @@ def round_scores(student_scores): def count_failed_students(student_scores): """Count the number of failing students out of the group provided. - :param student_scores: list - containing int student scores. - :return: int - count of student scores at or below 40. + Parameters: + student_scores (list[int]): Student scores as ints. + + Returns: + int: The count of student scores at or below 40. """ non_passing = 0 @@ -31,9 +37,12 @@ def count_failed_students(student_scores): def above_threshold(student_scores, threshold): """Determine how many of the provided student scores were 'the best' based on the provided threshold. - :param student_scores: list - of integer scores. - :param threshold: int - threshold to cross to be the "best" score. - :return: list - of integer scores that are at or above the "best" threshold. + Parameters: + student_scores (list[int]): Integer scores. + threshold (int): The threshold to cross to be the "best" score. + + Returns: + list[int]: Integer scores that are at or above the "best" threshold. """ above = [] @@ -47,11 +56,14 @@ def above_threshold(student_scores, threshold): def letter_grades(highest): """Create a list of grade thresholds based on the provided highest grade. - :param highest: int - value of highest exam score. - :return: list - of lower threshold scores for each D-A letter grade interval. - For example, where the highest score is 100, and failing is <= 40, - The result would be [41, 56, 71, 86]: + Parameters: + highest: int - value of the highest exam score. + Returns: + list[int]: Lower threshold scores for each D-A letter grade interval. + + For example, where the highest score is 100, and failing is <= 40, + The result would be [41, 56, 71, 86]: 41 <= "D" <= 55 56 <= "C" <= 70 71 <= "B" <= 85 @@ -66,11 +78,14 @@ def letter_grades(highest): def student_ranking(student_scores, student_names): - """Organize the student's rank, name, and grade information in ascending order. + """Organize the student's rank, name, and grade information in descending order. + + Parameters: + student_scores (list): Scores in descending order. + student_names (list[str]): Student names by exam score in descending order. - :param student_scores: list - of scores in descending order. - :param student_names: list - of string names by exam score in descending order. - :return: list - of strings in format [". : "]. + Returns: + list[str]: Strings in format [". : "]. """ results = [] @@ -84,8 +99,11 @@ def student_ranking(student_scores, student_names): def perfect_score(student_info): """Create a list that contains the name and grade of the first student to make a perfect score on the exam. - :param student_info: list - of [, ] lists. - :return: list - first `[, 100]` or `[]` if no student score of 100 is found. + Parameters: + student_info (list[list[str, int]]): List of [, ] lists. + + Returns: + list: First `[, 100]` found OR `[]` if no student score of 100 is found. """ result = [] diff --git a/exercises/concept/making-the-grade/loops.py b/exercises/concept/making-the-grade/loops.py index ecf7d06774..21da8c77e8 100644 --- a/exercises/concept/making-the-grade/loops.py +++ b/exercises/concept/making-the-grade/loops.py @@ -4,8 +4,11 @@ def round_scores(student_scores): """Round all provided student scores. - :param student_scores: list - float or int of student exam scores. - :return: list - student scores *rounded* to nearest integer value. + Parameters: + student_scores (list[float|int]): Student exam scores. + + Returns: + list[int]: Student scores *rounded* to the nearest integer value. """ pass @@ -14,8 +17,11 @@ def round_scores(student_scores): def count_failed_students(student_scores): """Count the number of failing students out of the group provided. - :param student_scores: list - containing int student scores. - :return: int - count of student scores at or below 40. + Parameters: + student_scores (list[int]): Student scores as ints. + + Returns: + int: The count of student scores at or below 40. """ pass @@ -24,9 +30,12 @@ def count_failed_students(student_scores): def above_threshold(student_scores, threshold): """Determine how many of the provided student scores were 'the best' based on the provided threshold. - :param student_scores: list - of integer scores. - :param threshold: int - threshold to cross to be the "best" score. - :return: list - of integer scores that are at or above the "best" threshold. + Parameters: + student_scores (list[int]): Integer scores. + threshold (int): The threshold to cross to be the "best" score. + + Returns: + list[int]: Integer scores that are at or above the "best" threshold. """ pass @@ -35,11 +44,14 @@ def above_threshold(student_scores, threshold): def letter_grades(highest): """Create a list of grade thresholds based on the provided highest grade. - :param highest: int - value of highest exam score. - :return: list - of lower threshold scores for each D-A letter grade interval. - For example, where the highest score is 100, and failing is <= 40, - The result would be [41, 56, 71, 86]: + Parameters: + highest: int - value of the highest exam score. + Returns: + list[int]: Lower threshold scores for each D-A letter grade interval. + + For example, where the highest score is 100, and failing is <= 40, + The result would be [41, 56, 71, 86]: 41 <= "D" <= 55 56 <= "C" <= 70 71 <= "B" <= 85 @@ -52,9 +64,12 @@ def letter_grades(highest): def student_ranking(student_scores, student_names): """Organize the student's rank, name, and grade information in descending order. - :param student_scores: list - of scores in descending order. - :param student_names: list - of string names by exam score in descending order. - :return: list - of strings in format [". : "]. + Parameters: + student_scores (list): Scores in descending order. + student_names (list[str]): Student names by exam score in descending order. + + Returns: + list[str]: Strings in format [". : "]. """ pass @@ -63,8 +78,11 @@ def student_ranking(student_scores, student_names): def perfect_score(student_info): """Create a list that contains the name and grade of the first student to make a perfect score on the exam. - :param student_info: list - of [, ] lists. - :return: list - first `[, 100]` or `[]` if no student score of 100 is found. + Parameters: + student_info (list[list[str, int]]): List of [, ] lists. + + Returns: + list: First `[, 100]` found OR `[]` if no student score of 100 is found. """ pass From e8b988b2a72b16f068418b573a58ad9bd3172dde Mon Sep 17 00:00:00 2001 From: BethanyG Date: Mon, 11 May 2026 18:04:11 -0700 Subject: [PATCH 14/47] Updated docsrings in exemplar and stub to use Google style. (#4158) --- .../little-sisters-vocab/.meta/exemplar.py | 56 ++++++++++++++----- .../concept/little-sisters-vocab/strings.py | 56 ++++++++++++++----- 2 files changed, 82 insertions(+), 30 deletions(-) diff --git a/exercises/concept/little-sisters-vocab/.meta/exemplar.py b/exercises/concept/little-sisters-vocab/.meta/exemplar.py index c71d4902ca..c17350fd3a 100644 --- a/exercises/concept/little-sisters-vocab/.meta/exemplar.py +++ b/exercises/concept/little-sisters-vocab/.meta/exemplar.py @@ -4,26 +4,34 @@ def add_prefix_un(word): """Take the given word and add the 'un' prefix. - :param word: str - containing the root word. - :return: str - of root word prepended with 'un'. + Parameters: + word (str): The root word. + + Returns: + str: Root word prepended with 'un'. """ return 'un' + word def make_word_groups(vocab_words): - """Transform a list containing a prefix and words into a string with the prefix followed by the words with prefix prepended. + """Transform a list containing a prefix and words. + + Parameters: + vocab_words (list[str]): Vocabulary words with prefix at first index. - :param vocab_words: list - of vocabulary words with prefix in first index. - :return: str - of prefix followed by vocabulary words with + Returns: + str: Prefix followed by vocabulary words with prefix applied. - This function takes a `vocab_words` list and returns a string + This function takes a `vocab_words` list of strings and returns a string with the prefix and the words with prefix applied, separated by ' :: '. - For example: list('en', 'close', 'joy', 'lighten'), - produces the following string: 'en :: enclose :: enjoy :: enlighten'. + Examples: + >>> list('en', 'close', 'joy', 'lighten') + 'en :: enclose :: enjoy :: enlighten'. + """ prefix = vocab_words[0] @@ -35,10 +43,19 @@ def make_word_groups(vocab_words): def remove_suffix_ness(word): """Remove the suffix from the word while keeping spelling in mind. - :param word: str - of word to remove suffix from. - :return: str - of word with suffix removed & spelling adjusted. + Parameters: + word (str): Word to remove suffix from. + + Returns: + str: Word with suffix removed & spelling adjusted. + + Examples: + >>> remove_suffix_ness('heaviness') + 'heavy' + + >>> remove_suffix_ness('sadness') + 'sad' - For example: "heaviness" becomes "heavy", but "sadness" becomes "sad". """ word = word[:-4] @@ -51,11 +68,20 @@ def remove_suffix_ness(word): def adjective_to_verb(sentence, index): """Change the adjective within the sentence to a verb. - :param sentence: str - that uses the word in sentence. - :param index: int - index of the word to remove and transform. - :return: str - word that changes the extracted adjective to a verb. + Parameters: + sentence (str): The word used in a sentence as an adjective. + index (int): Index of the adjective to remove and transform. + + Returns: + str: The extracted adjective in verb form. + + Examples: + >>> adjective_to_verb('It got dark as the sun set.', 2) + 'darken' + + >>> adjective_to_verb('The ink stains her fingers black.', -1) + 'blacken' - For example, ("It got dark as the sun set", 2) becomes "darken". """ word = sentence.split()[index] diff --git a/exercises/concept/little-sisters-vocab/strings.py b/exercises/concept/little-sisters-vocab/strings.py index 39ae7bb80c..e7c1a8de88 100644 --- a/exercises/concept/little-sisters-vocab/strings.py +++ b/exercises/concept/little-sisters-vocab/strings.py @@ -4,26 +4,34 @@ def add_prefix_un(word): """Take the given word and add the 'un' prefix. - :param word: str - containing the root word. - :return: str - of root word prepended with 'un'. + Parameters: + word (str): The root word. + + Returns: + str: Root word prepended with 'un'. """ pass def make_word_groups(vocab_words): - """Transform a list containing a prefix and words into a string with the prefix followed by the words with prefix prepended. + """Transform a list containing a prefix and words. + + Parameters: + vocab_words (list[str]): Vocabulary words with prefix at first index. - :param vocab_words: list - of vocabulary words with prefix in first index. - :return: str - of prefix followed by vocabulary words with + Returns: + str: Prefix followed by vocabulary words with prefix applied. - This function takes a `vocab_words` list and returns a string + This function takes a `vocab_words` list of strings and returns a string with the prefix and the words with prefix applied, separated by ' :: '. - For example: list('en', 'close', 'joy', 'lighten'), - produces the following string: 'en :: enclose :: enjoy :: enlighten'. + Examples: + >>> list('en', 'close', 'joy', 'lighten') + 'en :: enclose :: enjoy :: enlighten'. + """ pass @@ -32,10 +40,19 @@ def make_word_groups(vocab_words): def remove_suffix_ness(word): """Remove the suffix from the word while keeping spelling in mind. - :param word: str - of word to remove suffix from. - :return: str - of word with suffix removed & spelling adjusted. + Parameters: + word (str): Word to remove suffix from. + + Returns: + str: Word with suffix removed & spelling adjusted. + + Examples: + >>> remove_suffix_ness('heaviness') + 'heavy' + + >>> remove_suffix_ness('sadness') + 'sad' - For example: "heaviness" becomes "heavy", but "sadness" becomes "sad". """ pass @@ -44,11 +61,20 @@ def remove_suffix_ness(word): def adjective_to_verb(sentence, index): """Change the adjective within the sentence to a verb. - :param sentence: str - that uses the word in sentence. - :param index: int - index of the word to remove and transform. - :return: str - word that changes the extracted adjective to a verb. + Parameters: + sentence (str): The word used in a sentence as an adjective. + index (int): Index of the adjective to remove and transform. + + Returns: + str: The extracted adjective in verb form. + + Examples: + >>> adjective_to_verb('It got dark as the sun set.', 2) + 'darken' + + >>> adjective_to_verb('The ink stains her fingers black.', -1) + 'blacken' - For example, ("It got dark as the sun set.", 2) becomes "darken". """ pass From 58efbb47577f692191f997f511793495dda62e7b Mon Sep 17 00:00:00 2001 From: BethanyG Date: Mon, 11 May 2026 18:04:28 -0700 Subject: [PATCH 15/47] Updated exemplar and stub docstrigs to use Google style. (#4159) --- .../little-sisters-essay/.meta/exemplar.py | 32 +++++++++++------ .../little-sisters-essay/string_methods.py | 34 +++++++++++++------ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/exercises/concept/little-sisters-essay/.meta/exemplar.py b/exercises/concept/little-sisters-essay/.meta/exemplar.py index c51e930d82..f62fff90c3 100644 --- a/exercises/concept/little-sisters-essay/.meta/exemplar.py +++ b/exercises/concept/little-sisters-essay/.meta/exemplar.py @@ -4,8 +4,11 @@ def capitalize_title(title): """Convert the first letter of each word in the title to uppercase if needed. - :param title: str - title string that needs title casing. - :return: str - title string in title case (first letters capitalized). + Parameters: + title (str): Essay title that needs title casing. + + Returns: + str: The title string in title case (first letters capitalized). """ return title.title() @@ -14,8 +17,11 @@ def capitalize_title(title): def check_sentence_ending(sentence): """Check the ending of the sentence to verify that a period is present. - :param sentence: str - a sentence to check. - :return: bool - is the sentence punctuated correctly? + Parameters: + sentence (str): A sentence to check. + + Returns: + bool: Is the sentence punctuated correctly? """ return sentence.endswith(".") @@ -24,8 +30,11 @@ def check_sentence_ending(sentence): def clean_up_spacing(sentence): """Trim any leading or trailing whitespace from the sentence. - :param sentence: str - a sentence to clean of leading and trailing space characters. - :return: str - a sentence that has been cleaned of leading and trailing space characters. + Parameters: + sentence (str): A sentence to clean of leading and trailing space characters. + + Returns: + str: A sentence that has been cleaned of leading and trailing space characters. """ clean_sentence = sentence.strip() @@ -35,10 +44,13 @@ def clean_up_spacing(sentence): def replace_word_choice(sentence, old_word, new_word): """Replace a word in the provided sentence with a new one. - :param sentence: str - a sentence to replace words in. - :param old_word: str - word to replace. - :param new_word: str - replacement word. - :return: str - input sentence with new words in place of old words. + Parameters: + sentence (str): A sentence to replace words in. + old_word (str): The word to replace. + new_word (str): The replacement word. + + Returns: + str: Input sentence with new words in place of old words. """ better_sentence = sentence.replace(old_word, new_word) diff --git a/exercises/concept/little-sisters-essay/string_methods.py b/exercises/concept/little-sisters-essay/string_methods.py index 5c5b9ce66d..080dc8ec95 100644 --- a/exercises/concept/little-sisters-essay/string_methods.py +++ b/exercises/concept/little-sisters-essay/string_methods.py @@ -4,8 +4,11 @@ def capitalize_title(title): """Convert the first letter of each word in the title to uppercase if needed. - :param title: str - title string that needs title casing. - :return: str - title string in title case (first letters capitalized). + Parameters: + title (str): Essay title that needs title casing. + + Returns: + str: The title string in title case (first letters capitalized). """ pass @@ -14,18 +17,24 @@ def capitalize_title(title): def check_sentence_ending(sentence): """Check the ending of the sentence to verify that a period is present. - :param sentence: str - a sentence to check. - :return: bool - return True if punctuated correctly with period, False otherwise. + Parameters: + sentence (str): A sentence to check. + + Returns: + bool: Is the sentence punctuated correctly? """ pass def clean_up_spacing(sentence): - """Verify that there isn't any whitespace at the start and end of the sentence. + """Trim any leading or trailing whitespace from the sentence. - :param sentence: str - a sentence to clean of leading and trailing space characters. - :return: str - a sentence that has been cleaned of leading and trailing space characters. + Parameters: + sentence (str): A sentence to clean of leading and trailing space characters. + + Returns: + str: A sentence that has been cleaned of leading and trailing space characters. """ pass @@ -34,10 +43,13 @@ def clean_up_spacing(sentence): def replace_word_choice(sentence, old_word, new_word): """Replace a word in the provided sentence with a new one. - :param sentence: str - a sentence to replace words in. - :param old_word: str - word to replace. - :param new_word: str - replacement word. - :return: str - input sentence with new words in place of old words. + Parameters: + sentence (str): A sentence to replace words in. + old_word (str): The word to replace. + new_word (str): The replacement word. + + Returns: + str: Input sentence with new words in place of old words. """ pass From 1b30133382bb83dfd8d2e168c6fe16dd424e665a Mon Sep 17 00:00:00 2001 From: BethanyG Date: Mon, 11 May 2026 18:04:46 -0700 Subject: [PATCH 16/47] [Dict Methods Concept Exercise]: Docstring update for `mechamunch-management` (#4160) * Updated docstrings in stub to use Google style. * Updated docstrings in exemplar to use Google style. --- .../mecha-munch-management/.meta/exemplar.py | 53 +++++++++++++------ .../mecha-munch-management/dict_methods.py | 50 +++++++++++------ 2 files changed, 70 insertions(+), 33 deletions(-) diff --git a/exercises/concept/mecha-munch-management/.meta/exemplar.py b/exercises/concept/mecha-munch-management/.meta/exemplar.py index ea25110a3b..221a4c259e 100644 --- a/exercises/concept/mecha-munch-management/.meta/exemplar.py +++ b/exercises/concept/mecha-munch-management/.meta/exemplar.py @@ -4,9 +4,12 @@ def add_item(current_cart, items_to_add): """Add items to shopping cart. - :param current_cart: dict - the current shopping cart. - :param items_to_add: iterable - items to add to the cart. - :return: dict - the updated user cart dictionary. + Parameters: + current_cart (dict): The current shopping cart. + items_to_add (iterable): The items to add to the cart. + + Returns: + dict: The updated user cart dictionary. """ for item in items_to_add: @@ -19,8 +22,11 @@ def add_item(current_cart, items_to_add): def read_notes(notes): """Create user cart from an iterable notes entry. - :param notes: iterable of items to add to cart. - :return: dict - a user shopping cart dictionary. + Parameters: + notes (iterable): Group of items to add to cart. + + Returns: + dict: A user shopping cart dictionary. """ return dict.fromkeys(notes, 1) @@ -29,9 +35,12 @@ def read_notes(notes): def update_recipes(ideas, recipe_updates): """Update the recipe ideas dictionary. - :param ideas: dict - The "recipe ideas" dict. - :param recipe_updates: dict - dictionary with updates for the ideas section. - :return: dict - updated "recipe ideas" dict. + Parameters: + ideas (dict): The "recipe ideas" dict. + recipe_updates (iterable): Updates for the ideas section. + + Returns: + dict: The updated "recipe ideas" dict. """ ideas.update(recipe_updates) @@ -41,9 +50,12 @@ def update_recipes(ideas, recipe_updates): def sort_entries(cart): """Sort a users shopping cart in alphabetically order. - :param cart: dict - a users shopping cart dictionary. - :return: dict - users shopping cart sorted in alphabetical order. - """ + Parameters: + cart (dict): A users shopping cart dictionary. + + Returns: + dict: A sers shopping cart sorted in alphabetical order. + """ return dict(sorted(cart.items())) @@ -51,10 +63,14 @@ def sort_entries(cart): def send_to_store(cart, aisle_mapping): """Combine users order to aisle and refrigeration information. - :param cart: dict - users shopping cart dictionary. - :param aisle_mapping: dict - aisle and refrigeration information dictionary. - :return: dict - fulfillment dictionary ready to send to store. + Parameters: + cart (dict): The users shopping cart dictionary. + aisle_mapping (dict): The aisle and refrigeration information dictionary. + + Returns: + dict: The fulfillment dictionary ready to send to store. """ + fulfillment_cart = {} for key in cart.keys(): @@ -66,9 +82,12 @@ def send_to_store(cart, aisle_mapping): def update_store_inventory(fulfillment_cart, store_inventory): """Update store inventory levels with user order. - :param fulfillment cart: dict - fulfillment cart to send to store. - :param store_inventory: dict - store available inventory - :return: dict - store_inventory updated. + Parameters: + fulfillment cart (dict): The fulfillment cart to send to store. + store_inventory (dict): The stores available inventory. + + Returns: + dict: The store_inventory updated. """ for key, values in fulfillment_cart.items(): diff --git a/exercises/concept/mecha-munch-management/dict_methods.py b/exercises/concept/mecha-munch-management/dict_methods.py index 92bfd7325f..f6804281d2 100644 --- a/exercises/concept/mecha-munch-management/dict_methods.py +++ b/exercises/concept/mecha-munch-management/dict_methods.py @@ -4,9 +4,12 @@ def add_item(current_cart, items_to_add): """Add items to shopping cart. - :param current_cart: dict - the current shopping cart. - :param items_to_add: iterable - items to add to the cart. - :return: dict - the updated user cart dictionary. + Parameters: + current_cart (dict): The current shopping cart. + items_to_add (iterable): The items to add to the cart. + + Returns: + dict: The updated user cart dictionary. """ pass @@ -15,8 +18,11 @@ def add_item(current_cart, items_to_add): def read_notes(notes): """Create user cart from an iterable notes entry. - :param notes: iterable of items to add to cart. - :return: dict - a user shopping cart dictionary. + Parameters: + notes (iterable): Group of items to add to cart. + + Returns: + dict: A user shopping cart dictionary. """ pass @@ -25,9 +31,12 @@ def read_notes(notes): def update_recipes(ideas, recipe_updates): """Update the recipe ideas dictionary. - :param ideas: dict - The "recipe ideas" dict. - :param recipe_updates: iterable - with updates for the ideas section. - :return: dict - updated "recipe ideas" dict. + Parameters: + ideas (dict): The "recipe ideas" dict. + recipe_updates (iterable): Updates for the ideas section. + + Returns: + dict: The updated "recipe ideas" dict. """ pass @@ -36,8 +45,11 @@ def update_recipes(ideas, recipe_updates): def sort_entries(cart): """Sort a users shopping cart in alphabetically order. - :param cart: dict - a users shopping cart dictionary. - :return: dict - users shopping cart sorted in alphabetical order. + Parameters: + cart (dict): A users shopping cart dictionary. + + Returns: + dict: A sers shopping cart sorted in alphabetical order. """ pass @@ -46,9 +58,12 @@ def sort_entries(cart): def send_to_store(cart, aisle_mapping): """Combine users order to aisle and refrigeration information. - :param cart: dict - users shopping cart dictionary. - :param aisle_mapping: dict - aisle and refrigeration information dictionary. - :return: dict - fulfillment dictionary ready to send to store. + Parameters: + cart (dict): The users shopping cart dictionary. + aisle_mapping (dict): The aisle and refrigeration information dictionary. + + Returns: + dict: The fulfillment dictionary ready to send to store. """ pass @@ -57,9 +72,12 @@ def send_to_store(cart, aisle_mapping): def update_store_inventory(fulfillment_cart, store_inventory): """Update store inventory levels with user order. - :param fulfillment cart: dict - fulfillment cart to send to store. - :param store_inventory: dict - store available inventory - :return: dict - store_inventory updated. + Parameters: + fulfillment cart (dict): The fulfillment cart to send to store. + store_inventory (dict): The stores available inventory. + + Returns: + dict: The store_inventory updated. """ pass From f3cbd4620ddf88d52c2408ee54e849491b855ebc Mon Sep 17 00:00:00 2001 From: BethanyG Date: Mon, 11 May 2026 18:05:07 -0700 Subject: [PATCH 17/47] Updated docstrings in stub and exemplar to use Googel Style. (#4161) --- .../locomotive-engineer/.meta/exemplar.py | 43 ++++++++++++------ .../locomotive_engineer.py | 45 ++++++++++++------- 2 files changed, 59 insertions(+), 29 deletions(-) diff --git a/exercises/concept/locomotive-engineer/.meta/exemplar.py b/exercises/concept/locomotive-engineer/.meta/exemplar.py index bda295d269..a8a593b855 100644 --- a/exercises/concept/locomotive-engineer/.meta/exemplar.py +++ b/exercises/concept/locomotive-engineer/.meta/exemplar.py @@ -2,10 +2,13 @@ def get_list_of_wagons(*args): - """Return a list of wagons. + """Return a list of wagons, given an arbitrary amount of wagon numbers. - :param *args: arbitrary number of wagons. - :return: list - list of wagons. + Parameters: + *args: An arbitrary number of wagon numbers, unpacked. + + Returns: + list: A list of wagon numbers, assembled from *args.. """ return list(args) @@ -14,9 +17,12 @@ def get_list_of_wagons(*args): def fix_list_of_wagons(each_wagons_id, missing_wagons): """Fix the list of wagons. - :param each_wagons_id: list - the list of wagons. - :param missing_wagons: list - the list of missing wagons. - :return: list - list of wagons. + Parameters: + each_wagons_id (list[int]): The list of wagons. + missing_wagons (list[int]) The list of missing wagons. + + Returns: + list[int]: The corrected list of wagons. """ first, second, locomotive, *rest = each_wagons_id @@ -27,9 +33,12 @@ def fix_list_of_wagons(each_wagons_id, missing_wagons): def add_missing_stops(route, **kwargs): """Add missing stops to route dict. - :param route: dict - the dict of routing information. - :param **kwargs: arbitrary number of stops. - :return: dict - updated route dictionary. + Parameters: + route (dict): The dict of routing information. + **kwargs: arbitrary number of stops. + + Returns: + dict: The updated route dictionary. """ return {**route, "stops": list(kwargs.values())} @@ -38,9 +47,12 @@ def add_missing_stops(route, **kwargs): def extend_route_information(route, more_route_information): """Extend route information with more_route_information. - :param route: dict - the route information. - :param more_route_information: dict - extra route information. - :return: dict - extended route information. + Parameters: + route (dict): The route information. + more_route_information (dict): The extra route information. + + Returns: + dict: The extended route information. """ return {**route, **more_route_information} @@ -49,8 +61,11 @@ def extend_route_information(route, more_route_information): def fix_wagon_depot(wagons_rows): """Fix the list of rows of wagons. - :param wagons_rows: list[tuple] - the list of rows of wagons. - :return: list[tuple] - list of rows of wagons. + Parameters: + wagons_rows (list[tuple]) The list of rows of wagons. + + Returns: + list[tuple]: the list of rows of wagons. """ [*row_one], [*row_two], [*row_three] = zip(*wagons_rows) diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer.py b/exercises/concept/locomotive-engineer/locomotive_engineer.py index 180329208c..3a28255fde 100644 --- a/exercises/concept/locomotive-engineer/locomotive_engineer.py +++ b/exercises/concept/locomotive-engineer/locomotive_engineer.py @@ -2,10 +2,13 @@ def get_list_of_wagons(): - """Return a list of wagons. + """Return a list of wagons, given an arbitrary amount of wagon numbers. - :param: arbitrary number of wagons. - :return: list - list of wagons. + Parameters: + An arbitrary number of wagon numbers, unpacked. + + Returns: + list: A list of wagon numbers. """ pass @@ -13,19 +16,25 @@ def get_list_of_wagons(): def fix_list_of_wagons(each_wagons_id, missing_wagons): """Fix the list of wagons. - :param each_wagons_id: list - the list of wagons. - :param missing_wagons: list - the list of missing wagons. - :return: list - list of wagons. + Parameters: + each_wagons_id (list[int]): The list of wagons. + missing_wagons (list[int]) The list of missing wagons. + + Returns: + list[int]: The corrected list of wagons. """ pass -def add_missing_stops(): +def add_missing_stops(route): """Add missing stops to route dict. - :param route: dict - the dict of routing information. - :param: arbitrary number of stops. - :return: dict - updated route dictionary. + Parameters: + route (dict): The dict of routing information. + (dict): arbitrary number of stops. + + Returns: + dict: The updated route dictionary. """ pass @@ -33,9 +42,12 @@ def add_missing_stops(): def extend_route_information(route, more_route_information): """Extend route information with more_route_information. - :param route: dict - the route information. - :param more_route_information: dict - extra route information. - :return: dict - extended route information. + Parameters: + route (dict): The route information. + more_route_information (dict): The extra route information. + + Returns: + dict: The extended route information. """ pass @@ -43,7 +55,10 @@ def extend_route_information(route, more_route_information): def fix_wagon_depot(wagons_rows): """Fix the list of rows of wagons. - :param wagons_rows: list[list[tuple]] - the list of rows of wagons. - :return: list[list[tuple]] - list of rows of wagons. + Parameters: + wagons_rows (list[tuple]) The list of rows of wagons. + + Returns: + list[tuple]: the list of rows of wagons. """ pass From 98c308ac62a35514d1f8478d7506dec68ef73054 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Mon, 11 May 2026 19:25:31 -0700 Subject: [PATCH 18/47] Updated docstrings for exemlar solutions to follow Google style. (#4162) --- .../guidos-gorgeous-lasagna/.meta/exemplar.py | 25 +++++-- .../meltdown-mitigation/.meta/exemplar.py | 66 +++++++++++-------- .../concept/plane-tickets/.meta/exemplar.py | 47 +++++++------ .../tisbury-treasure-hunt/.meta/exemplar.py | 44 +++++++++---- 4 files changed, 115 insertions(+), 67 deletions(-) diff --git a/exercises/concept/guidos-gorgeous-lasagna/.meta/exemplar.py b/exercises/concept/guidos-gorgeous-lasagna/.meta/exemplar.py index ff0bff38e1..093571a293 100644 --- a/exercises/concept/guidos-gorgeous-lasagna/.meta/exemplar.py +++ b/exercises/concept/guidos-gorgeous-lasagna/.meta/exemplar.py @@ -11,8 +11,11 @@ def bake_time_remaining(elapsed_bake_time): """Calculate the bake time remaining. - :param elapsed_bake_time: int - baking time already elapsed. - :return: int - remaining bake time (in minutes) derived from 'EXPECTED_BAKE_TIME'. + Parameters: + elapsed_bake_time (int): The baking time already elapsed. + + Returns: + int: The remaining bake time (in minutes) derived from 'EXPECTED_BAKE_TIME'. Function that takes the actual minutes the lasagna has been in the oven as an argument and returns how many minutes the lasagna still needs to bake @@ -25,11 +28,15 @@ def bake_time_remaining(elapsed_bake_time): def preparation_time_in_minutes(number_of_layers): """Calculate the preparation time. - :param number_of_layers: int - the number of lasagna layers made. - :return: int - amount of prep time (in minutes), based on 2 minutes per layer added. + Parameters: + number_of_layers(int): The number of lasagna layers made. + + Returns: + int: Amount of prep time (in minutes), based on 2 minutes per layer added. This function takes an integer representing the number of layers added to the dish, calculating preparation time using a time of 2 minutes per layer added. + """ return number_of_layers * PREPARATION_TIME @@ -38,13 +45,17 @@ def preparation_time_in_minutes(number_of_layers): def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time): """Calculate the elapsed time. - :param number_of_layers: int - the number of layers in the lasagna. - :param elapsed_bake_time: int - elapsed cooking time. - :return: int - total time elapsed (in in minutes) preparing and cooking. + Parameters: + number_of_layers (int): The number of layers in the lasagna. + elapsed_bake_time (int): Elapsed cooking time. + + Returns: + int: Total time elapsed (in minutes) preparing + cooking. This function takes two integers representing the number of lasagna layers and the time already spent baking and calculates the total elapsed minutes spent cooking the lasagna. + """ return preparation_time_in_minutes(number_of_layers) + elapsed_bake_time diff --git a/exercises/concept/meltdown-mitigation/.meta/exemplar.py b/exercises/concept/meltdown-mitigation/.meta/exemplar.py index c2d8ddd7ed..ef2bb20147 100644 --- a/exercises/concept/meltdown-mitigation/.meta/exemplar.py +++ b/exercises/concept/meltdown-mitigation/.meta/exemplar.py @@ -4,14 +4,19 @@ def is_criticality_balanced(temperature, neutrons_emitted): """Verify criticality is balanced. - :param temperature: int or float - temperature value in kelvin. - :param neutrons_emitted: int or float - number of neutrons emitted per second. - :return: bool - is criticality balanced? - - A reactor is said to be critical if it satisfies the following conditions: - - The temperature is less than 800 K. - - The number of neutrons emitted per second is greater than 500. - - The product of temperature and neutrons emitted per second is less than 500000. + Parameters: + temperature (int or float): The temperature value in kelvin. + neutrons_emitted (int or float): The number of neutrons emitted per second. + + Returns: + bool: Is criticality balanced? + + Note: + A reactor is said to be balanced in criticality if it satisfies the following conditions: + - The temperature is less than 800 K. + - The number of neutrons emitted per second is greater than 500. + - The product of temperature and neutrons emitted per second is less than 500000. + """ output = temperature * neutrons_emitted @@ -26,21 +31,24 @@ def is_criticality_balanced(temperature, neutrons_emitted): def reactor_efficiency(voltage, current, theoretical_max_power): """Assess reactor efficiency zone. - :param voltage: int or float - voltage value. - :param current: int or float - current value. - :param theoretical_max_power: int or float - power that corresponds to a 100% efficiency. - :return: str - one of ('green', 'orange', 'red', or 'black'). + Parameters: + voltage (int or float): Voltage value. + current (int or float): Current value. + theoretical_max_power (int or float): The power level that corresponds to a 100% efficiency. - Efficiency can be grouped into 4 bands: + Returns: + str: One of ('green', 'orange', 'red', or 'black'). - 1. green -> efficiency of 80% or more, - 2. orange -> efficiency of less than 80% but at least 60%, - 3. red -> efficiency below 60%, but still 30% or more, - 4. black -> less than 30% efficient. + Note: + Efficiency can be grouped into 4 bands: + 1. green -> efficiency of 80% or more, + 2. orange -> efficiency of less than 80% but at least 60%, + 3. red -> efficiency below 60%, but still 30% or more, + 4. black -> less than 30% efficient. - The percentage value is calculated as - (generated power/ theoretical max power)*100 - where generated power = voltage * current + The percentage value is calculated as + (generated power/ theoretical max power)*100 + where generated power = voltage * current """ generated_power = voltage * current @@ -61,14 +69,18 @@ def reactor_efficiency(voltage, current, theoretical_max_power): def fail_safe(temperature, neutrons_produced_per_second, threshold): """Assess and return status code for the reactor. - :param temperature: int or float - value of the temperature in kelvin. - :param neutrons_produced_per_second: int or float - neutron flux. - :param threshold: int or float - threshold for category. - :return: str - one of ('LOW', 'NORMAL', 'DANGER'). + Parameters: + temperature (int or float): The value of the temperature in kelvin. + neutrons_produced_per_second (int or float): The neutron flux. + threshold (int or float): The threshold for the category. + + Returns: + str: One of ('LOW', 'NORMAL', 'DANGER'). - 1. 'LOW' -> `temperature * neutrons per second` < 90% of `threshold` - 2. 'NORMAL' -> `temperature * neutrons per second` +/- 10% of `threshold` - 3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges + Note: + 1. 'LOW' -> `temperature * neutrons per second` < 90% of `threshold` + 2. 'NORMAL' -> `temperature * neutrons per second` +/- 10% of `threshold` + 3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges """ output = temperature * neutrons_produced_per_second diff --git a/exercises/concept/plane-tickets/.meta/exemplar.py b/exercises/concept/plane-tickets/.meta/exemplar.py index 8261795c66..29d6c1561d 100644 --- a/exercises/concept/plane-tickets/.meta/exemplar.py +++ b/exercises/concept/plane-tickets/.meta/exemplar.py @@ -8,13 +8,16 @@ def generate_seat_letters(number): """Generate a series of letters for airline seats. - :param number: int - total number of seat letters to be generated. - :return: generator - generator that yields seat letters. + Parameters: + number (int): Total number of seat letters to be generated. - Seat letters are generated from A to D. - After D it should start again with A. + Returns: + generator: A generator that yields seat letters. - Example: A, B, C, D + Note: + Seat letters are generated from A to D. + After D the sequence starts again with A. + For example: A, B, C, D, A, B """ @@ -25,17 +28,18 @@ def generate_seat_letters(number): def generate_seats(number): """Generate a series of identifiers for airline seats. - :param number: int - total number of seats to be generated. - :return: generator - generator that yields seat numbers. + Parameters: + number (int): The total number of seats to be generated. - A seat number consists of the row number and the seat letter. + Returns: + generator: A generator that yields seat numbers. - There is no row 13. - Each row has 4 seats. + Note: + A seat number consists of the row number and the seat letter. + There is no row 13, and each row has 4 seats. - Seats should be sorted from low to high. - - Example: 3C, 3D, 4A, 4B + Seats should be sorted from low to high. + For exampl: 3C, 3D, 4A, 4B """ @@ -53,10 +57,12 @@ def generate_seats(number): def assign_seats(passengers): """Assign seats to passengers. - :param passengers: list[str] - a list of strings containing names of passengers. - :return: dict - with the names of the passengers as keys and seat numbers as values. + Parameters: + passengers (list[str]): A list of strings containing names of passengers. - Example output: {"Adele": "1A", "Björk": "1B"} + Returns: + dict: With passenger names as keys and seat numbers as values. + Example output: {"Adele": "1A", "Björk": "1B"} """ @@ -69,9 +75,12 @@ def assign_seats(passengers): def generate_codes(seat_numbers, flight_id): """Generate codes for a ticket. - :param seat_numbers: list[str] - list of seat numbers. - :param flight_id: str - string containing the flight identifier. - :return: generator - generator that yields 12 character long ticket codes. + Parameters: + seat_numbers (list[str]): A list of seat numbers. + flight_id (str): A string containing the flight identifier. + + Returns: + generator: A generator that yields 12 character long ticket codes. """ diff --git a/exercises/concept/tisbury-treasure-hunt/.meta/exemplar.py b/exercises/concept/tisbury-treasure-hunt/.meta/exemplar.py index 1b4baa26aa..74a3a2939e 100644 --- a/exercises/concept/tisbury-treasure-hunt/.meta/exemplar.py +++ b/exercises/concept/tisbury-treasure-hunt/.meta/exemplar.py @@ -4,8 +4,11 @@ def get_coordinate(record): """Return coordinate value from a tuple containing the treasure name, and treasure coordinate. - :param record: tuple - with a (treasure, coordinate) pair. - :return: str - the extracted map coordinate. + Parameters: + record (tuple): A (treasure, coordinate) pair. + + Returns: + str: The extracted map coordinate. """ return record[1] @@ -14,8 +17,11 @@ def get_coordinate(record): def convert_coordinate(coordinate): """Split the given coordinate into tuple containing its individual components. - :param coordinate: str - a string map coordinate - :return: tuple - the string coordinate split into its individual components. + Parameters: + coordinate (str): A string map coordinate. + + Returns: + tuple: The string coordinate split into its individual components. """ return tuple(coordinate) @@ -24,9 +30,12 @@ def convert_coordinate(coordinate): def compare_records(azara_record, rui_record): """Compare two record types and determine if their coordinates match. - :param azara_record: tuple - a (treasure, coordinate) pair. - :param rui_record: tuple - a (location, tuple(coordinate_1, coordinate_2), quadrant) trio. - :return: bool - do the coordinates match? + Parameters: + azara_record (tuple): A (treasure, coordinate) pair. + rui_record (tuple): A (location, tuple(coordinate_1, coordinate_2), quadrant) trio. + + Returns: + bool: Do the coordinates match? """ return convert_coordinate(azara_record[1]) in rui_record @@ -35,9 +44,12 @@ def compare_records(azara_record, rui_record): def create_record(azara_record, rui_record): """Combine the two record types (if possible) and create a combined record group. - :param azara_record: tuple - a (treasure, coordinate) pair. - :param rui_record: tuple - a (location, coordinate, quadrant) trio. - :return: tuple or str - the combined record (if compatible), or the string "not a match" (if incompatible). + Parameters: + azara_record (tuple): A (treasure, coordinate) pair. + rui_record (tuple): A (location, coordinate, quadrant) trio. + + Returns: + tuple or str: The combined record (if compatible), or the string "not a match" (if incompatible). """ result = "not a match" @@ -51,12 +63,16 @@ def create_record(azara_record, rui_record): def clean_up(combined_record_group): """Clean up a combined record group into a multi-line string of single records. - :param combined_record_group: tuple - everything from both participants. - :return: str - everything "cleaned", excess coordinates and information are removed. + Parameters: + combined_record_group (tuple): Everything from both participants. + + Returns: + str: Everything "cleaned", excess coordinates and information are removed. - The return statement should be a multi-lined string with items separated by newlines. + Note: + The return statement is a multi-lined string with items separated by newlines. + (see HINTS.md for an example). - (see HINTS.md for an example). """ report = "" From ae6554c6997a36be55d000557064c2d00131c74b Mon Sep 17 00:00:00 2001 From: BethanyG Date: Mon, 11 May 2026 19:26:01 -0700 Subject: [PATCH 19/47] Updated docstrings to use Google style. (#4163) --- .../currency-exchange/.meta/exemplar.py | 137 ++++++++++++++---- .../concept/currency-exchange/exchange.py | 2 +- .../ellens-alien-game/.meta/exemplar.py | 70 +++++---- .../.meta/exemplar.py | 40 +++-- 4 files changed, 181 insertions(+), 68 deletions(-) diff --git a/exercises/concept/currency-exchange/.meta/exemplar.py b/exercises/concept/currency-exchange/.meta/exemplar.py index 1cd9b2262e..c7e497bbe1 100644 --- a/exercises/concept/currency-exchange/.meta/exemplar.py +++ b/exercises/concept/currency-exchange/.meta/exemplar.py @@ -6,72 +6,157 @@ """ def exchange_money(budget, exchange_rate): - """ + """Calculate estimated value after exchange. + + Parameters: + budget (float): Tthe amount of money you are planning to exchange. + exchange_rate (float): The unit value of the foreign currency. + + Returns: + float: The exchanged value of the foreign currency you can receive. + + Examples: + >>> exchange_money(127.5, 1.2) + 106.25 + + >>> exchange_money(200, 1.10) + 181.82 + + This function calculates and returns the (estimated) value of the exchanged currency. - :param budget: float - amount of money you are planning to exchange. - :param exchange_rate: float - unit value of the foreign currency. - :return: float - exchanged value of the foreign currency you can receive. """ return budget / exchange_rate def get_change(budget, exchanging_value): - """ + """Calculate currency left after an exchange. + + Parameters: + budget (float): The amount of money you own. + exchanging_value (float): The amount of your money you want to exchange now. + + Returns: + float: The amount left of your starting currency after the exchange + + Examples: + .>>> get_change(127.5, 120.0) + 7.5 + + >>> get_change(300.75, 150.25) + 150.50 + + This function calcultes and returns the amount of money left over from the budget + after an exchange. - :param budget: float - amount of money you own. - :param exchanging_value: float - amount of your money you want to exchange now. - :return: float - amount left of your starting currency after exchanging. """ return budget - exchanging_value def get_value_of_bills(denomination, number_of_bills): - """ + """Calculate the total value of currency at current denomination. + + Parameters: + denomination (int): The value of a single unit (bill). + number_of_bills (int): The total number of units (bills). + + Returns: + int: Calculated value of the units (bills). + + Examples: + >>> get_value_of_bills(5, 128) + 640 + + >>> get_value_of_bills(15.13, 16) + 242 + + This function calculates and returns the total value of the bills (excluding fractionaal amounts). - :param denomination: int - the value of a bill. - :param number_of_bills: int - total number of bills. - :return: int - calculated value of the bills. """ return denomination * number_of_bills def get_number_of_bills(amount, denomination): - """ + """Calculate the number of currency units (bills) within the amount. + + Parameters: + amount (float): The total starting value. + denomination (int): The value of a single unit (bill). + + Returns: + int: The number of units (bills) that can be obtained from the amount. + + Examples: + >>> get_number_of_bills(127.5, 5) + 25 + + >>> get_number_of_bills(35.16, 10) + 3 + + This function calcluates and returns the number pf currency units (bills) that can + be obtained from the given amount. Whole bills only - no fractioal amounts. - :param amount: float - the total starting value. - :param denomination: int - the value of a single bill. - :return: int - number of bills that can be obtained from the amount. """ return int(amount) // denomination def get_leftover_of_bills(amount, denomination): - """ + """Calculate leftover amount after exchanging into bills. + + Parameters: + amount (float): The total starting value. + denomination (int): The value of a single unit (bill). + + Returns: + float: The amount that is "leftover", given the current denomination. + + Examples: + >>> get_leftover_of_bills(127.5, 20) + 7.5 + + >>> get_leftover_of_bills(153.2, 10) + 3.20 + + This function calculates and returns the leftover amount that cannot be + returned from starting amount, due to the currency denomination. - :param amount: float - the total starting value. - :param denomination: int - the value of a single bill. - :return: float - the amount that is "leftover", given the current denomination. """ return amount % denomination def exchangeable_value(budget, exchange_rate, spread, denomination): - """ + """Calculate the maximum value of the new currency. + + Parameters: + budget (float): The amount of your money you are planning to exchange. + exchange_rate (float): The unit value of the foreign currency. + spread (int): The percentage that is taken as an exchange fee. + denomination (int) The value of a single unit (bill). + + Returns: + int: The maximum value you can get in the new currency. - :param budget: float - the amount of your money you are planning to exchange. - :param exchange_rate: float - the unit value of the foreign currency. - :param spread: int - percentage that is taken as an exchange fee. - :param denomination: int - the value of a single bill. - :return: int - maximum value you can get. + Examples: + >>> exchangeable_value(127.25, 1.20, 10, 20) + 80 + + >>> exchangeable_value(127.25, 1.20, 10, 5) + 95 + + Note: + The currency denomination is a whole number and cannot be sub-divided. + + This function calculates and returns the maximum value of the new currency after + determining the exchange rate plus the spread. """ exchange_fee = (exchange_rate / 100) * spread exchange_value = exchange_money(budget, exchange_rate + exchange_fee) number_of_bills = get_number_of_bills(exchange_value, denomination) value_of_bills = get_value_of_bills(denomination, number_of_bills) + return value_of_bills diff --git a/exercises/concept/currency-exchange/exchange.py b/exercises/concept/currency-exchange/exchange.py index 36d972f798..8d4cce6cf1 100644 --- a/exercises/concept/currency-exchange/exchange.py +++ b/exercises/concept/currency-exchange/exchange.py @@ -48,7 +48,7 @@ def get_change(budget, exchanging_value): >>> get_change(300.75, 150.25) 150.50 - Tthis function calcultes and returns the amount of money left over from the budget + This function calcultes and returns the amount of money left over from the budget after an exchange. """ diff --git a/exercises/concept/ellens-alien-game/.meta/exemplar.py b/exercises/concept/ellens-alien-game/.meta/exemplar.py index ace31649c7..d4441f3b74 100644 --- a/exercises/concept/ellens-alien-game/.meta/exemplar.py +++ b/exercises/concept/ellens-alien-game/.meta/exemplar.py @@ -4,19 +4,18 @@ class Alien: """Create an Alien object with location x_coordinate and y_coordinate. - Attributes - ---------- - (class)total_aliens_created: int - x_coordinate: int - Position on the x-axis. - y_coordinate: int - Position on the y-axis. - health: int - Number of health points. - - Methods - ------- - hit(): Decrement Alien health by one point. - is_alive(): Return a boolean to indicate if Alien is alive (if health is > 0). - teleport(new_x_coordinate, new_y_coordinate): Move Alien object to new coordinates. - collision_detection(other): Implementation TBD. + Attributes: + (class) total_aliens_created (int): Total number of Alien instances. + x_coordinate (int): Position on the x-axis. + y_coordinate (int): Position on the y-axis. + health (int): Number of health points. + + Methods: + hit(): Decrement Alien health by one point. + is_alive(): Return a boolean for if Alien is alive (if health is > 0). + teleport(new_x_coordinate, new_y_coordinate): Move Alien object to new coordinates. + collision_detection(other): Implementation TBD. + """ total_aliens_created = 0 @@ -24,14 +23,18 @@ class Alien: def __init__(self, x_coordinate, y_coordinate): """Initialize a new Alien object and increment total_aliens_created by 1. - :param x_coordinate: int - Alien position on the x-axis - :param y_coordinate: int - Alien position on the y-axis + Parameters: + x_coordinate (int): Position on the x-axis. + y_coordinate (int): Position on the y-axis. + health (int): Number of health points. - :attribute x_coordinate: int - Alien position on the x-axis - :attribute y_coordinate: int - Alien position on the y-axis - :attribute health: int (3) - Initial Alien health points. + Attributes: + x_coordinate (int): Position on the x-axis. + y_coordinate (int): Position on the y-axis. + health (int): Number of health points. Defaults to 3. - :return: object - New Alien. + Returns: + Alien (Alien Object): New Alien. """ Alien.total_aliens_created += 1 @@ -43,7 +46,8 @@ def __init__(self, x_coordinate, y_coordinate): def hit(self): """Decrement Alien health by 1. - :return: None + Returns: + None """ #There are two valid interpretations for this method/task. @@ -54,7 +58,8 @@ def hit(self): def is_alive(self): """Return if the Alien is alive. - :return: boolean + Returns: + bool: Is the Alien Alive? """ return self.health > 0 @@ -62,29 +67,38 @@ def is_alive(self): def teleport(self, new_x_coordinate, new_y_coordinate): """Change Alien location. - :param new_x_coordinate: int - New location on x-axis. - :param new_y_coordinate: int - New location on y-axis. + Parameters: + new_x_coordinate (int): New location on x-axis. + new_y_coordinate (int): New location on y-axis. - :return: None + Returns: + None """ + self.x_coordinate = new_x_coordinate self.y_coordinate = new_y_coordinate def collision_detection(self, other): """Detect collisions with another Alien. - :param other: object - Other Alien object. + Parameters: + other (object): Other Alien object. - :return: None + Returns: + None """ pass + def new_aliens_collection(positions): """Create a list of Alien instances from a list of coordinate tuples. - :param positions: list - List of tuples of (x, y) coordinates. + Parameters: + positions (list[tuple]): List of (x, y) coordinates in tuples.. - :return: list - List of Alien objects. + Returns: + list[object]: List of Alien objects. """ + return [Alien(position[0], position[1]) for position in positions] diff --git a/exercises/concept/ghost-gobble-arcade-game/.meta/exemplar.py b/exercises/concept/ghost-gobble-arcade-game/.meta/exemplar.py index 4de10a25d5..f27df85d70 100644 --- a/exercises/concept/ghost-gobble-arcade-game/.meta/exemplar.py +++ b/exercises/concept/ghost-gobble-arcade-game/.meta/exemplar.py @@ -4,9 +4,13 @@ def eat_ghost(power_pellet_active, touching_ghost): """Verify that Pac-Man can eat a ghost if he is empowered by a power pellet. - :param power_pellet_active: bool - does the player have an active power pellet? - :param touching_ghost: bool - is the player touching a ghost? - :return: bool - can a ghost be eaten? + Parameters: + power_pellet_active (bool): Does the player have an active power pellet? + touching_ghost (bool): Is the player touching a ghost? + + Returns: + bool: Can a ghost be eaten? + """ return power_pellet_active and touching_ghost @@ -15,9 +19,13 @@ def eat_ghost(power_pellet_active, touching_ghost): def score(touching_power_pellet, touching_dot): """Verify that Pac-Man has scored when a power pellet or dot has been eaten. - :param touching_power_pellet: bool - is the player touching a power pellet? - :param touching_dot: bool - is the player touching a dot? - :return: bool - has the player scored or not? + Parameters: + touching_power_pellet (bool): Is the player touching a power pellet? + touching_dot (bool): Is the player touching a dot? + + Returns: + bool: Has the player scored or not? + """ return touching_power_pellet or touching_dot @@ -26,9 +34,12 @@ def score(touching_power_pellet, touching_dot): def lose(power_pellet_active, touching_ghost): """Trigger the game loop to end (GAME OVER) when Pac-Man touches a ghost without his power pellet. - :param power_pellet_active: bool - does the player have an active power pellet? - :param touching_ghost: bool - is the player touching a ghost? - :return: bool - has the player lost the game? + Parameters: + power_pellet_active (bool): Does the player have an active power pellet? + touching_ghost (bool): Is the player touching a ghost? + + Returns: + bool: Has the player lost the game? """ return not power_pellet_active and touching_ghost @@ -37,10 +48,13 @@ def lose(power_pellet_active, touching_ghost): def win(has_eaten_all_dots, power_pellet_active, touching_ghost): """Trigger the victory event when all dots have been eaten. - :param has_eaten_all_dots: bool - has the player "eaten" all the dots? - :param power_pellet_active: bool - does the player have an active power pellet? - :param touching_ghost: bool - is the player touching a ghost? - :return: bool - has the player won the game? + Parameters: + has_eaten_all_dots (bool): Has the player "eaten" all the dots? + power_pellet_active (bool): Does the player have an active power pellet? + touching_ghost (bool): Is the player touching a ghost? + + Returns: + bool: Has the player won the game? """ return has_eaten_all_dots and not lose(power_pellet_active, touching_ghost) From 0a7ec8aa8fc0f98e2bb7a0145359333fb37b47ec Mon Sep 17 00:00:00 2001 From: BethanyG Date: Mon, 11 May 2026 19:28:58 -0700 Subject: [PATCH 20/47] Updated docstrings in exemplar files to use Google style. (#4164) --- .../concept/black-jack/.meta/exemplar.py | 73 ++++++++++++------- .../concept/card-games/.meta/exemplar.py | 53 ++++++++++---- .../concept/cater-waiter/.meta/exemplar.py | 71 ++++++++++++------ .../.meta/exemplar.py | 65 +++++++++++------ 4 files changed, 176 insertions(+), 86 deletions(-) diff --git a/exercises/concept/black-jack/.meta/exemplar.py b/exercises/concept/black-jack/.meta/exemplar.py index 27c7a5dc07..ae8df079c1 100644 --- a/exercises/concept/black-jack/.meta/exemplar.py +++ b/exercises/concept/black-jack/.meta/exemplar.py @@ -8,12 +8,15 @@ def value_of_card(card): """Determine the scoring value of a card. - :param card: str - given card. - :return: int - value of a given card. See below for values. + Parameters: + card (str): The given card. - 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 - 2. 'A' (ace card) = 1 - 3. '2' - '10' = numerical value. + Returns: + int: The value of a given card. See below for values. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 1 + 3. '2' - '10' = numerical value. """ if card in ('JQK'): @@ -31,12 +34,16 @@ def value_of_card(card): def higher_card(card_one, card_two): """Determine which card has a higher value in the hand. - :param card_one, card_two: str - cards dealt in hand. See below for values. - :return: str or tuple - resulting Tuple contains both cards if they are of equal value. + Parameters: + card_one (str): First card dealt in the hand. See below for values. + card_two (str): Second card dealt in the hand. See below for values. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 1 + 3. '2' - '10' = numerical value. - 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 - 2. 'A' (ace card) = 1 - 3. '2' - '10' = numerical value. + Returns: + str or tuple: The resulting Tuple contains both cards if they are of equal value. """ card_one_value = value_of_card(card_one) @@ -55,14 +62,18 @@ def higher_card(card_one, card_two): def value_of_ace(card_one, card_two): - """Calculate the most advantageous value for the ace card. + """Calculate the most advantageous value for an upcoming ace card. + + Parameters: + card_one (str): First card dealt in the hand. See below for values. + card_two (str): Second card dealt in the hand. See below for values. - :param card_one, card_two: str - card dealt. See below for values. - :return: int - either 1 or 11 value of the upcoming ace card. + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. - 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 - 2. 'A' (ace card) = 11 (if already in hand) - 3. '2' - '10' = numerical value. + Returns: + int: Either 1 or 11, which is the value of the upcoming ace card. """ card_one_value = 11 if card_one == 'A' else value_of_card(card_one) @@ -76,12 +87,16 @@ def value_of_ace(card_one, card_two): def is_blackjack(card_one, card_two): """Determine if the hand is a 'natural' or 'blackjack'. - :param card_one, card_two: str - card dealt. See below for values. - :return: bool - is the hand is a blackjack (two cards worth 21). + Parameters: + card_one (str): First card dealt in the hand. See below for values. + card_two (str): Second card dealt in the hand. See below for values. - 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 - 2. 'A' (ace card) = 11 (if already in hand) - 3. '2' - '10' = numerical value. + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. + + Returns: + bool: Is the hand is a blackjack (two cards worth 21). """ return (card_one == 'A' or card_two == 'A') and (value_of_card(card_one) == 10 or value_of_card(card_two) == 10) @@ -90,8 +105,12 @@ def is_blackjack(card_one, card_two): def can_split_pairs(card_one, card_two): """Determine if a player can split their hand into two hands. - :param card_one, card_two: str - cards dealt. - :return: bool - can the hand be split into two pairs? (i.e. cards are of the same value). + Parameters: + card_one (str): First card in the hand. + card_two (str): Second card in the hand. + + Returns: + bool: Can the hand be split into two pairs? (i.e. cards are of the same value). """ return value_of_card(card_one) == value_of_card(card_two) @@ -100,8 +119,12 @@ def can_split_pairs(card_one, card_two): def can_double_down(card_one, card_two): """Determine if a blackjack player can place a double down bet. - :param card_one, card_two: str - first and second cards in hand. - :return: bool - can the hand can be doubled down? (i.e. totals 9, 10 or 11 points). + Parameters: + card_one (str): First card in the hand. + card_two (str): Second card in the hand. + + Returns: + bool: Can the hand can be doubled down? (i.e. totals 9, 10 or 11 points). """ return 8 < value_of_card(card_one) + value_of_card(card_two) < 12 diff --git a/exercises/concept/card-games/.meta/exemplar.py b/exercises/concept/card-games/.meta/exemplar.py index d6531f0186..35b1b5377b 100644 --- a/exercises/concept/card-games/.meta/exemplar.py +++ b/exercises/concept/card-games/.meta/exemplar.py @@ -7,8 +7,11 @@ def get_rounds(number): """Create a list containing the current and next two round numbers. - :param number: int - current round number. - :return: list - current round and the two that follow. + Parameters: + number (int): The current round number. + + Returns: + list: The current round number and the two that follow. """ return [number, number + 1, number + 2] @@ -17,9 +20,12 @@ def get_rounds(number): def concatenate_rounds(rounds_1, rounds_2): """Concatenate two lists of round numbers. - :param rounds_1: list - first rounds played. - :param rounds_2: list - second set of rounds played. - :return: list - all rounds played. + Parameters: + rounds_1 (list): The first rounds played. + rounds_2 (list): The second group of rounds played. + + Returns: + list: All rounds played. """ return rounds_1 + rounds_2 @@ -28,9 +34,12 @@ def concatenate_rounds(rounds_1, rounds_2): def list_contains_round(rounds, number): """Check if the list of rounds contains the specified number. - :param rounds: list - rounds played. - :param number: int - round number. - :return: bool - was the round played? + Parameters: + rounds (list): The rounds played. + number (int): The round number. + + Returns: + bool: Was the round played? """ return number in rounds @@ -39,8 +48,11 @@ def list_contains_round(rounds, number): def card_average(hand): """Calculate and returns the average card value from the list. - :param hand: list - cards in hand. - :return: float - average value of the cards in the hand. + Parameters: + hand (list): The cards in the hand. + + Returns: + float: The average value of the cards in the hand. """ return sum(hand) / len(hand) @@ -49,8 +61,11 @@ def card_average(hand): def approx_average_is_average(hand): """Return if the (average of first and last card values) OR ('middle' card) == calculated average. - :param hand: list - cards in hand. - :return: bool - does one of the approximate averages equal the `true average`? + Parameters: + hand (list): The cards in the hand. + + Returns: + bool: Does one of the approximate averages equal the `true average`? """ real_average = card_average(hand) @@ -68,8 +83,11 @@ def approx_average_is_average(hand): def average_even_is_average_odd(hand): """Return if the (average of even indexed card values) == (average of odd indexed card values). - :param hand: list - cards in hand. - :return: bool - are even and odd averages equal? + Parameters: + hand (list): The cards in the hand. + + Returns: + bool: Are the even and odd averages equal? """ return card_average(hand[::2]) == card_average(hand[1::2]) @@ -78,8 +96,11 @@ def average_even_is_average_odd(hand): def maybe_double_last(hand): """Multiply a Jack card value in the last index position by 2. - :param hand: list - cards in hand. - :return: list - hand with Jacks (if present) value doubled. + Parameters: + hand (list): The cards in the hand. + + Returns: + list: The hand with Jacks (if present) value doubled. """ if hand[-1] == 11: diff --git a/exercises/concept/cater-waiter/.meta/exemplar.py b/exercises/concept/cater-waiter/.meta/exemplar.py index 8f48c520f2..b9c5d3e198 100644 --- a/exercises/concept/cater-waiter/.meta/exemplar.py +++ b/exercises/concept/cater-waiter/.meta/exemplar.py @@ -13,12 +13,16 @@ def clean_ingredients(dish_name, dish_ingredients): """Remove duplicates from `dish_ingredients`. - :param dish_name: str - containing the dish name. - :param dish_ingredients: list - dish ingredients. - :return: tuple - containing (dish_name, ingredient set). + Parameters: + dish_name (str): The name of the dish. + dish_ingredients (list): The ingredients for the dish. + + Returns: + tuple: Containing (dish_name, ingredient set). This function should return a `tuple` with the name of the dish as the first item, followed by the de-duped `set` of ingredients as the second item. + """ return dish_name, set(dish_ingredients) @@ -27,12 +31,16 @@ def clean_ingredients(dish_name, dish_ingredients): def check_drinks(drink_name, drink_ingredients): """Append "Cocktail" (alcohol) or "Mocktail" (no alcohol) to `drink_name`, based on `drink_ingredients`. - :param drink_name: str - name of the drink. - :param drink_ingredients: list - ingredients in the drink. - :return: str - drink_name appended with "Mocktail" or "Cocktail". + Parameters: + drink_name (str): Name of the drink. + drink_ingredients (list): Ingredients in the drink. + + Returns: + str: drink_name appended with "Mocktail" or "Cocktail". The function should return the name of the drink followed by "Mocktail" (non-alcoholic) and drink name followed by "Cocktail" (includes alcohol). + """ if not ALCOHOLS.isdisjoint(drink_ingredients): @@ -44,13 +52,16 @@ def check_drinks(drink_name, drink_ingredients): def categorize_dish(dish_name, dish_ingredients): """Categorize `dish_name` based on `dish_ingredients`. - :param dish_name: str - dish to be categorized. - :param dish_ingredients: list - ingredients for the dish. - :return: str - the dish name appended with ": ". + Parameters: + dish_name (str): The dish to be categorized. + dish_ingredients (set): The ingredients for the dish. + + Returns: + str: TThe dish name appended with ": ". This function should return a string with the `dish name: ` (which meal category the dish belongs to). `` can be any one of (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE). - All dishes will "fit" into one of the categories imported from `sets_categories_data.py` + All dishes will "fit" into one of the categories imported from `sets_categories_data.py`. """ @@ -69,8 +80,11 @@ def categorize_dish(dish_name, dish_ingredients): def tag_special_ingredients(dish): """Compare `dish` ingredients to `SPECIAL_INGREDIENTS`. - :param dish: tuple - of (dish name, list of dish ingredients). - :return: tuple - containing (dish name, dish special ingredients). + Parameters: + dish (tuple): (dish name, list of dish ingredients). + + Returns: + tuple: Containing (dish name, dish special ingredients). Return the dish name followed by the `set` of ingredients that require a special note on the dish description. For the purposes of this exercise, all allergens or special ingredients that need to be tracked are in the @@ -81,12 +95,17 @@ def tag_special_ingredients(dish): def compile_ingredients(dishes): - """Create a master list of ingredients. + """Determine which `dishes` are designated `appetizers` and remove them. + + Parameters: + dishes (list): Group of dish names. + appetizers (list): Group of appetizer names. - :param dishes: list - of dish ingredient sets. - :return: set - of ingredients compiled from `dishes`. + Returns: + list: Group of dish names that do not appear on appetizer list. - This function should return a `set` of all ingredients from all listed dishes. + The function should return the list of dish names with appetizer names removed. + Either list could contain duplicates and may require de-duping. """ combined_ingredients = set() @@ -100,9 +119,12 @@ def compile_ingredients(dishes): def separate_appetizers(dishes, appetizers): """Determine which `dishes` are designated `appetizers` and remove them. - :param dishes: list - of dish names. - :param appetizers: list - of appetizer names. - :return: list - of dish names that do not appear on appetizer list. + Parameters: + dishes (list): Group of dish names. + appetizers (list): Group of appetizer names. + + Returns: + list: Group of dish names that do not appear on appetizer list. The function should return the list of dish names with appetizer names removed. Either list could contain duplicates and may require de-duping. @@ -114,13 +136,16 @@ def separate_appetizers(dishes, appetizers): def singleton_ingredients(dishes, intersection): """Determine which `dishes` have a singleton ingredient (an ingredient that only appears once across dishes). - :param dishes: list - of ingredient sets. - :param intersection: constant - can be one of `_INTERSECTION` constants imported from `sets_categories_data.py`. - :return: set - containing singleton ingredients. + Parameters: + dishes (list): Group of ingredient sets. + intersection (constant): Can be one of `_INTERSECTIONS` constants imported from `sets_categories_data.py`. + + Returns: + set: Containing singleton ingredients. Each dish is represented by a `set` of its ingredients. - Each `_INTERSECTION` is an `intersection` of all dishes in the category. `` can be any one of: + Each `_INTERSECTIONS` is an `intersection` of all dishes in the category. `` can be any one of: (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE). The function should return a `set` of ingredients that only appear in a single dish. diff --git a/exercises/concept/chaitanas-colossal-coaster/.meta/exemplar.py b/exercises/concept/chaitanas-colossal-coaster/.meta/exemplar.py index 24a8394964..5d40f939b8 100644 --- a/exercises/concept/chaitanas-colossal-coaster/.meta/exemplar.py +++ b/exercises/concept/chaitanas-colossal-coaster/.meta/exemplar.py @@ -4,11 +4,14 @@ def add_me_to_the_queue(express_queue, normal_queue, ticket_type, person_name): """Add a person to the 'express' or 'normal' queue depending on the ticket number. - :param express_queue: list - names in the Fast-track queue. - :param normal_queue: list - names in the normal queue. - :param ticket_type: int - type of ticket. 1 = express, 0 = normal. - :param person_name: str - name of person to add to a queue. - :return: list - the (updated) queue the name was added to. + Parameters: + express_queue (list): The names in the Fast-track queue. + normal_queue (list): The names in the normal queue. + ticket_type (int): Type of ticket. 1 = express, 0 = normal. + person_name (str): The name of person to add to a queue. + + Returns: + list: The (updated) queue the name was added to. """ result = express_queue if ticket_type == 1 else normal_queue @@ -19,9 +22,12 @@ def add_me_to_the_queue(express_queue, normal_queue, ticket_type, person_name): def find_my_friend(queue, friend_name): """Search the queue for a name and return their queue position (index). - :param queue: list - names in the queue. - :param friend_name: str - name of friend to find. - :return: int - index at which the friends name was found. + Parameters: + queue (list): The names in the queue. + friend_name (str): The name of friend to find. + + Returns: + int: The index at which the friends name was found. """ return queue.index(friend_name) @@ -30,10 +36,13 @@ def find_my_friend(queue, friend_name): def add_me_with_my_friends(queue, index, person_name): """Insert the late arrival's name at a specific index of the queue. - :param queue: list - names in the queue. - :param index: int - the index at which to add the new name. - :param person_name: str - the name to add. - :return: list - queue updated with new name. + Parameters: + queue (list): The names in the queue. + index (int): The index at which to add the new name. + person_name (str): The name to add. + + Returns: + list: The queue updated with new name. """ queue.insert(index, person_name) @@ -43,9 +52,12 @@ def add_me_with_my_friends(queue, index, person_name): def remove_the_mean_person(queue, person_name): """Remove the mean person from the queue by the provided name. - :param queue: list - names in the queue. - :param person_name: str - name of mean person. - :return: list - queue update with the mean persons name removed. + Parameters: + queue (list): The names in the queue. + person_name (str): The name of mean person. + + Returns: + list: The queue updated with the mean persons name removed. """ queue.remove(person_name) @@ -55,9 +67,12 @@ def remove_the_mean_person(queue, person_name): def how_many_namefellows(queue, person_name): """Count how many times the provided name appears in the queue. - :param queue: list - names in the queue. - :param person_name: str - name you wish to count or track. - :return: int - the number of times the name appears in the queue. + Parameters: + queue (list): The names in the queue. + person_name (str): The name you wish to count or track. + + Returns: + int: The number of times the name appears in the queue. """ return queue.count(person_name) @@ -66,8 +81,11 @@ def how_many_namefellows(queue, person_name): def remove_the_last_person(queue): """Remove the person in the last index from the queue and return their name. - :param queue: list - names in the queue. - :return: str - name that has been removed from the end of the queue. + Parameters: + queue (list): The names in the queue. + + Returns: + str: The name that has been removed from the end of the queue. """ return queue.pop() @@ -76,8 +94,11 @@ def remove_the_last_person(queue): def sorted_names(queue): """Sort the names in the queue in alphabetical order and return the result. - :param queue: list - names in the queue. - :return: list - copy of the queue in alphabetical order. + Parameters: + queue (list): The names in the queue. + + Returns: + list: A copy of the queue in alphabetical order. """ new_queue = queue[:] From 6202cef1eb4ea5dc1454100b8734b0d147291727 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Wed, 13 May 2026 14:45:12 -0700 Subject: [PATCH 21/47] Addressing typos outlined in https://forum.exercism.org/t/cater-waiter-docstrings-have-a-different-style-and-some-typos/50755/9. (#4167) --- exercises/concept/cater-waiter/.meta/exemplar.py | 12 ++++++------ exercises/concept/cater-waiter/sets.py | 14 ++++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/exercises/concept/cater-waiter/.meta/exemplar.py b/exercises/concept/cater-waiter/.meta/exemplar.py index b9c5d3e198..b00cbef26a 100644 --- a/exercises/concept/cater-waiter/.meta/exemplar.py +++ b/exercises/concept/cater-waiter/.meta/exemplar.py @@ -14,11 +14,11 @@ def clean_ingredients(dish_name, dish_ingredients): """Remove duplicates from `dish_ingredients`. Parameters: - dish_name (str): The name of the dish. + dish_name (str): The name of the dish. dish_ingredients (list): The ingredients for the dish. Returns: - tuple: Containing (dish_name, ingredient set). + tuple: Containing (dish name, ingredient set). This function should return a `tuple` with the name of the dish as the first item, followed by the de-duped `set` of ingredients as the second item. @@ -36,7 +36,7 @@ def check_drinks(drink_name, drink_ingredients): drink_ingredients (list): Ingredients in the drink. Returns: - str: drink_name appended with "Mocktail" or "Cocktail". + str: `drink_name` appended with "Mocktail" or "Cocktail". The function should return the name of the drink followed by "Mocktail" (non-alcoholic) and drink name followed by "Cocktail" (includes alcohol). @@ -57,7 +57,7 @@ def categorize_dish(dish_name, dish_ingredients): dish_ingredients (set): The ingredients for the dish. Returns: - str: TThe dish name appended with ": ". + str: The dish name appended with ": ". This function should return a string with the `dish name: ` (which meal category the dish belongs to). `` can be any one of (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE). @@ -134,11 +134,11 @@ def separate_appetizers(dishes, appetizers): def singleton_ingredients(dishes, intersection): - """Determine which `dishes` have a singleton ingredient (an ingredient that only appears once across dishes). + """Find singleton ingredients within the group of dishes (ingredients that only appear once across dishes). Parameters: dishes (list): Group of ingredient sets. - intersection (constant): Can be one of `_INTERSECTIONS` constants imported from `sets_categories_data.py`. + intersection (set): Can be one of `_INTERSECTIONS` constants imported from `sets_categories_data.py`. Returns: set: Containing singleton ingredients. diff --git a/exercises/concept/cater-waiter/sets.py b/exercises/concept/cater-waiter/sets.py index 44f1918019..a0c92f63b8 100644 --- a/exercises/concept/cater-waiter/sets.py +++ b/exercises/concept/cater-waiter/sets.py @@ -14,14 +14,15 @@ def clean_ingredients(dish_name, dish_ingredients): """Remove duplicates from `dish_ingredients`. Parameters: - dish_name (str): The name of the dish. + dish_name (str): The name of the dish. dish_ingredients (list): The ingredients for the dish. Returns: - tuple: Containing (dish_name, ingredient set). + tuple: Containing (dish name, ingredient set). This function should return a `tuple` with the name of the dish as the first item, followed by the de-duped `set` of ingredients as the second item. + """ pass @@ -35,10 +36,11 @@ def check_drinks(drink_name, drink_ingredients): drink_ingredients (list): Ingredients in the drink. Returns: - str: drink_name appended with "Mocktail" or "Cocktail". + str: `drink_name` appended with "Mocktail" or "Cocktail". The function should return the name of the drink followed by "Mocktail" (non-alcoholic) and drink name followed by "Cocktail" (includes alcohol). + """ pass @@ -52,7 +54,7 @@ def categorize_dish(dish_name, dish_ingredients): dish_ingredients (set): The ingredients for the dish. Returns: - str: TThe dish name appended with ": ". + str: The dish name appended with ": ". This function should return a string with the `dish name: ` (which meal category the dish belongs to). `` can be any one of (VEGAN, VEGETARIAN, PALEO, KETO, or OMNIVORE). @@ -112,11 +114,11 @@ def separate_appetizers(dishes, appetizers): def singleton_ingredients(dishes, intersection): - """Determine which `dishes` have a singleton ingredient (an ingredient that only appears once across dishes). + """Find singleton ingredients within the group of dishes (ingredients that only appear once across dishes). Parameters: dishes (list): Group of ingredient sets. - intersection (constant): Can be one of `_INTERSECTIONS` constants imported from `sets_categories_data.py`. + intersection (set): Can be one of `_INTERSECTIONS` constants imported from `sets_categories_data.py`. Returns: set: Containing singleton ingredients. From ff82bc0aa11f5ec972931325700ee68ef9236d88 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Wed, 13 May 2026 14:49:34 -0700 Subject: [PATCH 22/47] [Mecha Munch Management]: Typo & formatting fixes to documentation (#4166) * Typo and formatting fixes as outlined on https://forum.exercism.org/t/mecha-munch-management-typos/48413. * Deleted unneeded test file. --- concepts/dict-methods/about.md | 4 ++-- .../mecha-munch-management/.docs/hints.md | 8 ++++---- .../mecha-munch-management/.docs/instructions.md | 10 +++++----- .../mecha-munch-management/.docs/introduction.md | 2 +- .../mecha-munch-management/.meta/exemplar.py | 16 ++++++++-------- .../mecha-munch-management/dict_methods.py | 10 +++++----- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/concepts/dict-methods/about.md b/concepts/dict-methods/about.md index 6dcf9b4ae7..02f2dc21fa 100644 --- a/concepts/dict-methods/about.md +++ b/concepts/dict-methods/about.md @@ -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_. diff --git a/exercises/concept/mecha-munch-management/.docs/hints.md b/exercises/concept/mecha-munch-management/.docs/hints.md index 2d2f49e2cc..2c8b35b2cc 100644 --- a/exercises/concept/mecha-munch-management/.docs/hints.md +++ b/exercises/concept/mecha-munch-management/.docs/hints.md @@ -9,13 +9,13 @@ It's OK to be simple and direct with the functions you are writing. The dictionary section of the [official tutorial][dicts-docs] and the mapping type [official library reference][mapping-types-dict] are excellent places to look for more help with all these methods. -## 1. Add Item(s) to the Users Shopping Cart +## 1. Add Item(s) to the User's Shopping Cart - You will need to iterate through each item in `items_to_add`. - You can avoid a `KeyError` when a key is missing by using a `dict` [method][set-default] that takes a _default value_ as one of its arguments. - It is also possible to accomplish the same thing manually in the `loop` by using some checking and error handling, but the `dict` method is easier. -## 2. Read in Items Listed in the Users Notes App +## 2. Read in Items Listed in the User's Notes App - Remember, Python's got a method for _everything_. This one is a _classmethod_ that's an easy way to [populate a `dict`][fromkeys] with keys. - This `dict` method returns a _new dictionary_, populated with default values. If no value is given, the default value will become `None` @@ -25,13 +25,13 @@ The dictionary section of the [official tutorial][dicts-docs] and the mapping ty - Don't overthink this one! This can be solved in **one** `dict` method call. - The key word here is .... [_update_][update]. -## 4. Sort the Items in the User Cart +## 4. Sort the Items in the User's Cart - What method would you call to get an [iterable view of items][items] in the dictionary? - If you had a `list` or a `tuple`, what [`built-in`][builtins] function might you use to sort them? - The built-in function you want is the one that returns a _copy_, and doesn't mutate the original. -## 5. Send User Shopping Cart to Store for Fulfillment +## 5. Send the User's Shopping Cart to the Store for Fulfillment - Having a fresh, empty dictionary here as the `fulfillment_cart` might be handy for adding in items. - `Looping` through the members of the cart might be the most direct way of accessing things here. diff --git a/exercises/concept/mecha-munch-management/.docs/instructions.md b/exercises/concept/mecha-munch-management/.docs/instructions.md index e679db7974..fc3d0ff7cb 100644 --- a/exercises/concept/mecha-munch-management/.docs/instructions.md +++ b/exercises/concept/mecha-munch-management/.docs/instructions.md @@ -1,10 +1,10 @@ # Instructions -Mecha Munch™, a grocery shopping automation company has just hired you to work on their ordering app. +Mecha Munch™, a grocery shopping automation company, has just hired you to work on their ordering app. Your team is tasked with building an MVP (_[minimum viable product][mvp]_) that manages all the basic shopping cart activities, allowing users to add, remove, and sort their grocery orders. Thankfully, a different team is handling all the money and check-out functions! -## 1. Add Item(s) to the Users Shopping Cart +## 1. Add Item(s) to the User's Shopping Cart The MVP should allow the user to add items to their shopping cart. This could be a single item or multiple items at once. @@ -26,12 +26,12 @@ It should return a new/updated shopping cart dictionary for the user. {'Banana': 5, 'Apple': 2, 'Orange': 2, 'Blueberries': 1} ``` -## 2. Read in Items Listed in the Users Notes App +## 2. Read in Items Listed in the User's Notes App Uh-oh. Looks like the product team is engaging in [feature creep][feature creep]. They want to add extra functionality to the MVP. -The application now has to create a shopping cart by reading items off a users notes app. +The application now has to create a shopping cart by reading items off a user's notes app. Convenient for the users, but slightly more work for the team. Create the function `read_notes()` that can take any list-like iterable as an argument. @@ -97,7 +97,7 @@ Create the function `sort_entries()` that takes a shopping cart/dictionary ## 5. Send User Shopping Cart to Store for Fulfillment -The app needs to send a given users cart to the store for fulfillment. +The app needs to send a given user's cart to the store for fulfillment. However, the shoppers in the store need to know which store aisle the item can be found in and if the item needs refrigeration. So (_rather arbitrarily_) the "fulfillment cart" needs to be sorted in reverse alphabetical order with item quantities combined with location and refrigeration information. diff --git a/exercises/concept/mecha-munch-management/.docs/introduction.md b/exercises/concept/mecha-munch-management/.docs/introduction.md index b2938b8c21..6f63d8acd6 100644 --- a/exercises/concept/mecha-munch-management/.docs/introduction.md +++ b/exercises/concept/mecha-munch-management/.docs/introduction.md @@ -171,7 +171,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`. diff --git a/exercises/concept/mecha-munch-management/.meta/exemplar.py b/exercises/concept/mecha-munch-management/.meta/exemplar.py index 221a4c259e..e5074607cb 100644 --- a/exercises/concept/mecha-munch-management/.meta/exemplar.py +++ b/exercises/concept/mecha-munch-management/.meta/exemplar.py @@ -48,23 +48,23 @@ def update_recipes(ideas, recipe_updates): def sort_entries(cart): - """Sort a users shopping cart in alphabetically order. + """Sort a user's shopping cart in alphabetical order. - Parameters: - cart (dict): A users shopping cart dictionary. + Parameters: + cart (dict): A user's shopping cart dictionary. - Returns: - dict: A sers shopping cart sorted in alphabetical order. - """ + Returns: + dict: A user's shopping cart sorted in alphabetical order. + """ return dict(sorted(cart.items())) def send_to_store(cart, aisle_mapping): - """Combine users order to aisle and refrigeration information. + """Combine user's order to aisle and refrigeration information. Parameters: - cart (dict): The users shopping cart dictionary. + cart (dict): The user's shopping cart dictionary. aisle_mapping (dict): The aisle and refrigeration information dictionary. Returns: diff --git a/exercises/concept/mecha-munch-management/dict_methods.py b/exercises/concept/mecha-munch-management/dict_methods.py index f6804281d2..a2a535c4b7 100644 --- a/exercises/concept/mecha-munch-management/dict_methods.py +++ b/exercises/concept/mecha-munch-management/dict_methods.py @@ -43,23 +43,23 @@ def update_recipes(ideas, recipe_updates): def sort_entries(cart): - """Sort a users shopping cart in alphabetically order. + """Sort a user's shopping cart in alphabetical order. Parameters: - cart (dict): A users shopping cart dictionary. + cart (dict): A user's shopping cart dictionary. Returns: - dict: A sers shopping cart sorted in alphabetical order. + dict: A user's shopping cart sorted in alphabetical order. """ pass def send_to_store(cart, aisle_mapping): - """Combine users order to aisle and refrigeration information. + """Combine user's order to aisle and refrigeration information. Parameters: - cart (dict): The users shopping cart dictionary. + cart (dict): The user's shopping cart dictionary. aisle_mapping (dict): The aisle and refrigeration information dictionary. Returns: From 1f7b5951817a52343934f0949520810e8c569797 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 14 May 2026 21:06:21 -0700 Subject: [PATCH 23/47] Cleaned up typos and grammar in stubs, exemplar, and hints. (#4173) --- exercises/concept/locomotive-engineer/.docs/hints.md | 8 ++++---- .../concept/locomotive-engineer/.meta/exemplar.py | 12 ++++++------ .../locomotive-engineer/locomotive_engineer.py | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/exercises/concept/locomotive-engineer/.docs/hints.md b/exercises/concept/locomotive-engineer/.docs/hints.md index 208188c0ad..877f4decb9 100644 --- a/exercises/concept/locomotive-engineer/.docs/hints.md +++ b/exercises/concept/locomotive-engineer/.docs/hints.md @@ -2,7 +2,7 @@ ## General -- To extract multiple arguments in the function parameters so can you pack them with the `*args` operator for `list` or `tuples` or `**kwargs` for keyword-based arguments. +- A function can be defined to take multiple arguments packaged together by using the `*args` parameter for `list` & `tuple` arguments, or the `**kwargs` parameter for dictionary/keyword-based arguments. - To pack or unpack use the `*` or `**` operator. ## 1. Create a list of all wagons @@ -11,7 +11,7 @@ ## 2. Fix list of wagons -- Using unpacking with the `*` operator, lets you extract the first two elements of a `list` while keeping the rest intact. +- Using unpacking with the `*` operator allows you to extract the first two elements of a `list` while keeping the rest intact. - To add another `list` into an existing `list`, you can use the `*` operator to "spread" the `list`. ## 3. Add missing stops @@ -28,7 +28,7 @@ ## 5. Fix the wagon depot -- `zip(*iterators)` can use used to transpose a nested `list`. +- `zip(*iterators)` can be used to transpose a nested `list`. - To extract data from zipped iterators, you can use a for loop. -- you can also unpack zipped iterators using `*`. +- you can also unpack zipped iterators using `*`. `[*content] = zip(iterator_1, iterator_2)` will unzip the `tuple` produced by `zip()` into a `list`. diff --git a/exercises/concept/locomotive-engineer/.meta/exemplar.py b/exercises/concept/locomotive-engineer/.meta/exemplar.py index a8a593b855..f7a3a40e6c 100644 --- a/exercises/concept/locomotive-engineer/.meta/exemplar.py +++ b/exercises/concept/locomotive-engineer/.meta/exemplar.py @@ -5,10 +5,10 @@ def get_list_of_wagons(*args): """Return a list of wagons, given an arbitrary amount of wagon numbers. Parameters: - *args: An arbitrary number of wagon numbers, unpacked. + An arbitrary number of wagon numbers, unpacked. Returns: - list: A list of wagon numbers, assembled from *args.. + list: A list of wagon numbers. """ return list(args) @@ -19,7 +19,7 @@ def fix_list_of_wagons(each_wagons_id, missing_wagons): Parameters: each_wagons_id (list[int]): The list of wagons. - missing_wagons (list[int]) The list of missing wagons. + missing_wagons (list[int]): The list of missing wagons. Returns: list[int]: The corrected list of wagons. @@ -35,7 +35,7 @@ def add_missing_stops(route, **kwargs): Parameters: route (dict): The dict of routing information. - **kwargs: arbitrary number of stops. + (dict): An arbitrary number of stops. Returns: dict: The updated route dictionary. @@ -62,10 +62,10 @@ def fix_wagon_depot(wagons_rows): """Fix the list of rows of wagons. Parameters: - wagons_rows (list[tuple]) The list of rows of wagons. + wagons_rows (list[list[tuple]]): The list of rows of wagons. Returns: - list[tuple]: the list of rows of wagons. + list[list[tuple]]: the list of rows of wagons. """ [*row_one], [*row_two], [*row_three] = zip(*wagons_rows) diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer.py b/exercises/concept/locomotive-engineer/locomotive_engineer.py index 3a28255fde..f57209b7a8 100644 --- a/exercises/concept/locomotive-engineer/locomotive_engineer.py +++ b/exercises/concept/locomotive-engineer/locomotive_engineer.py @@ -5,7 +5,7 @@ def get_list_of_wagons(): """Return a list of wagons, given an arbitrary amount of wagon numbers. Parameters: - An arbitrary number of wagon numbers, unpacked. + An arbitrary number of wagon numbers, unpacked. Returns: list: A list of wagon numbers. @@ -18,7 +18,7 @@ def fix_list_of_wagons(each_wagons_id, missing_wagons): Parameters: each_wagons_id (list[int]): The list of wagons. - missing_wagons (list[int]) The list of missing wagons. + missing_wagons (list[int]): The list of missing wagons. Returns: list[int]: The corrected list of wagons. @@ -31,7 +31,7 @@ def add_missing_stops(route): Parameters: route (dict): The dict of routing information. - (dict): arbitrary number of stops. + (dict): An arbitrary number of stops. Returns: dict: The updated route dictionary. @@ -56,9 +56,9 @@ def fix_wagon_depot(wagons_rows): """Fix the list of rows of wagons. Parameters: - wagons_rows (list[tuple]) The list of rows of wagons. + wagons_rows (list[list[tuple]]): The list of rows of wagons. Returns: - list[tuple]: the list of rows of wagons. + list[list[tuple]]: the list of rows of wagons. """ pass From 22bb9a3d90d925ef5f8332dd582ac9d63f64c696 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 14 May 2026 21:19:18 -0700 Subject: [PATCH 24/47] [Lists and Comparisons]: Typo & Formatting Corrections for `black-jack` and `card-games` (#4171) * Typo and formatting corrections for lists and comparisons and corresponding exercises. * Adjusted space for elipsis used in terminal code examples. * re-adjusted indentation for code examples using ... --- concepts/comparisons/about.md | 8 ++--- concepts/comparisons/introduction.md | 2 +- concepts/lists/about.md | 36 +++++++++---------- .../concept/black-jack/.meta/exemplar.py | 4 +-- exercises/concept/black-jack/black_jack.py | 4 +-- .../concept/card-games/.meta/exemplar.py | 6 ++-- exercises/concept/card-games/lists.py | 6 ++-- 7 files changed, 33 insertions(+), 33 deletions(-) diff --git a/concepts/comparisons/about.md b/concepts/comparisons/about.md index 1d2c677d22..9aa681ff9f 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 e597063c62..40c40ea8a1 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/lists/about.md b/concepts/lists/about.md index f7d4054eef..51c44060d5 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/exercises/concept/black-jack/.meta/exemplar.py b/exercises/concept/black-jack/.meta/exemplar.py index ae8df079c1..e30df63316 100644 --- a/exercises/concept/black-jack/.meta/exemplar.py +++ b/exercises/concept/black-jack/.meta/exemplar.py @@ -12,7 +12,7 @@ def value_of_card(card): card (str): The given card. Returns: - int: The value of a given card. See below for values. + int: The value of a given card. See below for values. 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 2. 'A' (ace card) = 1 @@ -43,7 +43,7 @@ def higher_card(card_one, card_two): 3. '2' - '10' = numerical value. Returns: - str or tuple: The resulting Tuple contains both cards if they are of equal value. + str or tuple: The resulting tuple contains both cards if they are of equal value. """ card_one_value = value_of_card(card_one) diff --git a/exercises/concept/black-jack/black_jack.py b/exercises/concept/black-jack/black_jack.py index fd5cedb20a..25cd61f907 100644 --- a/exercises/concept/black-jack/black_jack.py +++ b/exercises/concept/black-jack/black_jack.py @@ -12,7 +12,7 @@ def value_of_card(card): card (str): The given card. Returns: - int: The value of a given card. See below for values. + int: The value of a given card. See below for values. 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 2. 'A' (ace card) = 1 @@ -34,7 +34,7 @@ def higher_card(card_one, card_two): 3. '2' - '10' = numerical value. Returns: - str or tuple: The resulting Tuple contains both cards if they are of equal value. + str or tuple: The resulting tuple contains both cards if they are of equal value. """ pass diff --git a/exercises/concept/card-games/.meta/exemplar.py b/exercises/concept/card-games/.meta/exemplar.py index 35b1b5377b..bb108e9ecc 100644 --- a/exercises/concept/card-games/.meta/exemplar.py +++ b/exercises/concept/card-games/.meta/exemplar.py @@ -8,7 +8,7 @@ def get_rounds(number): """Create a list containing the current and next two round numbers. Parameters: - number (int): The current round number. + number (int): The current round number. Returns: list: The current round number and the two that follow. @@ -21,7 +21,7 @@ def concatenate_rounds(rounds_1, rounds_2): """Concatenate two lists of round numbers. Parameters: - rounds_1 (list): The first rounds played. + rounds_1 (list): The first rounds played. rounds_2 (list): The second group of rounds played. Returns: @@ -35,7 +35,7 @@ def list_contains_round(rounds, number): """Check if the list of rounds contains the specified number. Parameters: - rounds (list): The rounds played. + rounds (list): The rounds played. number (int): The round number. Returns: diff --git a/exercises/concept/card-games/lists.py b/exercises/concept/card-games/lists.py index f5a4b5fc44..56c85a3366 100644 --- a/exercises/concept/card-games/lists.py +++ b/exercises/concept/card-games/lists.py @@ -8,7 +8,7 @@ def get_rounds(number): """Create a list containing the current and next two round numbers. Parameters: - number (int): The current round number. + number (int): The current round number. Returns: list: The current round number and the two that follow. @@ -21,7 +21,7 @@ def concatenate_rounds(rounds_1, rounds_2): """Concatenate two lists of round numbers. Parameters: - rounds_1 (list): The first rounds played. + rounds_1 (list): The first rounds played. rounds_2 (list): The second group of rounds played. Returns: @@ -35,7 +35,7 @@ def list_contains_round(rounds, number): """Check if the list of rounds contains the specified number. Parameters: - rounds (list): The rounds played. + rounds (list): The rounds played. number (int): The round number. Returns: From 977c3de0c31f420fbfdccf84cb149c5a022c3bb4 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 14 May 2026 21:24:21 -0700 Subject: [PATCH 25/47] [Little Sisters Vocab and Essay]: Typo & Formatting Fixes for String Related Concept Exercises. (#4172) * Typo and formatting fixes for string related concept exercises. * Rewording formatting sentence for clarity and grammar. * Removed errant comma from introduction. --- concepts/string-methods/about.md | 11 ++++++----- concepts/strings/about.md | 7 ++++--- concepts/strings/introduction.md | 5 +++-- .../concept/little-sisters-essay/.meta/exemplar.py | 2 +- .../concept/little-sisters-essay/string_methods.py | 2 +- exercises/concept/little-sisters-vocab/.docs/hints.md | 5 ++--- .../concept/little-sisters-vocab/.meta/exemplar.py | 6 ++---- exercises/concept/little-sisters-vocab/strings.py | 6 ++---- 8 files changed, 21 insertions(+), 23 deletions(-) diff --git a/concepts/string-methods/about.md b/concepts/string-methods/about.md index 4f9dcde5dd..6c9c843b97 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 fe49f6208a..19920adad3 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 e213b0fbea..22770fde4c 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()`, and offer multiple types of formatting, interpolation, and templating. +Strings can be concatenated with ` + ` or `.join()` and split via `.split()`. +They also offer multiple additional formatting, assembly, and templating options. Being immutable, a `str` object's value in memory doesn't change; methods that appear to modify a string return a new copy or instance of `str`. 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. [common sequence operations]: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations diff --git a/exercises/concept/little-sisters-essay/.meta/exemplar.py b/exercises/concept/little-sisters-essay/.meta/exemplar.py index f62fff90c3..fa70976a51 100644 --- a/exercises/concept/little-sisters-essay/.meta/exemplar.py +++ b/exercises/concept/little-sisters-essay/.meta/exemplar.py @@ -31,7 +31,7 @@ def clean_up_spacing(sentence): """Trim any leading or trailing whitespace from the sentence. Parameters: - sentence (str): A sentence to clean of leading and trailing space characters. + sentence (str): A sentence to clean of leading and trailing space characters. Returns: str: A sentence that has been cleaned of leading and trailing space characters. diff --git a/exercises/concept/little-sisters-essay/string_methods.py b/exercises/concept/little-sisters-essay/string_methods.py index 080dc8ec95..3c1797741c 100644 --- a/exercises/concept/little-sisters-essay/string_methods.py +++ b/exercises/concept/little-sisters-essay/string_methods.py @@ -31,7 +31,7 @@ def clean_up_spacing(sentence): """Trim any leading or trailing whitespace from the sentence. Parameters: - sentence (str): A sentence to clean of leading and trailing space characters. + sentence (str): A sentence to clean of leading and trailing space characters. Returns: str: A sentence that has been cleaned of leading and trailing space characters. diff --git a/exercises/concept/little-sisters-vocab/.docs/hints.md b/exercises/concept/little-sisters-vocab/.docs/hints.md index 0be143a7f6..eabd05e473 100644 --- a/exercises/concept/little-sisters-vocab/.docs/hints.md +++ b/exercises/concept/little-sisters-vocab/.docs/hints.md @@ -19,12 +19,11 @@ There's four activities in the assignment, each with a set of text or words to w - Remember that delimiter strings go between elements and "glue" them together into a single string. Delimiters are inserted _without_ space, although you can include space characters within them. - Like [`str.split()`][str-split], `str.join()` can process an arbitrary-length string, made up of any unicode code points. _Unlike_ `str.split()`, it can also process arbitrary-length iterables like `list`, `tuple`, and `set`. - ## 3. Remove a suffix from a word - Strings can be indexed or sliced from either the left (starting at 0) or the right (starting at -1). -- If you want the last code point of an arbitrary-length string, you can use [-1]. -- The last three letters in a string can be "sliced off" using a negative index. e.g. 'beautiful'[:-3] == 'beauti' +- If you want the last code point of an arbitrary-length string, you can use `[-1]`. +- The last three letters in a string can be "sliced off" using a negative index. e.g. `beautiful'[:-3] == 'beauti` ## 4. Extract and transform a word diff --git a/exercises/concept/little-sisters-vocab/.meta/exemplar.py b/exercises/concept/little-sisters-vocab/.meta/exemplar.py index c17350fd3a..ed024abaf0 100644 --- a/exercises/concept/little-sisters-vocab/.meta/exemplar.py +++ b/exercises/concept/little-sisters-vocab/.meta/exemplar.py @@ -21,12 +21,10 @@ def make_word_groups(vocab_words): vocab_words (list[str]): Vocabulary words with prefix at first index. Returns: - str: Prefix followed by vocabulary words with - prefix applied. + str: Prefix followed by vocabulary words with prefix applied. This function takes a `vocab_words` list of strings and returns a string - with the prefix and the words with prefix applied, separated - by ' :: '. + with the prefix and the words with prefix applied, separated by ' :: '. Examples: >>> list('en', 'close', 'joy', 'lighten') diff --git a/exercises/concept/little-sisters-vocab/strings.py b/exercises/concept/little-sisters-vocab/strings.py index e7c1a8de88..de0e37edf3 100644 --- a/exercises/concept/little-sisters-vocab/strings.py +++ b/exercises/concept/little-sisters-vocab/strings.py @@ -21,12 +21,10 @@ def make_word_groups(vocab_words): vocab_words (list[str]): Vocabulary words with prefix at first index. Returns: - str: Prefix followed by vocabulary words with - prefix applied. + str: Prefix followed by vocabulary words with prefix applied. This function takes a `vocab_words` list of strings and returns a string - with the prefix and the words with prefix applied, separated - by ' :: '. + with the prefix and the words with prefix applied, separated by ' :: '. Examples: >>> list('en', 'close', 'joy', 'lighten') From eb0e9287454bbdddff3bc5c342a7fb81a3c86f74 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 14 May 2026 22:03:25 -0700 Subject: [PATCH 26/47] Corrected typos and grammar mistakes for docs and stubs. (#4174) --- concepts/loops/about.md | 36 +++++++++---------- .../concept/making-the-grade/.docs/hints.md | 10 +++--- .../making-the-grade/.meta/exemplar.py | 8 ++--- exercises/concept/making-the-grade/loops.py | 8 ++--- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/concepts/loops/about.md b/concepts/loops/about.md index 0f39e733d0..e3322af0e3 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/exercises/concept/making-the-grade/.docs/hints.md b/exercises/concept/making-the-grade/.docs/hints.md index 3e8deff958..eee64b21ac 100644 --- a/exercises/concept/making-the-grade/.docs/hints.md +++ b/exercises/concept/making-the-grade/.docs/hints.md @@ -2,15 +2,15 @@ ## General -- [`while`][while-loops] loops are used for _indefinite_ (uncounted) iteration -- [`for`][for-loops] loops are used for _definite_, (counted) iteration. +- [`while`][while-loops] loops are used for _indefinite_ (uncounted) iteration. +- [`for`][for-loops] loops are used for _definite_ (counted) iteration. - The keywords [`break` and `continue`][control flow] help customize loop behavior. -- [`range(, stop, )`][range] can be used to generate a sequence for a loop counter. +- [`range(, , )`][range] can be used to generate a sequence for a loop counter. - The built-in [`enumerate()`][enumerate] will return (``, ``) pairs to iterate over. Also being familiar with the following can help with completing the tasks: -- [`lists`][list]: indexing, nested lists, [`.append`][append and pop], [`.pop()`][append and pop]. +- [`lists`][list]: indexing, nested lists, [`.append()`][append and pop], [`.pop()`][append and pop]. - [`str`][str]: `str()` constructor, using the `+` to concatenate strings, optionally, [`f-strings`][f-strings]. ## 1. Rounding Scores @@ -22,7 +22,7 @@ Also being familiar with the following can help with completing the tasks: ## 2. Non-Passing Students - There's no need to declare `loop` counters or `index` counters when iterating through an object using a `for` loop. -- A results counter does need to be set up and _incremented_ -- you'll want to `return` the count of non-passing students when the loop terminates. +- A results counter does need to be set up and _incremented_ — you'll want to `return` the count of non-passing students when the loop terminates. ## 3. The "Best" diff --git a/exercises/concept/making-the-grade/.meta/exemplar.py b/exercises/concept/making-the-grade/.meta/exemplar.py index c490dbf1c6..2a8f52846b 100644 --- a/exercises/concept/making-the-grade/.meta/exemplar.py +++ b/exercises/concept/making-the-grade/.meta/exemplar.py @@ -5,7 +5,7 @@ def round_scores(student_scores): """Round all provided student scores. Parameters: - student_scores (list[float|int]): Student exam scores. + student_scores (list[float]): Student exam scores. Returns: list[int]: Student scores *rounded* to the nearest integer value. @@ -42,7 +42,7 @@ def above_threshold(student_scores, threshold): threshold (int): The threshold to cross to be the "best" score. Returns: - list[int]: Integer scores that are at or above the "best" threshold. + list[int]: Integer scores that are at or above the "best" threshold. """ above = [] @@ -57,7 +57,7 @@ def letter_grades(highest): """Create a list of grade thresholds based on the provided highest grade. Parameters: - highest: int - value of the highest exam score. + highest (int): The value of the highest exam score. Returns: list[int]: Lower threshold scores for each D-A letter grade interval. @@ -85,7 +85,7 @@ def student_ranking(student_scores, student_names): student_names (list[str]): Student names by exam score in descending order. Returns: - list[str]: Strings in format [". : "]. + list[str]: Strings in format [". : "]. """ results = [] diff --git a/exercises/concept/making-the-grade/loops.py b/exercises/concept/making-the-grade/loops.py index 21da8c77e8..5bc0c72722 100644 --- a/exercises/concept/making-the-grade/loops.py +++ b/exercises/concept/making-the-grade/loops.py @@ -5,7 +5,7 @@ def round_scores(student_scores): """Round all provided student scores. Parameters: - student_scores (list[float|int]): Student exam scores. + student_scores (list[float]): Student exam scores. Returns: list[int]: Student scores *rounded* to the nearest integer value. @@ -35,7 +35,7 @@ def above_threshold(student_scores, threshold): threshold (int): The threshold to cross to be the "best" score. Returns: - list[int]: Integer scores that are at or above the "best" threshold. + list[int]: Integer scores that are at or above the "best" threshold. """ pass @@ -45,7 +45,7 @@ def letter_grades(highest): """Create a list of grade thresholds based on the provided highest grade. Parameters: - highest: int - value of the highest exam score. + highest (int): The value of the highest exam score. Returns: list[int]: Lower threshold scores for each D-A letter grade interval. @@ -69,7 +69,7 @@ def student_ranking(student_scores, student_names): student_names (list[str]): Student names by exam score in descending order. Returns: - list[str]: Strings in format [". : "]. + list[str]: Strings in format [". : "]. """ pass From 0b27e8e04ab3d563bd387975f0de22f5b98688e1 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 15 May 2026 10:13:38 -0700 Subject: [PATCH 27/47] Corrected typos and formatting errors in plane tickets exercise. (#4180) --- exercises/concept/plane-tickets/.docs/instructions.md | 4 ++-- exercises/concept/plane-tickets/.meta/exemplar.py | 3 ++- exercises/concept/plane-tickets/generators.py | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/exercises/concept/plane-tickets/.docs/instructions.md b/exercises/concept/plane-tickets/.docs/instructions.md index edd92680b1..7584c53f38 100644 --- a/exercises/concept/plane-tickets/.docs/instructions.md +++ b/exercises/concept/plane-tickets/.docs/instructions.md @@ -75,10 +75,10 @@ The function should then return a _dictionary_ of `passenger` as _key_, and `sea Conda Airlines would like to have a unique code for each ticket. Since they are a big airline, they have a lot of flights. This means that there are multiple flights with the same seat number. -They want you to create a system that creates a unique ticket that is _12_ characters long string code for identification. +They want you to create a system that formulates a unique ticket that is a _12_ character long string. This code begins with the `assigned_seat` followed by the `flight_id`. -The rest of the code is appended by `0s`. +The rest of the code is filled with `0s`. Implement a function `generate_codes(, )` that accepts a `list` of `seat_numbers` and a `string` with the flight number. The function should then return a `generator` that yields a `ticket_number`. diff --git a/exercises/concept/plane-tickets/.meta/exemplar.py b/exercises/concept/plane-tickets/.meta/exemplar.py index 29d6c1561d..498a54e6cb 100644 --- a/exercises/concept/plane-tickets/.meta/exemplar.py +++ b/exercises/concept/plane-tickets/.meta/exemplar.py @@ -39,7 +39,7 @@ def generate_seats(number): There is no row 13, and each row has 4 seats. Seats should be sorted from low to high. - For exampl: 3C, 3D, 4A, 4B + For example: 3C, 3D, 4A, 4B """ @@ -72,6 +72,7 @@ def assign_seats(passengers): output[passenger] = seat_number return output + def generate_codes(seat_numbers, flight_id): """Generate codes for a ticket. diff --git a/exercises/concept/plane-tickets/generators.py b/exercises/concept/plane-tickets/generators.py index db7b327055..c55dc9434d 100644 --- a/exercises/concept/plane-tickets/generators.py +++ b/exercises/concept/plane-tickets/generators.py @@ -34,12 +34,13 @@ def generate_seats(number): There is no row 13, and each row has 4 seats. Seats should be sorted from low to high. - For exampl: 3C, 3D, 4A, 4B + For example: 3C, 3D, 4A, 4B """ pass + def assign_seats(passengers): """Assign seats to passengers. @@ -54,6 +55,7 @@ def assign_seats(passengers): pass + def generate_codes(seat_numbers, flight_id): """Generate codes for a ticket. From c99ae1ae0d5e418444f5f071bba71ab7bac3b3c5 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 15 May 2026 12:46:28 -0700 Subject: [PATCH 28/47] [Numbers, Bools, and Currency Exchange]: Typos & Formatting Corrections (#4178) * Typos and formatting error corrections for numbers, bools, and currency exchange exercise. * Fixed spacing on line 51. --- concepts/bools/about.md | 28 +++++++++---------- concepts/numbers/about.md | 2 +- .../currency-exchange/.meta/exemplar.py | 2 +- .../concept/currency-exchange/exchange.py | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/concepts/bools/about.md b/concepts/bools/about.md index 0b8354750a..7015fdfafa 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/numbers/about.md b/concepts/numbers/about.md index 1155bcf7a5..3fa63c140d 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/exercises/concept/currency-exchange/.meta/exemplar.py b/exercises/concept/currency-exchange/.meta/exemplar.py index c7e497bbe1..d835bce65c 100644 --- a/exercises/concept/currency-exchange/.meta/exemplar.py +++ b/exercises/concept/currency-exchange/.meta/exemplar.py @@ -40,7 +40,7 @@ def get_change(budget, exchanging_value): float: The amount left of your starting currency after the exchange Examples: - .>>> get_change(127.5, 120.0) + >>> get_change(127.5, 120.0) 7.5 >>> get_change(300.75, 150.25) diff --git a/exercises/concept/currency-exchange/exchange.py b/exercises/concept/currency-exchange/exchange.py index 8d4cce6cf1..c135c33beb 100644 --- a/exercises/concept/currency-exchange/exchange.py +++ b/exercises/concept/currency-exchange/exchange.py @@ -42,7 +42,7 @@ def get_change(budget, exchanging_value): float: The amount left of your starting currency after the exchange Examples: - .>>> get_change(127.5, 120.0) + >>> get_change(127.5, 120.0) 7.5 >>> get_change(300.75, 150.25) From ace990cef7b3346300f6a306a60f2a517af3c63f Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 15 May 2026 12:46:56 -0700 Subject: [PATCH 29/47] Fixes for typos and formatting errors in classes and classes concept exercise. (#4176) --- concepts/classes/about.md | 9 ++++----- .../concept/ellens-alien-game/.docs/hints.md | 4 ++-- .../ellens-alien-game/.docs/instructions.md | 6 +++--- .../ellens-alien-game/.docs/introduction.md | 4 ++-- .../ellens-alien-game/.meta/exemplar.py | 18 +++++++++--------- exercises/concept/ellens-alien-game/classes.py | 6 +++--- 6 files changed, 23 insertions(+), 24 deletions(-) diff --git a/concepts/classes/about.md b/concepts/classes/about.md index 11b0364354..9b6a8a0dfb 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/exercises/concept/ellens-alien-game/.docs/hints.md b/exercises/concept/ellens-alien-game/.docs/hints.md index e3045b6016..54df99eee4 100644 --- a/exercises/concept/ellens-alien-game/.docs/hints.md +++ b/exercises/concept/ellens-alien-game/.docs/hints.md @@ -20,7 +20,7 @@ ## 4. The `teleport` Method - Remember that `object methods` are always passed `self` as the first parameter. -- Instance attributes can be updated from a method by using `self.` = ``. +- Instance attributes can be updated from a method by using `self. = `. ## 5. The `collision_detection` Method @@ -39,4 +39,4 @@ - A `tuple` would be a _single_ parameter. - The Alien constructor takes _2 parameters_. - Unpacking what is _inside_ the tuple would yield two parameters. -- The standalone function is outside of the `class` +- The standalone function is outside of the `class`. diff --git a/exercises/concept/ellens-alien-game/.docs/instructions.md b/exercises/concept/ellens-alien-game/.docs/instructions.md index 81ec62dbe5..1093895f25 100644 --- a/exercises/concept/ellens-alien-game/.docs/instructions.md +++ b/exercises/concept/ellens-alien-game/.docs/instructions.md @@ -31,9 +31,9 @@ It is up to you if `hit()` takes healths points _to_ or _below_ zero. ```python >>> alien = Alien(0, 0) ->>> alien.health # Initialized health value. +>>> alien.health 3 # Decrements health by 1 point. @@ -103,8 +103,8 @@ For example: 2 >>> alien_one.total_aliens_created 2 ->>> Alien.total_aliens_created # Accessing the variable from the class directly +>>> Alien.total_aliens_created 2 ``` @@ -120,7 +120,7 @@ For example: >>> aliens = new_aliens_collection(alien_start_positions) ... >>> for alien in aliens: - print(alien.x_coordinate, alien.y_coordinate) +... print(alien.x_coordinate, alien.y_coordinate) (4, 7) (-1, 0) ``` diff --git a/exercises/concept/ellens-alien-game/.docs/introduction.md b/exercises/concept/ellens-alien-game/.docs/introduction.md index ea1fc940fa..fead4bf640 100644 --- a/exercises/concept/ellens-alien-game/.docs/introduction.md +++ b/exercises/concept/ellens-alien-game/.docs/introduction.md @@ -182,7 +182,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)) @@ -209,7 +209,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): diff --git a/exercises/concept/ellens-alien-game/.meta/exemplar.py b/exercises/concept/ellens-alien-game/.meta/exemplar.py index d4441f3b74..1aeae69134 100644 --- a/exercises/concept/ellens-alien-game/.meta/exemplar.py +++ b/exercises/concept/ellens-alien-game/.meta/exemplar.py @@ -6,9 +6,9 @@ class Alien: Attributes: (class) total_aliens_created (int): Total number of Alien instances. - x_coordinate (int): Position on the x-axis. + x_coordinate (int): Position on the x-axis. y_coordinate (int): Position on the y-axis. - health (int): Number of health points. + health (int): Number of health points. Methods: hit(): Decrement Alien health by one point. @@ -29,9 +29,9 @@ def __init__(self, x_coordinate, y_coordinate): health (int): Number of health points. Attributes: - x_coordinate (int): Position on the x-axis. + x_coordinate (int): Position on the x-axis. y_coordinate (int): Position on the y-axis. - health (int): Number of health points. Defaults to 3. + health (int): Number of health points. Defaults to 3. Returns: Alien (Alien Object): New Alien. @@ -59,7 +59,7 @@ def is_alive(self): """Return if the Alien is alive. Returns: - bool: Is the Alien Alive? + bool: Is the Alien Alive? """ return self.health > 0 @@ -94,11 +94,11 @@ def collision_detection(self, other): def new_aliens_collection(positions): """Create a list of Alien instances from a list of coordinate tuples. - Parameters: - positions (list[tuple]): List of (x, y) coordinates in tuples.. + Parameters: + positions (list[tuple]): List of (x, y) coordinates in tuples. - Returns: - list[object]: List of Alien objects. + Returns: + list[object]: List of Alien objects. """ return [Alien(position[0], position[1]) for position in positions] diff --git a/exercises/concept/ellens-alien-game/classes.py b/exercises/concept/ellens-alien-game/classes.py index f3fbe84383..f64d1055e3 100644 --- a/exercises/concept/ellens-alien-game/classes.py +++ b/exercises/concept/ellens-alien-game/classes.py @@ -6,9 +6,9 @@ class Alien: Attributes: (class) total_aliens_created (int): Total number of Alien instances. - x_coordinate (int): Position on the x-axis. + x_coordinate (int): Position on the x-axis. y_coordinate (int): Position on the y-axis. - health (int): Number of health points. + health (int): Number of health points. Methods: hit(): Decrement Alien health by one point. @@ -21,5 +21,5 @@ class Alien: pass -#TODO (Student): Create the new_aliens_collection() function below to call your Alien class with a list of coordinates +#TODO (Student): Create the new_aliens_collection() function below to call your Alien class with a list of coordinates From fa51110dc800560f581ee7d7382848d6d2410473 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 15 May 2026 13:34:37 -0700 Subject: [PATCH 30/47] [Dicts Concept]: Fixed Typos & Grammar Errors in Docs (#4177) * Fixed typos and grammar errors in dicts concept. * Applied corrections from code review. --- concepts/dicts/about.md | 99 +++++++++++++++++----------------- concepts/dicts/introduction.md | 6 +-- 2 files changed, 54 insertions(+), 51 deletions(-) diff --git a/concepts/dicts/about.md b/concepts/dicts/about.md index 1f83fe6e30..a525f6248c 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 5c8a772480..676bf11066 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. From 1c9b44cb3a75e394659315c4ec13ad3f098324b5 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 15 May 2026 13:35:24 -0700 Subject: [PATCH 31/47] [Tuples Concept and Exercise]: Typo, grammar, & formatting fixes for Tuples Concept & Exercise. (#4175) * Typo, grammar, and formatting fixes for tuples concept and exercise. * Indentation and other corrections from code review. --- concepts/tuples/about.md | 48 ++++++++----------- .../tisbury-treasure-hunt/.docs/hints.md | 4 +- .../tisbury-treasure-hunt/.meta/exemplar.py | 2 +- .../concept/tisbury-treasure-hunt/tuples.py | 2 +- 4 files changed, 24 insertions(+), 32 deletions(-) diff --git a/concepts/tuples/about.md b/concepts/tuples/about.md index ea8179e2d8..e61cd39ead 100644 --- a/concepts/tuples/about.md +++ b/concepts/tuples/about.md @@ -57,8 +57,7 @@ To include both keys and values in a tuple made from a dictionary, use `.i which will return an iterator of (`key`, `value`) `tuples`. ```python -source_data = {"fish": "gold", - "monkey": "brown"} +>>> source_data = {"fish": "gold", "monkey": "brown"} >>> multiple_elements_dict_1 = tuple(source_data) ('fish', 'monkey') @@ -81,7 +80,7 @@ Because the `tuple()` constructor only takes _iterables_ (or nothing) as argumen ``` Note that generally parentheses are **not** required to create a `tuple` literal - only commas. -However, using `(, )` is considered more readable in most circumstances. +However, using `(, )` is considered more readable in most circumstances. Parentheses are also required in cases of ambiguity, such as an empty or one-item tuple or where a function takes a tuple as an argument. ```python @@ -102,7 +101,7 @@ Other data structures can be included as `tuple` elements, including other `tupl (["fish", "gold", "monkey", "brown", "parrot", "grey"], ("fish", "mammal", "bird")) ``` -Tuples can be concatenated using plus `+` operator, which unpacks each `tuple` creating a new, combined `tuple`. +Tuples can be concatenated using plus `+` operator, which unpacks each `tuple`, creating a new, combined `tuple`. ```python >>> new_via_concatenate = ("George", 5) + ("cat", "Tabby") @@ -122,8 +121,7 @@ Indexes can be from **`left`** --> **`right`** (_starting at zero_) or **`right` Tuples can also be copied in whole or in part via _slice notation_ or using `.copy()`. ```python - ->>> student_info = ("Alyssa", "grade 3", "female", 8 ) +>>> student_info = ("Alyssa", "grade 3", "female", 8) #name is at index 0 or index -4 >>> student_name = student_info[0] @@ -146,10 +144,9 @@ Elements inside tuples can be _iterated over_ in a loop using `for item in )` can be used. ```python ->>> student_info = ("Alyssa", "grade 3", "female", 8 ) +>>> student_info = ("Alyssa", "grade 3", "female", 8) >>> for item in student_info: ... print(item) - ... Alyssa grade 3 @@ -157,8 +154,7 @@ female 8 >>> for index, item in enumerate(student_info): -... print("Index is: " + str(index) + ", value is: " + str(item) +".") - +... print("Index is: " + str(index) + ", value is: " + str(item) + ".") ... Index is: 0, value is: Alyssa. Index is: 1, value is: grade 3. @@ -172,9 +168,7 @@ Index is: 3, value is: 8. Tuples are often used as _records_ containing data that is _organizationally_ or _conceptually_ homogeneous and treated as a single unit of information -- even if individual elements are of _heterogeneous_ data types. ```python - ->>> student_info = ("Alyssa", "grade 3", "female", 8 ) - +>>> student_info = ("Alyssa", "grade 3", "female", 8) ``` Tuples are also used when homogeneous immutable sequences of data are needed for [`hashability`][hashability], storage in a `set`, or creation of keys in a dictionary. @@ -183,21 +177,20 @@ Note that while `tuples` are in most cases _immutable_, because they can contain Using a mutable data type within a `tuple` will make the enclosing `tuple` **un-hashable**. ```python - >>> cmyk_color_map = { - (.69, .3, .48, .1) : ("Teal 700", (59, 178, 146), 0x3BB292), - (0, .5, 1, 0) : ("Pantone 151", (247, 127, 1), 0xF77F01), - (.37, .89, 0, .44) : ("Pantone 267", (89, 16, 142), 0x59108E), - (0, 1, .46, .45) : ("Pantone 228", (140, 0, 76), 0x8C004C) - } - ->>>> unique_rgb_colors = { - (59, 178, 146), - (247, 127, 1), - (89, 16, 142), - (140, 0, 76), - (76, 0, 140) - } + (.69, .3, .48, .1) : ("Teal 700", (59, 178, 146), 0x3BB292), + (0, .5, 1, 0) : ("Pantone 151", (247, 127, 1), 0xF77F01), + (.37, .89, 0, .44) : ("Pantone 267", (89, 16, 142), 0x59108E), + (0, 1, .46, .45) : ("Pantone 228", (140, 0, 76), 0x8C004C) + } + +>>> unique_rgb_colors = { + (59, 178, 146), + (247, 127, 1), + (89, 16, 142), + (140, 0, 76), + (76, 0, 140) + } >>> teal_700 = hash((59, 178, 146)) @@ -205,7 +198,6 @@ Using a mutable data type within a `tuple` will make the enclosing `tuple` **un- Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: 'list' - ``` ## Extended tuples and related data types diff --git a/exercises/concept/tisbury-treasure-hunt/.docs/hints.md b/exercises/concept/tisbury-treasure-hunt/.docs/hints.md index 55697511dc..168e93ba7f 100644 --- a/exercises/concept/tisbury-treasure-hunt/.docs/hints.md +++ b/exercises/concept/tisbury-treasure-hunt/.docs/hints.md @@ -3,7 +3,7 @@ ## General -- [Tuples][tuples] are immutable [sequence Types][sequence types] that can contain any data type. +- [Tuples][tuples] are immutable [sequence types][sequence types] that can contain any data type. - Tuples are [iterable][iterable]. If you need indexes as well as values, use [`enumerate()`][enumerate] - Elements within tuples can be accessed via [bracket notation][bracket notation], using a zero-based index from the left, or -1 from the right. Other [Common Sequence Operations][common sequence operations] can also be used when working with tuples. @@ -32,7 +32,7 @@ - Remember: tuples are _immutable_, but the contents can be accessed via _index_ using _bracket notation_. - Tuples don't have to use parentheses unless there is _ambiguity_. - Python has multiple methods of string formatting. [`str.format()`][str.format] and [`f-strings`][f-strings] are two very common ones. -- There are multiple textual formatting options available via Pythons [`format specification mini-language`][format specification mini-language]. +- There are multiple textual formatting options available via Python's [`format specification mini-language`][format specification mini-language]. [bracket notation]: https://stackoverflow.com/questions/30250282/whats-the-difference-between-the-square-bracket-and-dot-notations-in-python diff --git a/exercises/concept/tisbury-treasure-hunt/.meta/exemplar.py b/exercises/concept/tisbury-treasure-hunt/.meta/exemplar.py index 74a3a2939e..e4429a0e1e 100644 --- a/exercises/concept/tisbury-treasure-hunt/.meta/exemplar.py +++ b/exercises/concept/tisbury-treasure-hunt/.meta/exemplar.py @@ -31,7 +31,7 @@ def compare_records(azara_record, rui_record): """Compare two record types and determine if their coordinates match. Parameters: - azara_record (tuple): A (treasure, coordinate) pair. + azara_record (tuple): A (treasure, coordinate) pair. rui_record (tuple): A (location, tuple(coordinate_1, coordinate_2), quadrant) trio. Returns: diff --git a/exercises/concept/tisbury-treasure-hunt/tuples.py b/exercises/concept/tisbury-treasure-hunt/tuples.py index 4bd18224fd..459557f334 100644 --- a/exercises/concept/tisbury-treasure-hunt/tuples.py +++ b/exercises/concept/tisbury-treasure-hunt/tuples.py @@ -31,7 +31,7 @@ def compare_records(azara_record, rui_record): """Compare two record types and determine if their coordinates match. Parameters: - azara_record (tuple): A (treasure, coordinate) pair. + azara_record (tuple): A (treasure, coordinate) pair. rui_record (tuple): A (location, tuple(coordinate_1, coordinate_2), quadrant) trio. Returns: From b6ea39b3e0460130831dc7a9da45b6f90a35f98a Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 15 May 2026 13:48:12 -0700 Subject: [PATCH 32/47] [Basics, Conditionals, and List Methods]: Corrected Typos, Formatting, & Grammar Issues (#4179) * Corrected typos, formatting, and grammar issues for basics, conditionals, and list-methods. * Further corrections from code review. * More typos. * Fixed double quote issue. --- concepts/basics/about.md | 75 ++++++++------- concepts/basics/introduction.md | 44 +++++---- concepts/conditionals/about.md | 91 ++++++++++--------- concepts/conditionals/introduction.md | 20 ++-- concepts/list-methods/about.md | 5 +- .../.docs/introduction.md | 17 ++-- 6 files changed, 135 insertions(+), 117 deletions(-) diff --git a/concepts/basics/about.md b/concepts/basics/about.md index 71f30524c4..6f932bfd16 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 d44e6c9fd0..cb61a0184a 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/conditionals/about.md b/concepts/conditionals/about.md index 2060905b33..8891683f79 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 ee1d433620..ba4f098493 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/list-methods/about.md b/concepts/list-methods/about.md index 1c9686360d..00b41f325e 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/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md index 166063057d..8f06aed400 100644 --- a/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md +++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md @@ -141,8 +141,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 @@ -165,10 +164,10 @@ 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. +>>> raise_to_power(3,3) # <--Invoking the function with the arguments 3 and 3. 27 @@ -182,14 +181,14 @@ 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.' # Importing the math module -import math +>>> import math ->>> math.pow(2,4) # Calling the pow() function from the math module +>>> math.pow(2,4) # <--Calling the pow() function from the math module. 16.0 ``` @@ -237,8 +236,7 @@ Docstrings can also function as [lightweight unit tests][doctests], which will b ```python -# An example on a user-defined function. -# This uses Google style docstrings. +# 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. @@ -270,6 +268,7 @@ Returns: Takes a number and raises it to the specified power, returning the result. ``` + [calls]: https://docs.python.org/3/reference/expressions.html#calls [comments]: https://realpython.com/python-comments-guide/#python-commenting-basics [docstring]: https://docs.python.org/3/tutorial/controlflow.html#tut-docstrings From 4a3ca80d8444dedfce68ec075d3fa0a243ee818d Mon Sep 17 00:00:00 2001 From: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> Date: Fri, 15 May 2026 20:51:48 -0400 Subject: [PATCH 33/47] [Rotational Cipher Approaches] Fix `ALPHABET` capitalization and approaches link (#4184) * fix ALPHABET capitalization and approaches link * add name to individual article contributor lists --- .../.approaches/alphabet/content.md | 12 ++++++------ .../.approaches/alphabet/snippet.txt | 4 ++-- .../rotational-cipher/.approaches/config.json | 11 +++++++---- .../.approaches/introduction.md | 18 +++++++++--------- .../.approaches/recursion/content.md | 6 +++--- .../.approaches/recursion/snippet.txt | 4 ++-- .../.approaches/str-translate/content.md | 12 ++++++------ .../.approaches/str-translate/snippet.txt | 6 +++--- .../rotational-cipher/.articles/config.json | 3 ++- .../.articles/performance/code/Benchmark.py | 16 ++++++++-------- .../.articles/performance/content.md | 1 + 11 files changed, 49 insertions(+), 44 deletions(-) diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md index a8fa1cd661..7897b74eb5 100644 --- a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md +++ b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md @@ -1,16 +1,16 @@ # Alphabet ```python -AlPHABET = "abcdefghijklmnopqrstuvwxyz" +ALPHABET = "abcdefghijklmnopqrstuvwxyz" def rotate(text, key): result = "" for letter in text: if letter.isalpha(): if letter.isupper(): - result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper() + result += ALPHABET[(ALPHABET.index(letter.lower()) + key) % 26].upper() else: - result += AlPHABET[(AlPHABET.index(letter) + key) % 26] + result += ALPHABET[(ALPHABET.index(letter) + key) % 26] else: result += letter return result @@ -22,9 +22,9 @@ The function `rotate()` is then declared, and a variable `result` is defined as The text argument is then iterated over via a [`for loop`][for-loop]. Each element is checked to make sure it is a letter, and subsequently checked if it is uppercase or lowercase. Uppercase letters are converted to lowercase. -Then the index of each letter is found in the `AlPHABET` constant. +Then the index of each letter is found in the `ALPHABET` constant. The numeric key value is added to the letter index and [modulo (`%`)][modulo] 26 is used on the result. -Finally, the new number is used as an index into the `AlPHABET` constant, and the resulting letter is converted back to uppercase. +Finally, the new number is used as an index into the `ALPHABET` constant, and the resulting letter is converted back to uppercase. Lowercase letters follow the same process without the conversion steps. @@ -36,7 +36,7 @@ If only English letters are needed, the constant [`string.ascii_lowercase`][asci ```python from string import ascii_lowercase -AlPHABET = ascii_lowercase +ALPHABET = ascii_lowercase ``` [ascii_lowercase]: https://docs.python.org/3/library/string.html#string.ascii_letters diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt b/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt index ade372000b..292f3b28f1 100644 --- a/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt +++ b/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt @@ -1,8 +1,8 @@ for letter in text: if letter.isalpha(): if letter.isupper(): - result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper() + result += ALPHABET[(ALPHABET.index(letter.lower()) + key) % 26].upper() else: - result += AlPHABET[(AlPHABET.index(letter) + key) % 26] + result += ALPHABET[(ALPHABET.index(letter) + key) % 26] else: result += letter \ No newline at end of file diff --git a/exercises/practice/rotational-cipher/.approaches/config.json b/exercises/practice/rotational-cipher/.approaches/config.json index 5cf51697a6..f58b5dff27 100644 --- a/exercises/practice/rotational-cipher/.approaches/config.json +++ b/exercises/practice/rotational-cipher/.approaches/config.json @@ -1,7 +1,7 @@ { "introduction": { "authors": ["meatball133", "bethanyg"], - "contributors": [] + "contributors": ["yrahcaz7"] }, "approaches": [ { @@ -16,21 +16,24 @@ "slug": "alphabet", "title": "Alphabet", "blurb": "Using the alphabet to rotate the alphabet", - "authors": ["meatball133", "bethanyg"] + "authors": ["meatball133", "bethanyg"], + "contributors": ["yrahcaz7"] }, { "uuid": "e539d1a5-f497-402b-a232-7e889f4323c1", "slug": "str-translate", "title": "Str Translate", "blurb": "Using str.translate to rotate the alphabet", - "authors": ["meatball133", "bethanyg"] + "authors": ["meatball133", "bethanyg"], + "contributors": ["yrahcaz7"] }, { "uuid": "0c74890e-d96e-47a2-a8bf-93c45dd67f94", "slug": "recursion", "title": "Recursion", "blurb": "Using Recursion to rotate the alphabet", - "authors": ["meatball133", "bethanyg"] + "authors": ["meatball133", "bethanyg"], + "contributors": ["yrahcaz7"] } ] } diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md index 047d9950ec..182193236a 100644 --- a/exercises/practice/rotational-cipher/.approaches/introduction.md +++ b/exercises/practice/rotational-cipher/.approaches/introduction.md @@ -54,16 +54,16 @@ Here, if we want to use the Scandinavian letter: **å**, we can simply insert it ```python # This only uses English characters -AlPHABET = "abcdefghijklmnopqrstuvwxyz" +ALPHABET = "abcdefghijklmnopqrstuvwxyz" def rotate(text, key): result = "" for letter in text: if letter.isalpha(): if letter.isupper(): - result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper() + result += ALPHABET[(ALPHABET.index(letter.lower()) + key) % 26].upper() else: - result += AlPHABET[(AlPHABET.index(letter) + key) % 26] + result += ALPHABET[(ALPHABET.index(letter) + key) % 26] else: result += letter return result @@ -82,11 +82,11 @@ The benefit of this approach is that it has no visible loop, making the code mor ```python -AlPHABET = "abcdefghijklmnopqrstuvwxyz" +ALPHABET = "abcdefghijklmnopqrstuvwxyz" def rotate(text, key): - translator = AlPHABET[key:] + AlPHABET[:key] - return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper())) + translator = ALPHABET[key:] + ALPHABET[:key] + return text.translate(str.maketrans(ALPHABET + ALPHABET.upper(), translator + translator.upper())) ``` For more information, check out the [Str translate approach][approach-str-translate]. @@ -106,7 +106,7 @@ Calculate your base case carefully to avoid errors. ```python -AlPHABET = "abcdefghijklmnopqrstuvwxyz" +ALPHABET = "abcdefghijklmnopqrstuvwxyz" def rotate(text, key): if text == "": @@ -114,9 +114,9 @@ def rotate(text, key): first_letter, rest = text[0], text[1:] if first_letter.isalpha(): if first_letter.isupper(): - return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key) + return ALPHABET[(ALPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key) else: - return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate(rest, key) + return ALPHABET[(ALPHABET.index(first_letter) + key) % 26] + rotate(rest, key) else: return first_letter + rotate(rest, key) ``` diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/content.md b/exercises/practice/rotational-cipher/.approaches/recursion/content.md index 48ff3facae..7211b5ae6e 100644 --- a/exercises/practice/rotational-cipher/.approaches/recursion/content.md +++ b/exercises/practice/rotational-cipher/.approaches/recursion/content.md @@ -1,7 +1,7 @@ # Recursion ```python -AlPHABET = "abcdefghijklmnopqrstuvwxyz" +ALPHABET = "abcdefghijklmnopqrstuvwxyz" def rotate(text, key): if text == "": @@ -9,9 +9,9 @@ def rotate(text, key): first_letter, rest = text[0], text[1:] if first_letter.isalpha(): if first_letter.isupper(): - return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key) + return ALPHABET[(ALPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key) else: - return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate(rest, key) + return ALPHABET[(ALPHABET.index(first_letter) + key) % 26] + rotate(rest, key) else: return first_letter + rotate(rest, key) ``` diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt b/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt index 098c419fe7..ae0ff78fb5 100644 --- a/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt +++ b/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt @@ -1,8 +1,8 @@ first_letter, rest = text[0], text[1:] if first_letter.isalpha(): if first_letter.isupper(): - return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key) + return ALPHABET[(ALPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key) else: - return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate(rest, key) + return ALPHABET[(ALPHABET.index(first_letter) + key) % 26] + rotate(rest, key) else: return first_letter + rotate(rest, key) \ No newline at end of file diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md index b3d37110c8..ac00db3e9b 100644 --- a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md +++ b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md @@ -1,11 +1,11 @@ # Str Translate ```python -AlPHABET = "abcdefghijklmnopqrstuvwxyz" +ALPHABET = "abcdefghijklmnopqrstuvwxyz" def rotate(text, key): - translator = AlPHABET[key:] + AlPHABET[:key] - return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper())) + translator = ALPHABET[key:] + ALPHABET[:key] + return text.translate(str.maketrans(ALPHABET + ALPHABET.upper(), translator + translator.upper())) ``` This approach uses the [`.translate`][translate] method. @@ -14,10 +14,10 @@ To create a translation table we use [`str.makestrans`][maketrans]. This approach starts with defining a constant of all the lowercase letters in the alphabet. Then the function `rotate()` is declared. -A `translator` variable defined with the value of the `AlPHABET` constant [sliced][slicing] from the key to the end and then sliced from the start to the key. +A `translator` variable defined with the value of the `ALPHABET` constant [sliced][slicing] from the key to the end and then sliced from the start to the key. This is done so we have 2 strings which are the same but shifted by the key value. -Say we have the `AlPHABET` constant with the value of `abcdefghijklmnopqrstuvwxyz` and the key is 3. +Say we have the `ALPHABET` constant with the value of `abcdefghijklmnopqrstuvwxyz` and the key is 3. Then the `translator` variable will have the value of `defghijklmnopqrstuvwxyzabc`. `str.translate` is then called on the `text` argument. @@ -25,7 +25,7 @@ Then the `translator` variable will have the value of `defghijklmnopqrstuvwxyzab To create a translation table, `str.makestrans` is used. `makestrans` takes 2 arguments: the first is the string to be translated, and the second is the string the first argument should be translated to. -For our solution, the first argument is the `AlPHABET` constant + the `AlPHABET` constant in uppercase. +For our solution, the first argument is the `ALPHABET` constant + the `ALPHABET` constant in uppercase. The second argument is the `translator` variable + uppercase `translator` variable. `str.makestrans` takes the [Unicode][unicode] values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a `dict`. diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt b/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt index 75350ae406..61a63b38d0 100644 --- a/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt +++ b/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt @@ -1,5 +1,5 @@ -AlPHABET = "abcdefghijklmnopqrstuvwxyz" +ALPHABET = "abcdefghijklmnopqrstuvwxyz" def rotate(text, key): - translator = AlPHABET[key:] + AlPHABET[:key] - return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper())) \ No newline at end of file + translator = ALPHABET[key:] + ALPHABET[:key] + return text.translate(str.maketrans(ALPHABET + ALPHABET.upper(), translator + translator.upper())) \ No newline at end of file diff --git a/exercises/practice/rotational-cipher/.articles/config.json b/exercises/practice/rotational-cipher/.articles/config.json index fe3d6dc2a2..40cfdbec8e 100644 --- a/exercises/practice/rotational-cipher/.articles/config.json +++ b/exercises/practice/rotational-cipher/.articles/config.json @@ -5,7 +5,8 @@ "slug": "performance", "title": "Performance deep dive", "blurb": "Deep dive to find out the performance between different approaches", - "authors": ["meatball133", "bethanyg"] + "authors": ["meatball133", "bethanyg"], + "contributors": ["yrahcaz7"] } ] } diff --git a/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py b/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py index 2919024a1f..e37e7938ae 100644 --- a/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py +++ b/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py @@ -6,8 +6,8 @@ print(sys.version) -AlPHABET = "abcdefghijklmnopqrstuvwxyz" -COMBINATIONS = itertools.combinations_with_replacement(f"{AlPHABET[:13]}{AlPHABET[:13].upper()} 12,", 2) +ALPHABET = "abcdefghijklmnopqrstuvwxyz" +COMBINATIONS = itertools.combinations_with_replacement(f"{ALPHABET[:13]}{ALPHABET[:13].upper()} 12,", 2) TEST_TEST = "".join([element for sublist in COMBINATIONS for element in sublist]) def rotate_ascii(text, key): @@ -28,17 +28,17 @@ def rotate_alphabet(text, key): for letter in text: if letter.isalpha(): if letter.isupper(): - result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper() + result += ALPHABET[(ALPHABET.index(letter.lower()) + key) % 26].upper() else: - result += AlPHABET[(AlPHABET.index(letter) + key) % 26] + result += ALPHABET[(ALPHABET.index(letter) + key) % 26] else: result += letter return result def rotate_translate(text, key): - translator = AlPHABET[key:] + AlPHABET[:key] - return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper())) + translator = ALPHABET[key:] + ALPHABET[:key] + return text.translate(str.maketrans(ALPHABET + ALPHABET.upper(), translator + translator.upper())) def rotate_recursion(text, key): @@ -47,9 +47,9 @@ def rotate_recursion(text, key): first_letter, rest = text[0], text[1:] if first_letter.isalpha(): if first_letter.isupper(): - return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate_recursion(rest, key) + return ALPHABET[(ALPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate_recursion(rest, key) else: - return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate_recursion(rest, key) + return ALPHABET[(ALPHABET.index(first_letter) + key) % 26] + rotate_recursion(rest, key) else: return first_letter + rotate_recursion(rest, key) diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md index 8401b40e25..f53da0935f 100644 --- a/exercises/practice/rotational-cipher/.articles/performance/content.md +++ b/exercises/practice/rotational-cipher/.articles/performance/content.md @@ -35,6 +35,7 @@ For a short string as input, is the alphabet approach the fastest, followed by a This means that if you know the input is a short string, the fastest approach is to use the alphabet, and forgo the overhead of making and saving a translation dictionary. On the other hand, if the input is a long string, the overhead of making a dictionary is amortized over the length of the text to be translated, and the fastest approach becomes `str.translate`. +[approaches]: https://exercism.org/tracks/python/exercises/rotational-cipher/dig_deeper [approach-recursion]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/recursion [approach-str-translate]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/str-translate [approach-ascii-values]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/ascii-values From ed70b14585f1c88d15d0ac05acb8d8f14f2562e0 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 15 May 2026 18:43:28 -0700 Subject: [PATCH 34/47] [Sets and Dict Methods Concepts]: Typo & Formatting Fixes (#4185) * Typo and formatting fixes for stets and dictmethods. * Applied changes from code review. --- concepts/dict-methods/about.md | 46 +++++++++++++-------------- concepts/dict-methods/introduction.md | 14 ++++---- concepts/sets/about.md | 18 +++++------ concepts/sets/introduction.md | 6 ++-- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/concepts/dict-methods/about.md b/concepts/dict-methods/about.md index 02f2dc21fa..7af90a7714 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), @@ -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 c15fbc113d..b1e8eb8f20 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/sets/about.md b/concepts/sets/about.md index 2c011c1447..944f98d1b5 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 5d66b6a8ad..551e295e6a 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 From 9deedc7cc0a32e63b1fe7c9856919e5e1c13c016 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Fri, 15 May 2026 20:14:47 -0700 Subject: [PATCH 35/47] [Unpacking, Generators, Bin/Ox/Hex Concepts]: Typo & Formatting Fixes (#4186) * Typo and formatting fixes in concept docs. * Took care of the generator-iterator thingy. --- concepts/binary-octal-hexadecimal/about.md | 21 +++++++++----- .../binary-octal-hexadecimal/introduction.md | 2 +- concepts/generators/about.md | 29 +++++++++++++++++-- .../about.md | 6 ++-- .../introduction.md | 2 +- .../plane-tickets/.docs/introduction.md | 6 ++-- 6 files changed, 47 insertions(+), 19 deletions(-) diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md index a7fca3714e..67646aed2f 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 a06ac922fa..84ff634263 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/generators/about.md b/concepts/generators/about.md index 59b5035d6b..4b2e74cbad 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/unpacking-and-multiple-assignment/about.md b/concepts/unpacking-and-multiple-assignment/about.md index d4b9168ad1..b269b312ad 100644 --- a/concepts/unpacking-and-multiple-assignment/about.md +++ b/concepts/unpacking-and-multiple-assignment/about.md @@ -8,7 +8,7 @@ A very common example of this behavior is `for item in list`, where `item` takes This allows for code to be more concise and readable, and is done by separating the variables to be assigned with a comma such as `first, second, third = (1,2,3)` or `for index, item in enumerate(iterable)`. The special operators `*` and `**` are often used in unpacking contexts. -`*` can be used to combine multiple `lists`/`tuples` into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`. +`*` can be used to combine multiple `list`s/`tuple`s into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`. `**` can be used to combine multiple dictionaries into one dictionary by _unpacking_ each into a new common `dict`. When the `*` operator is used without a collection, it _packs_ a number of values into a `list`. @@ -73,7 +73,7 @@ Since `tuples` are immutable, you can't swap elements in a `tuple`. The examples below use `lists` but the same concepts apply to `tuples`. ~~~~ -In Python, it is possible to [unpack the elements of `list`/`tuple`/`dictionary`][unpacking] into distinct variables. +In Python, it is possible to [unpack the elements of a `list`/`tuple`/`dict`][unpacking] into distinct variables. Since values appear within `lists`/`tuples` in a specific order, they are unpacked into variables in the same order: ```python @@ -94,7 +94,7 @@ If there are values that are not needed then you can use `_` to flag them: ### Deep unpacking -Unpacking and assigning values from a `list`/`tuple` inside of a `list` or `tuple` (_also known as nested lists/tuples_), works in the same way a shallow unpacking does, but often needs qualifiers to clarify the values context or position: +Unpacking and assigning values from a `list`/`tuple` inside of a `list` or `tuple` (_also known as nested lists/tuples_), works in the same way a shallow unpacking does, but often needs qualifiers to clarify the value's context or position: ```python >>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]] diff --git a/concepts/unpacking-and-multiple-assignment/introduction.md b/concepts/unpacking-and-multiple-assignment/introduction.md index 59cab3b4ec..c8878eb180 100644 --- a/concepts/unpacking-and-multiple-assignment/introduction.md +++ b/concepts/unpacking-and-multiple-assignment/introduction.md @@ -8,7 +8,7 @@ A very common example of this behavior is `for item in list`, where `item` takes This allows for code to be more concise and readable, and is done by separating the variables to be assigned with a comma such as `first, second, third = (1,2,3)` or `for index, item in enumerate(iterable)`. The special operators `*` and `**` are often used in unpacking contexts. -`*` can be used to combine multiple `lists`/`tuples` into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`. +`*` can be used to combine multiple `list`s/`tuple`s into one `list`/`tuple` by _unpacking_ each into a new common `list`/`tuple`. `**` can be used to combine multiple dictionaries into one dictionary by _unpacking_ each into a new common `dict`. When the `*` operator is used without a collection, it _packs_ a number of values into a `list`. diff --git a/exercises/concept/plane-tickets/.docs/introduction.md b/exercises/concept/plane-tickets/.docs/introduction.md index d17f90c812..ac0a53a8ef 100644 --- a/exercises/concept/plane-tickets/.docs/introduction.md +++ b/exercises/concept/plane-tickets/.docs/introduction.md @@ -1,7 +1,7 @@ # Generators -A `generator` is a function or expression that returns a special type of [iterator][iterator] called [generator iterator][generator-iterator]. -`Generator-iterators` are [lazy][lazy iterator]: they do not store their `values` in memory, but _generate_ their values when needed. +A `generator` is a function or expression that returns a special type of [iterator][iterator] called a [`generator iterator`][generator-iterator]. +`Generator-iterator`s are [lazy][lazy iterator]: they do not store their `values` in memory, but _generate_ their values when needed. A generator function looks like any other function, but contains one or more [yield expressions][yield expression]. Each `yield` will suspend code execution, saving the current execution state (_including all local variables and try-statements_). @@ -144,8 +144,6 @@ Now whenever `__next__()` is called on the `infinite_sequence` object, it will r [generator-iterator]: https://docs.python.org/3.11/glossary.html#term-generator-iterator -[iterables]: https://wiki.python.org/moin/Iterator [iterator]: https://docs.python.org/3.11/glossary.html#term-iterator -[lazy evaluation]: https://en.wikipedia.org/wiki/Lazy_evaluation [lazy iterator]: https://en.wikipedia.org/wiki/Lazy_evaluation [yield expression]: https://docs.python.org/3.11/reference/expressions.html#yield-expressions From a3a82a324c6b3d04ee63e814aaf0d1edd0a600a6 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Sat, 16 May 2026 12:16:48 -0700 Subject: [PATCH 36/47] Updated version of Python in badge. (#4188) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd9be64f71..38ebda1d34 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

Exercism Python Track

                          [![Discourse topics](https://img.shields.io/discourse/topics?color=8A08E6&label=Connect%20&labelColor=FFDF58&logo=Discourse&logoColor=8A08E6&server=https%3A%2F%2Fforum.exercism.org&style=social)](https://forum.exercism.org) -  [![Exercism_II](https://img.shields.io/badge/Exercism--Built-9101FF?logo=python&logoColor=FFDF58&labelColor=3D7AAB&label=Python%203.11%20Powered)](https://exercism.org) +  [![Exercism_II](https://img.shields.io/badge/Exercism--Built-9101FF?logo=python&logoColor=FFDF58&labelColor=3D7AAB&label=Python%203.13.5%20Powered)](https://exercism.org)   [![Exercism_III](https://img.shields.io/badge/PAUSED-C73D4E?labelColor=3D454D&label=Contributions)](https://exercism.org/blog/freeing-our-maintainers)   [![Build Status](https://github.com/exercism/python/workflows/Exercises%20check/badge.svg)](https://github.com/exercism/python/actions?query=workflow%3A%22Exercises+check%22) From 232c8952f98ec0b31dbae169a63f606c8342b2d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Sat, 16 May 2026 12:21:13 -0700 Subject: [PATCH 37/47] Tweaks to `string-formatting` concept (#4099) * Tweaks to `string-formatting` concept * cannot to can not * Add self to contributors * Add t-strings link * Add examples * can to could Co-authored-by: BethanyG * considerations to use-cases Co-authored-by: BethanyG * A f-string to an f-string Co-authored-by: BethanyG --------- Co-authored-by: BNAndras Co-authored-by: BethanyG --- concepts/string-formatting/.meta/config.json | 10 +- concepts/string-formatting/about.md | 135 ++++++++++++++----- concepts/string-formatting/introduction.md | 22 +-- 3 files changed, 121 insertions(+), 46 deletions(-) diff --git a/concepts/string-formatting/.meta/config.json b/concepts/string-formatting/.meta/config.json index 4e3955fc29..9f4edb0732 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 f3b2756b76..be2ac0a122 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 aa476de9ca..ed88cc4d01 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. From ba57e6f35504c989689e6d756c4bb7779cefb1d6 Mon Sep 17 00:00:00 2001 From: VaiaPatta1985 <145572182+VaiaPatta1985@users.noreply.github.com> Date: Sun, 17 May 2026 23:02:52 +0300 Subject: [PATCH 38/47] Change curly brackets to angled in tests.md (#4189) --- exercises/shared/.docs/tests.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/exercises/shared/.docs/tests.md b/exercises/shared/.docs/tests.md index 5d1c9b9959..8b6d608a48 100644 --- a/exercises/shared/.docs/tests.md +++ b/exercises/shared/.docs/tests.md @@ -13,32 +13,32 @@ Extended information can be found in our website [Python testing guide][Python t ### Running Tests -To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_). +To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `` below with your path_). Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded. Linux/MacOS ```bash -$ cd {path/to/exercise-folder-location} +$ cd ``` Windows ```powershell -PS C:\Users\foobar> cd {path\to\exercise-folder-location} +PS C:\Users\foobar> cd ```
-Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file: +Next, run the `pytest` command in your terminal, replacing `` with the name of the test file: Linux/MacOS ```bash -$ python3 -m pytest -o markers=task {exercise_test.py} +$ python3 -m pytest -o markers=task ==================== 7 passed in 0.08s ==================== ``` Windows ```powershell -PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py} +PS C:\Users\foobar> py -m pytest -o markers=task ==================== 7 passed in 0.08s ==================== ``` From 1350f3501fbb34c8ceabb7454d12efac5e264a10 Mon Sep 17 00:00:00 2001 From: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> Date: Mon, 18 May 2026 15:44:07 -0400 Subject: [PATCH 39/47] [Darts Approach Docs] Various fixes and improvements (#4187) * Clean up Darts approach docs * Darts: Improve `dict-and-generator` variants --- .../.approaches/booleans-as-ints/content.md | 10 ++-- .../.approaches/booleans-as-ints/snippet.txt | 4 +- .../practice/darts/.approaches/config.json | 22 +++++--- .../.approaches/dict-and-dict-get/content.md | 22 ++++---- .../.approaches/dict-and-dict-get/snippet.txt | 2 +- .../.approaches/dict-and-generator/content.md | 37 +++++++------ .../dict-and-generator/snippet.txt | 13 ++--- .../.approaches/if-statements/content.md | 27 +++++---- .../.approaches/if-statements/snippet.txt | 4 +- .../darts/.approaches/introduction.md | 55 ++++++++++--------- .../darts/.approaches/match-case/content.md | 30 +++++----- .../darts/.approaches/match-case/snippet.txt | 4 +- .../.approaches/tuple-and-loop/content.md | 14 +++-- .../.approaches/tuple-and-loop/snippet.txt | 13 +++-- 14 files changed, 136 insertions(+), 121 deletions(-) diff --git a/exercises/practice/darts/.approaches/booleans-as-ints/content.md b/exercises/practice/darts/.approaches/booleans-as-ints/content.md index d3c1541d2a..c948f3d631 100644 --- a/exercises/practice/darts/.approaches/booleans-as-ints/content.md +++ b/exercises/practice/darts/.approaches/booleans-as-ints/content.md @@ -3,8 +3,8 @@ ```python def score(x_coord, y_coord): - radius = (x_coord**2 + y_coord**2) - return (radius<=1)*5 + (radius<=25)*4 + (radius<=100)*1 + radius_squared = x_coord**2 + y_coord**2 + return (radius_squared<=1)*5 + (radius_squared<=25)*4 + (radius_squared<=100)*1 ``` @@ -25,12 +25,12 @@ Instead, the Python documentation recommends an explicit conversion to `int`: ```python def score(x_coord, y_coord): - radius = (x_coord**2 + y_coord**2) - return int(radius<=1)*5 + int(radius<=25)*4 + int(radius<=100)*1 + radius_squared = x_coord**2 + y_coord**2 + return int(radius_squared<=1)*5 + int(radius_squared<=25)*4 + int(radius_squared<=100)*1 ``` Beyond that recommendation, the terseness of this approach might be harder to reason about or decode — especially if a programmer is coming from a programming langauge that does not treat Boolean values as `ints`. -Despite the "radius" variable name, it is also more difficult to relate the scoring "rings" of the Dartboard to the values being checked and calculated in the `return` statement. +Despite the "radius_squared" variable name, it is also more difficult to relate the scoring "rings" of the Dartboard to the values being checked and calculated in the `return` statement. If using this code in a larger program, it would be strongly recommended that a docstring be provided to explain the Dartboard rings, scoring rules, and the corresponding scores. [bools-as-ints]: https://docs.python.org/3/library/stdtypes.html#boolean-type-bool \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt b/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt index ec7dcfabbf..f09eb53386 100644 --- a/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt +++ b/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt @@ -1,3 +1,3 @@ def score(x_coord, y_coord): - radius = (x_coord**2 + y_coord**2) - return (radius<=1)*5 + (radius<=25)*4 +(radius<=100)*1 \ No newline at end of file + radius_squared = x_coord**2 + y_coord**2 + return (radius_squared<=1)*5 + (radius_squared<=25)*4 + (radius_squared<=100)*1 \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/config.json b/exercises/practice/darts/.approaches/config.json index 77f331bfce..337f370bf9 100644 --- a/exercises/practice/darts/.approaches/config.json +++ b/exercises/practice/darts/.approaches/config.json @@ -1,7 +1,7 @@ { "introduction": { "authors": ["bethanyg"], - "contributors": [] + "contributors": ["yrahcaz7"] }, "approaches": [ { @@ -9,42 +9,48 @@ "slug": "if-statements", "title": "Use If Statements", "blurb": "Use if-statements to check scoring boundaries for a dart throw.", - "authors": ["bethanyg"] + "authors": ["bethanyg"], + "contributors": ["yrahcaz7"] }, { "uuid": "f8f5533a-09d2-4b7b-9dec-90f268bfc03b", "slug": "tuple-and-loop", "title": "Use a Tuple & Loop through Scores", "blurb": "Score the Dart throw by looping through a tuple of scores.", - "authors": ["bethanyg"] + "authors": ["bethanyg"], + "contributors": ["yrahcaz7"] }, { "uuid": "a324f99e-15bb-43e0-9181-c1652094bc4f", "slug": "match-case", "title": "Use Structural Pattern Matching ('Match-Case')", "blurb": "Use a Match-Case (Structural Pattern Matching) to score the dart throw.)", - "authors": ["bethanyg"] + "authors": ["bethanyg"], + "contributors": ["yrahcaz7"] }, { "uuid": "966bd2dd-c4fd-430b-ad77-3a304dedd82e", "slug": "dict-and-generator", "title": "Use a Dictionary with a Generator Expression", "blurb": "Use a generator expression looping over a scoring dictionary, getting the max score for the dart throw.", - "authors": ["bethanyg"] + "authors": ["bethanyg"], + "contributors": ["yrahcaz7"] }, { "uuid": "5b087f50-31c5-4b84-9116-baafd3a30ed6", "slug": "booleans-as-ints", "title": "Use Boolean Values as Integers", "blurb": "Use True and False as integer values to calculate the score of the dart throw.", - "authors": ["bethanyg"] + "authors": ["bethanyg"], + "contributors": ["yrahcaz7"] }, { "uuid": "0b2dbcd3-f0ac-45f7-af75-3451751fd21f", "slug": "dict-and-dict-get", "title": "Use a Dictionary with dict.get", - "blurb": "Loop over a dictionary and retrieve score via dct.get.", - "authors": ["bethanyg"] + "blurb": "Loop over a dictionary and retrieve score via dict.get.", + "authors": ["bethanyg"], + "contributors": ["yrahcaz7"] } ] } \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/dict-and-dict-get/content.md b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md index a3c5bc2ac5..62c79f36a0 100644 --- a/exercises/practice/darts/.approaches/dict-and-dict-get/content.md +++ b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md @@ -3,11 +3,11 @@ ```python def score(x_coord, y_coord): - point = (x_coord**2 + y_coord**2) + point = x_coord**2 + y_coord**2 scores = { point <= 100: 1, point <= 25: 5, - point <= 1: 10 + point <= 1: 10, } return scores.get(True, 0) @@ -17,10 +17,10 @@ At first glance, this approach looks similar to the [Booleans as Integers][appro However, this approach is **not** interpreting Booleans as integers and is instead exploiting three key properties of [dictionaries][dicts]: -1. [Keys must be hashable][hashable-keys] — in other words, keys have to be _unique_. -2. Insertion order is preserved (_as of `Python 3.7`_), and evaluation/iteration happens in insertion order. -3. Duplicate keys _overwrite_ existing keys. - If the first key is `True` and the third key is `True`, the _value_ from the third key will overwrite the value from the first key. +1. [Keys must be hashable][hashable-keys] — in other words, keys have to be _unique_. +2. Insertion order is preserved (_as of `Python 3.7`_), and evaluation/iteration happens in insertion order. +3. Duplicate keys _overwrite_ existing keys. + If the first key is `True` and the third key is `True`, the _value_ from the third key will overwrite the value from the first key. Finally, the `return` line uses [`dict.get()`][dict-get] to `return` a default value of 0 when a throw is outside the existing circle radii. To see this in action, you can view this code on [Python Tutor][dict-get-python-tutor]. @@ -34,9 +34,8 @@ The following code variations do not pass the exercise tests: ```python - def score(x_coord, y_coord): - point = (x_coord**2 + y_coord**2) + point = x_coord**2 + y_coord**2 scores = { point <= 1: 10, point <= 25: 5, @@ -44,11 +43,11 @@ def score(x_coord, y_coord): } return scores.get(True, 0) - - #OR# + +#OR# def score(x_coord, y_coord): - point = (x_coord**2 + y_coord**2) + point = x_coord**2 + y_coord**2 scores = { point <= 25: 5, point <= 1: 10, @@ -56,7 +55,6 @@ def score(x_coord, y_coord): } return scores.get(True, 0) - ``` While this approach is a _very clever_ use of dictionary properties, it is likely to be very hard to reason about for those who are not deeply knowledgeable. diff --git a/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt b/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt index 6d496f54d3..8d2f426d84 100644 --- a/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt +++ b/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt @@ -1,5 +1,5 @@ def score(x_coord, y_coord): - point = (x_coord**2 + y_coord**2) + point = x_coord**2 + y_coord**2 scores = {point <= 100: 1, point <= 25: 5, point <= 1: 10} return scores.get(True, 0) \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/dict-and-generator/content.md b/exercises/practice/darts/.approaches/dict-and-generator/content.md index 30ffeac1eb..041ce80f1e 100644 --- a/exercises/practice/darts/.approaches/dict-and-generator/content.md +++ b/exercises/practice/darts/.approaches/dict-and-generator/content.md @@ -3,14 +3,15 @@ ```python def score(x_coord, y_coord): throw = x_coord**2 + y_coord**2 - rules = {1: 10, 25: 5, 100: 1, 200: 0} + rules = {1: 10, 25: 5, 100: 1} - return max(point for distance, point in - rules.items() if throw <= distance) + return max(point for distance, point in + rules.items() if throw <= distance, + default=0) ``` -This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items] and writes the `loop` as a [`generator-expression`][generator-expression] inside `max()`. +This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items] and writes the `loop` as a [`generator-expression`][generator-expression] inside `max()`. In cases where the scoring circles overlap, `max()` will return the maximum score available for the throw. The generator expression inside `max()` is the equivalent of using a `for-loop` and a variable to determine the max score: @@ -24,6 +25,7 @@ def score(x_coord, y_coord): for distance, point in rules.items(): if throw <= distance and point > max_score: max_score = point + return max_score ``` @@ -31,21 +33,23 @@ def score(x_coord, y_coord): A `list` or `tuple` can also be used in place of `max()`, but then requires an index to return the max score: ```python +from math import inf + def score(x_coord, y_coord): throw = x_coord**2 + y_coord**2 - rules = {1: 10, 25: 5, 100: 1, 200: 0} + rules = {1: 10, 25: 5, 100: 1, inf: 0} - return [point for distance, point in - rules.items() if throw <= distance][0] #<-- have to specify index 0. - + return [point for distance, point in + rules.items() if throw <= distance][0] # <-- Have to specify index 0. + #OR# def score(x_coord, y_coord): throw = x_coord**2 + y_coord**2 - rules = {1: 10, 25: 5, 100: 1, 200: 0} + rules = {1: 10, 25: 5, 100: 1, inf: 0} - return tuple(point for distance, point in - rules.items() if throw <= distance)[0] + return tuple(point for distance, point in + rules.items() if throw <= distance)[0] ``` @@ -53,10 +57,11 @@ This solution can even be reduced to a "one-liner". However, this is not performant, and is difficult to read: ```python -def score(x_coord, y_coord): - return max(point for distance, point in - {1: 10, 25: 5, 100: 1, 200: 0}.items() if - (x_coord**2 + y_coord**2) <= distance) +def score(x_coord, y_coord): + return max(point for distance, point in + {1: 10, 25: 5, 100: 1}.items() if + (x_coord**2 + y_coord**2) <= distance, + default=0) ``` While all of these variations do pass the tests, they suffer from even more over-engineering/performance caution than the earlier tuple and loop approach (_although for the data in this problem, the performance hit is slight_). @@ -64,6 +69,6 @@ Additionally, the dictionary will take much more space in memory than using a `t In some circumstances, these variations might also be harder to reason about for those not familiar with `generator-expressions` or `list comprehensions`. -[approach-tuple-and-loop]: https://exercism.org/tracks/python/exercises/darts/approaches/tuple-and-loop +[approach-tuple-and-loop]: https://exercism.org/tracks/python/exercises/darts/approaches/tuple-and-loop [dict-items]: https://docs.python.org/3/library/stdtypes.html#dict.items [generator-expression]: https://dbader.org/blog/python-generator-expressions diff --git a/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt b/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt index f6649cf3a9..1e2b61a355 100644 --- a/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt +++ b/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt @@ -1,8 +1,7 @@ def score(x_coord, y_coord): - length = x_coord**2 + y_coord**2 - rules = {1.0: 10, 25.0: 5, 100.0: 1, 200: 0} - score = max(point for - distance, point in - rules.items() if length <= distance) - - return score \ No newline at end of file + throw = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1} + + return max(point for distance, point in + rules.items() if throw <= distance, + default=0) \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/if-statements/content.md b/exercises/practice/darts/.approaches/if-statements/content.md index 40e2886ddb..9bbb7ecf8c 100644 --- a/exercises/practice/darts/.approaches/if-statements/content.md +++ b/exercises/practice/darts/.approaches/if-statements/content.md @@ -9,8 +9,8 @@ def score(x_coord, y_coord): distance = math.sqrt(x_coord**2 + y_coord**2) if distance <= 1: return 10 - if distance <= 5: return 5 - if distance <= 10: return 1 + if distance <= 5: return 5 + if distance <= 10: return 1 return 0 ``` @@ -21,23 +21,23 @@ Because the `if-statements` are simple and readable, they're written on one line Zero is returned if no other check is true. -To avoid importing the `math` module (_for a very very slight speedup_), (x**2 +y**2) can be calculated instead, and the scoring rings can be adjusted to 1, 25, and 100: +To avoid importing the `math` module (_for a very very slight speedup_), (`x**2 + y**2`) can be calculated instead, and the scoring rings can be adjusted to 1, 25, and 100: ```python # Checks scores from the center --> edge. def score(x_coord, y_coord): - distance = x_coord**2 + y_coord**2 + distance_squared = x_coord**2 + y_coord**2 - if distance <= 1: return 10 - if distance <= 25: return 5 - if distance <= 100: return 1 + if distance_squared <= 1: return 10 + if distance_squared <= 25: return 5 + if distance_squared <= 100: return 1 return 0 ``` -# Variation 1: Check from Edge to Center Using Upper and Lower Bounds +## Variation 1: Check from Edge to Center Using Upper and Lower Bounds ```python @@ -56,18 +56,17 @@ def score(x_coord, y_coord): This variant checks from the edge moving inward, checking both a lower and upper bound due to the overlapping scoring circles in this direction. -Scores for any of these solutions can also be assigned to a variable to avoid multiple `returns`, but this isn't really necessary: +Scores for any of these solutions can also be assigned to a variable to avoid multiple `returns`, but this isn't really necessary: ```python # Checks scores from the edge --> center def score(x_coord, y_coord): - distance = x_coord**2 + y_coord**2 + distance_squared = x_coord**2 + y_coord**2 points = 10 - if distance > 100: points = 0 - if 25 < distance <= 100: points = 1 - if 1 < distance <= 25: points = 5 + if distance_squared > 100: points = 0 + if 25 < distance_squared <= 100: points = 1 + if 1 < distance_squared <= 25: points = 5 return points ``` - diff --git a/exercises/practice/darts/.approaches/if-statements/snippet.txt b/exercises/practice/darts/.approaches/if-statements/snippet.txt index 18537416e2..b91a4285e6 100644 --- a/exercises/practice/darts/.approaches/if-statements/snippet.txt +++ b/exercises/practice/darts/.approaches/if-statements/snippet.txt @@ -3,6 +3,6 @@ import math def score(x_coord, y_coord): distance = math.sqrt(x_coord**2 + y_coord**2) if distance <= 1: return 10 - if distance <= 5: return 5 - if distance <= 10: return 1 + if distance <= 5: return 5 + if distance <= 10: return 1 return 0 \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/introduction.md b/exercises/practice/darts/.approaches/introduction.md index cf7c6a23dd..f4ca20dcf0 100644 --- a/exercises/practice/darts/.approaches/introduction.md +++ b/exercises/practice/darts/.approaches/introduction.md @@ -1,7 +1,7 @@ # Introduction -There are multiple Pythonic ways to solve the Darts exercise. +There are multiple Pythonic ways to solve the Darts exercise. Among them are: - Using `if-statements` @@ -17,10 +17,10 @@ Among them are: The goal of the Darts exercise is to score a single throw in a Darts game. The scoring areas are _concentric circles_, so boundary values need to be checked in order to properly score a throw. -The key is to determine how far from the center the dart lands (_by calculating sqrt(x**2 + y**2), or a variation_) and then determine what scoring ring it falls into. +The key is to determine how far from the center the dart lands (_by calculating `sqrt(x**2 + y**2)`, or a variation_) and then determine what scoring ring it falls into. -**_Order matters_** - each bigger target circle contains all the smaller circles, so the most straightforward solution is to check the smallest circle first. +**_Order matters_** — each bigger target circle contains all the smaller circles, so the most straightforward solution is to check the smallest circle first. Otherwise, you must box your scoring by checking both a _lower bound_ and an _upper bound_. @@ -38,8 +38,8 @@ def score(x_coord, y_coord): distance = math.sqrt(x_coord**2 + y_coord**2) if distance <= 1: return 10 - if distance <= 5: return 5 - if distance <= 10: return 1 + if distance <= 5: return 5 + if distance <= 10: return 1 return 0 ``` @@ -49,16 +49,18 @@ This approach uses [concept:python/conditionals]() to check the boundaries for e For more details, see the [if statements][approach-if-statements] approach. -## Approach: Using a `tuple` and a `loop` +## Approach: Using a `tuple` and a `loop` ```python def score(x_coord, y_coord): throw = x_coord**2 + y_coord**2 - rules = (1, 10), (25, 5), (100, 1), (200, 0) + rules = (1, 10), (25, 5), (100, 1) for distance, points in rules: if throw <= distance: return points + + return 0 ``` @@ -71,34 +73,35 @@ For more details, see the [tuple and loop][approach-tuple-and-loop] approach. ```python def score(x_coord, y_coord): throw = x_coord**2 + y_coord**2 - rules = {1: 10, 25: 5, 100: 1, 200: 0} + rules = {1: 10, 25: 5, 100: 1} - return max(point for distance, point in - rules.items() if throw <= distance) + return max(point for distance, point in + rules.items() if throw <= distance, + default=0) ``` -This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items]. -For more information, see the [dict and generator][approach-dict-and-generator] approach. +This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items]. +For more information, see the [dict and generator][approach-dict-and-generator] approach. ## Approach: Using Boolean Values as Integers ```python def score(x_coord, y_coord): - radius = (x_coord**2 + y_coord**2) - return (radius<=1)*5 + (radius<=25)*4 +(radius<=100)*1 + radius_squared = x_coord**2 + y_coord**2 + return (radius_squared<=1)*5 + (radius_squared<=25)*4 + (radius_squared<=100)*1 ``` This approach exploits the fact that Boolean values are an integer subtype in Python. -For more information, see the [boolean values as integers][approach-boolean-values-as-integers] approach. +For more information, see the [boolean values as integers][approach-booleans-as-ints] approach. ## Approach: Using a `Dictionary` and `dict.get()` ```python def score(x_coord, y_coord): - point = (x_coord**2 + y_coord**2) + point = x_coord**2 + y_coord**2 scores = { point <= 100: 1, point <= 25: 5, @@ -109,17 +112,17 @@ def score(x_coord, y_coord): ``` This approach uses a dictionary to hold the distance --> scoring mappings and `dict.get()` to retrieve the correct points value. -For more details, read the [`Dictionary and dict.get()`][approach-dict-and-dict-get] approach. +For more details, read the [`Dictionary and dict.get()`][approach-dict-and-dict-get] approach. -## Approach: Using `match/case` (structural pattern matching) +## Approach: Using `match/case` (structural pattern matching) ```python from math import hypot, ceil -def score(x, y): - match ceil(hypot(x, y)): +def score(x_coord, y_coord): + match ceil(hypot(x_coord, y_coord)): case 0 | 1: return 10 case 2 | 3 | 4 | 5: return 5 case 6 | 7 | 8 | 9 | 10: return 1 @@ -129,7 +132,7 @@ def score(x, y): This approach uses `Python 3.10`'s structural pattern matching with `return` values on the same line as `case`. A fallthrough case (`_`) is used if the dart throw is outside the outer circle of the target (_greater than 10_). -For more details, see the [structural pattern matching][approach-struct-pattern-matching] approach. +For more details, see the [structural pattern matching][approach-match-case] approach. ## Which approach to use? @@ -137,10 +140,10 @@ For more details, see the [structural pattern matching][approach-struct-pattern- Many of these approaches are a matter of personal preference - there are not significant memory or performance differences. Although a strong argument could be made for simplicity and clarity — many listed solutions (_while interesting_) are harder to reason about or are over-engineered for the current scope of the exercise. -[approach-boolean-values-as-integers]: https://exercism.org/tracks/python/exercises/darts/approaches/boolean-values-as-integers -[approach-dict-and-dict-get]: https://exercism.org/tracks/python/exercises/darts/approaches/dict-and-dict-get +[approach-booleans-as-ints]: https://exercism.org/tracks/python/exercises/darts/approaches/booleans-as-ints +[approach-dict-and-dict-get]: https://exercism.org/tracks/python/exercises/darts/approaches/dict-and-dict-get [approach-dict-and-generator]: https://exercism.org/tracks/python/exercises/darts/approaches/dict-and-generator -[approach-if-statements ]: https://exercism.org/tracks/python/exercises/darts/approaches/if-statements -[approach-struct-pattern-matching]: https://exercism.org/tracks/python/exercises/darts/approaches/struct-pattern-matching -[approach-tuple-and-loop]: https://exercism.org/tracks/python/exercises/darts/approaches/tuple-and-loop +[approach-if-statements ]: https://exercism.org/tracks/python/exercises/darts/approaches/if-statements +[approach-match-case]: https://exercism.org/tracks/python/exercises/darts/approaches/match-case +[approach-tuple-and-loop]: https://exercism.org/tracks/python/exercises/darts/approaches/tuple-and-loop [dict-items]: https://docs.python.org/3/library/stdtypes.html#dict.items diff --git a/exercises/practice/darts/.approaches/match-case/content.md b/exercises/practice/darts/.approaches/match-case/content.md index 04430a5dc5..39bb3c35b8 100644 --- a/exercises/practice/darts/.approaches/match-case/content.md +++ b/exercises/practice/darts/.approaches/match-case/content.md @@ -5,8 +5,8 @@ from math import hypot, ceil -def score(x, y): - throw = ceil(hypot(x, y)) +def score(x_coord, y_coord): + throw = ceil(hypot(x_coord, y_coord)) match throw: case 0 | 1: return 10 @@ -16,8 +16,8 @@ def score(x, y): #OR# -def score(x, y): - match ceil(hypot(x, y)): +def score(x_coord, y_coord): + match ceil(hypot(x_coord, y_coord)): case 0 | 1: return 10 case 2 | 3 | 4 | 5: return 5 case 6 | 7 | 8 | 9 | 10: return 1 @@ -26,16 +26,16 @@ def score(x, y): This approach uses `Python 3.10`'s [`structural pattern matching`][structural-pattern-matching] with `return` values on the same line as `case`. Because the match is numeric, each case explicitly lists allowed values using the `|` (OR) operator. -A fallthrough case (`_`) is used if the dart throw is greater than 10 (_the outer circle radius of the target_). -This is equivalent to using `if-statements` to check throw values although some might argue it is clearer to read. +A fallthrough case (`_`) is used if the dart throw is greater than 10 (_the outer circle radius of the target_). +This is equivalent to using `if-statements` to check throw values, although some might argue it is clearer to read. An `if-statement` equivalent would be: ```python from math import hypot, ceil -def score(x, y): - throw = ceil(hypot(x, y)) +def score(x_coord, y_coord): + throw = ceil(hypot(x_coord, y_coord)) if throw in (0, 1): return 10 if throw in (2, 3, 4, 5): return 5 @@ -51,8 +51,8 @@ One can also use `<`, `>`, or `<=` and `>=` in structural pattern matching, alth from math import hypot, ceil -def score(x, y): - throw = ceil(hypot(x, y)) +def score(x_coord, y_coord): + throw = ceil(hypot(x_coord, y_coord)) match throw: case throw if throw <= 1: return 10 @@ -63,17 +63,17 @@ def score(x, y): Finally, one can use an [assignment expression][assignment-expression] or [walrus operator][walrus] to calculate the throw value rather than calculating and assigning a variable on a separate line. -This isn't necessary (_the first variations shows this clearly_) and might be harder to reason about/understand for some programmers: +This isn't necessary (_the previous variations show this clearly_) and might be harder to reason about/understand for some programmers: ```python from math import hypot, ceil -def score(x, y): - match throw := ceil(hypot(x, y)): +def score(x_coord, y_coord): + match throw := ceil(hypot(x_coord, y_coord)): case throw if throw <= 1: return 10 - case throw if throw <=5: return 5 - case throw if throw <=10: return 1 + case throw if throw <= 5: return 5 + case throw if throw <= 10: return 1 case _: return 0 ``` diff --git a/exercises/practice/darts/.approaches/match-case/snippet.txt b/exercises/practice/darts/.approaches/match-case/snippet.txt index e66b5382b2..564f499949 100644 --- a/exercises/practice/darts/.approaches/match-case/snippet.txt +++ b/exercises/practice/darts/.approaches/match-case/snippet.txt @@ -1,7 +1,7 @@ from math import hypot, ceil -def score(x, y): - match ceil(hypot(x, y)): +def score(x_coord, y_coord): + match ceil(hypot(x_coord, y_coord)): case 0 | 1: return 10 case 2 | 3 | 4 | 5: return 5 case 6 | 7 | 8 | 9 | 10: return 1 diff --git a/exercises/practice/darts/.approaches/tuple-and-loop/content.md b/exercises/practice/darts/.approaches/tuple-and-loop/content.md index 042b9e88ae..ec92b3142e 100644 --- a/exercises/practice/darts/.approaches/tuple-and-loop/content.md +++ b/exercises/practice/darts/.approaches/tuple-and-loop/content.md @@ -3,11 +3,13 @@ ```python def score(x_coord, y_coord): throw = x_coord**2 + y_coord**2 - rules = (1, 10), (25, 5), (100, 1), (200, 0) + rules = (1, 10), (25, 5), (100, 1) for distance, points in rules: if throw <= distance: return points + + return 0 ``` This approach uses a loop to iterate through the _rules_ `tuple`, unpacking each (`distance`, `points`) pair (_For a little more on unpacking, see [Tuple Unpacking Improves Python Code Readability][tuple-unpacking]_). @@ -24,16 +26,18 @@ def score(x_coord, y_coord): return points return 0 - + #OR# def score(x_coord, y_coord): throw = x_coord**2 + y_coord**2 - rules = [(1, 10), (25, 5), (100, 1), (200, 0)] + rules = [(1, 10), (25, 5), (100, 1)] for distance, points in rules: if throw <= distance: return points + + return 0 #OR# @@ -48,8 +52,8 @@ def score(x_coord, y_coord): return 0 ``` -This approach would work nicely in a scenario where you expect to be adding more scoring "rings", since it is cleaner to edit the data structure than to add additional `if-statements` as you would have to in the [`if-statement` approach][approach-if-statements ]. +This approach would work nicely in a scenario where you expect to be adding more scoring "rings", since it is cleaner to edit the data structure than to add additional `if-statements` as you would have to in the [`if-statement` approach][approach-if-statements]. For the three rings as defined by the current exercise, it is a bit over-engineered to use a data structure + `loop`, and results in a slight (_**very** slight_) slowdown over using `if-statements`. [tuple-unpacking]: https://treyhunner.com/2018/03/tuple-unpacking-improves-python-code-readability/#Unpacking_in_a_for_loop -[approach-if-statements ]: https://exercism.org/tracks/python/exercises/darts/approaches/if-statements +[approach-if-statements]: https://exercism.org/tracks/python/exercises/darts/approaches/if-statements diff --git a/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt b/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt index ad50500526..f777f4c04e 100644 --- a/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt +++ b/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt @@ -1,7 +1,8 @@ def score(x_coord, y_coord): - distance = x_coord**2 + y_coord**2 - rules = (1.0, 10), (25.0, 5), (100.0, 1), (200.0, 0) - - for distance, point in rules: - if length <= distance: - return point \ No newline at end of file + throw = x_coord**2 + y_coord**2 + rules = (1, 10), (25, 5), (100, 1) + + for distance, points in rules: + if throw <= distance: + return points + return 0 \ No newline at end of file From 140b712b5799ac35e7d2529db143c6adb3404cc4 Mon Sep 17 00:00:00 2001 From: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> Date: Mon, 18 May 2026 17:54:32 -0400 Subject: [PATCH 40/47] [Sublist] Fix existing approaches & add new ones (#4190) * fix existing approaches * add `manual-loop` approach also move the note about magic values into `introduction.md` and remove unverified statement about performance * add `sort-lists` approach * remove stray enum variables in `introduction.md` * Update `using-strings` warning as suggested in code review Co-authored-by: BethanyG * improve code & add note on W1114 --------- Co-authored-by: BethanyG --- .../practice/sublist/.approaches/config.json | 23 +++- .../sublist/.approaches/introduction.md | 110 ++++++++++++++---- .../.approaches/list-manipulation/content.md | 34 +++--- .../.approaches/manual-loop/content.md | 56 +++++++++ .../.approaches/manual-loop/snippet.txt | 8 ++ .../sublist/.approaches/sort-lists/content.md | 44 +++++++ .../.approaches/sort-lists/snippet.txt | 8 ++ .../.approaches/using-strings/content.md | 43 ++++--- .../.approaches/using-strings/snippet.txt | 4 +- 9 files changed, 268 insertions(+), 62 deletions(-) create mode 100644 exercises/practice/sublist/.approaches/manual-loop/content.md create mode 100644 exercises/practice/sublist/.approaches/manual-loop/snippet.txt create mode 100644 exercises/practice/sublist/.approaches/sort-lists/content.md create mode 100644 exercises/practice/sublist/.approaches/sort-lists/snippet.txt diff --git a/exercises/practice/sublist/.approaches/config.json b/exercises/practice/sublist/.approaches/config.json index ce54db9c14..22ae02518b 100644 --- a/exercises/practice/sublist/.approaches/config.json +++ b/exercises/practice/sublist/.approaches/config.json @@ -1,6 +1,7 @@ { "introduction": { - "authors": ["safwansamsudeen"] + "authors": ["safwansamsudeen"], + "contributors": ["yrahcaz7"] }, "approaches": [ { @@ -8,14 +9,30 @@ "slug": "list-manipulation", "title": "List manipulation", "blurb": "Manipulate and check lists to solve the exercise", - "authors": ["safwansamsudeen"] + "authors": ["safwansamsudeen"], + "contributors": ["yrahcaz7"] }, { "uuid": "61366160-c859-4d16-9085-171428209b8d", "slug": "using-strings", "title": "Using strings", "blurb": "Convert the lists to string and use string manipulation to solve the exercise", - "authors": ["safwansamsudeen"] + "authors": ["safwansamsudeen"], + "contributors": ["yrahcaz7"] + }, + { + "uuid": "b2695c39-c1c7-47f0-bfcd-5e9703674bea", + "slug": "manual-loop", + "title": "Manual looping", + "blurb": "Manually track indexes while looping through the lists to solve the exercise", + "authors": ["yrahcaz7"] + }, + { + "uuid": "a1eeaf9b-a9b3-421e-bfad-44f7e1575450", + "slug": "sort-lists", + "title": "Sorting lists", + "blurb": "Sort the lists to determine the shorter and longer ones to solve the exercise", + "authors": ["yrahcaz7"] } ] } diff --git a/exercises/practice/sublist/.approaches/introduction.md b/exercises/practice/sublist/.approaches/introduction.md index 42f991ef08..13c10b2dba 100644 --- a/exercises/practice/sublist/.approaches/introduction.md +++ b/exercises/practice/sublist/.approaches/introduction.md @@ -1,24 +1,24 @@ # Introduction -There are two broad ways to solve Sublist. + +There are four broad ways to solve Sublist, though one of them ("using strings") is not recommended. ## General guidance -To write the code, you need to branch out (probably with `if`) into the four different possible conditions, and return the appropriate name of the category. -## Approach: list manipulation +To write the code, you need to branch out (probably with `if`) into the four different possible conditions, and return the appropriate category (`SUBLIST`, `SUPERLIST`, `EQUAL`, or `UNEQUAL`). + +Note that you shouldn't return the category's value directly, as that would introduce [magic values][magic-values] into your code. + +## Approach: List manipulation + The direct approach would be to manipulate and check the given lists to solve this. This solution uses a helper function, which simplifies things, but the approach can be implemented without it. ```python -SUBLIST = 1 -SUPERLIST = 2 -EQUAL = 3 -UNEQUAL = 4 - -def check_sub_sequences(list_one, list_two): - n1 = len(list_one) - n2 = len(list_two) - return any(list_two[i:i+n1] == list_one for i in range(n2 - n1 + 1)) - +def check_sub_sequences(list_a, list_b): + len_a = len(list_a) + len_b = len(list_b) + return any(list_b[i : i + len_a] == list_a for i in range(len_b - len_a + 1)) + def sublist(list_one, list_two): if list_one == list_two: return EQUAL @@ -31,29 +31,89 @@ def sublist(list_one, list_two): Read more on the [detail of this approach][approach-list-manipulation]. -## Approach: using strings -Another seemingly clever approach is to convert the lists to strings and then -use the `in` operator to check for sub-sequences. -**However, this does not work.** +## Approach: Manual looping + +This approach uses a helper function that manually loops through the lists to determine if the first list is a sublist of the second one. +This approach is the longest one by far, though it may be more comprehensible to some. + ```python -SUBLIST = 1 -SUPERLIST = 2 -EQUAL = 3 -UNEQUAL = 4 +def check_sub_sequences(list_a, list_b): + len_a, len_b = len(list_a), len(list_b) + index_a, index_b = 0, 0 + next_index_b = 1 + + while index_a < len_a and index_b < len_b: + if list_a[index_a] == list_b[index_b]: + index_a += 1 + else: + index_a, index_b = 0, next_index_b + next_index_b += 1 + index_b += 1 + + if index_a == len_a: + if len_a == len_b: + return EQUAL + return SUBLIST + return UNEQUAL def sublist(list_one, list_two): - list_one_check = (str(list_one).strip("[]") + ",") - list_two_check = (str(list_two).strip("[]") + ",") + result = check_sub_sequences(list_one, list_two) + + if result == UNEQUAL and check_sub_sequences(list_two, list_one) == SUBLIST: + result = SUPERLIST + return result +``` + +Learn more about the [details of this approach here][approach-manual-loop]. + +## Approach: Sorting lists + +This approach uses the `sorted()` function to determine which list is shorter and which is longer. +Knowing this information, one can implement a simplified version of the list manipulation approach. + +```python +def sublist(list_one, list_two): + if list_one == list_two: + return EQUAL + if not list_one: + return SUBLIST + if not list_two: + return SUPERLIST + + shorter, longer = sorted((list_one, list_two), key=len) + + for index in range(len(longer) - len(shorter) + 1): + if longer[index : index + len(shorter)] == shorter: + return SUPERLIST if longer is list_one else SUBLIST + + return UNEQUAL +``` + +Read more on the [detail of this approach][approach-sort-lists]. + +## Approach: Using strings + +Another seemingly clever approach is to convert the lists to strings and then use the `in` operator to check for sub-sequences. +**However, this does not work.** + +```python +def sublist(list_one, list_two): + list_one_check = str(list_one).strip("[]") + "," + list_two_check = str(list_two).strip("[]") + "," if list_one_check == list_two_check: return EQUAL - elif list_one_check in list_two_check: + if list_one_check in list_two_check: return SUBLIST - elif list_two_check in list_one_check: + if list_two_check in list_one_check: return SUPERLIST return UNEQUAL ``` + To understand more about this approach and **why it fails**, [read here][approach-using-strings]. +[magic-values]: https://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad [approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation +[approach-manual-loop]: https://exercism.org/tracks/python/exercises/sublist/approaches/manual-loop +[approach-sort-lists]: https://exercism.org/tracks/python/exercises/sublist/approaches/sort-lists [approach-using-strings]: https://exercism.org/tracks/python/exercises/sublist/approaches/using-strings diff --git a/exercises/practice/sublist/.approaches/list-manipulation/content.md b/exercises/practice/sublist/.approaches/list-manipulation/content.md index ac374b730e..3e68260292 100644 --- a/exercises/practice/sublist/.approaches/list-manipulation/content.md +++ b/exercises/practice/sublist/.approaches/list-manipulation/content.md @@ -1,4 +1,5 @@ # List manipulation + The direct approach would be to manipulate and check the given lists to solve this. This solution uses a helper function, which simplifies things, but the approach can be implemented without it. @@ -8,11 +9,11 @@ SUPERLIST = 2 EQUAL = 3 UNEQUAL = 4 -def check_sub_sequences(list_one, list_two): - n1 = len(list_one) - n2 = len(list_two) - return any(list_two[i:i+n1] == list_one for i in range(n2 - n1 + 1)) - +def check_sub_sequences(list_a, list_b): + len_a = len(list_a) + len_b = len(list_b) + return any(list_b[i : i + len_a] == list_a for i in range(len_b - len_a + 1)) + def sublist(list_one, list_two): if list_one == list_two: return EQUAL @@ -23,16 +24,21 @@ def sublist(list_one, list_two): return UNEQUAL ``` -We first check for equality using the `==` operator, if so, then we return `EQUAL`. -A common way to do this differently would be to return `1` directly, but this is better practice as we [remove magic values][magic values]. +~~~~exercism/note +You might wonder why the lists in the helper function are named `list_a` and `list_b` instead of `list_one` and `list_two`. +This is because if the parameters have the same name, Pylint thinks the parameters are being passed in incorrectly when we call `check_sub_sequences(list_two, list_one)`. +(The exact warning generated is [`W1114 arguments-out-of-order`][w1114].) + +[w1114]: https://pylint.readthedocs.io/en/stable/user_guide/messages/warning/arguments-out-of-order.html +~~~~ -After that we call `check_sub_sequences` passing in `list_one` and `list_two`. -In the helper function, we check if `any` of the possible sub-sequences in `list_two` of length `n1` (the length of the first list) are equal to the first list. -If so, then we conclude that `list_one` is a `SUBLIST` of `list_two`. +In this approach, we first check for equality using the `==` operator, and if the lists are equal, then we return `EQUAL`. +After that, we call `check_sub_sequences()`, passing in `list_one` and `list_two` for the parameters `list_a` and `list_b`. -To find whether `list_one` is a `SUPERLIST` of `list_two`, we just reverse this process - pass in the lists in the opposite order. -Thus, we check if `any` of the possible sub-sequences in `list_one` of length `n2` (the length of the second list) are equal to the second list. +In the helper function, we check if `any` of the possible sub-sequences in `list_b` of length `len_a` (the length of `list_a`) are equal to `list_a`. +If so, then we conclude that `list_a` is a `SUBLIST` of `list_b`. -If none of the above conditions are true, we conclude that the two lists are unequal. +To find whether `list_one` is a `SUPERLIST` of `list_two`, we just reverse this process — pass in the lists in the opposite order. +Thus, we check if `any` of the possible sub-sequences in `list_one` of the length of `list_two` are equal to `list_two`. -[magic values]: https://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad \ No newline at end of file +If none of the above conditions are true, we conclude that the two lists are `UNEQUAL`. diff --git a/exercises/practice/sublist/.approaches/manual-loop/content.md b/exercises/practice/sublist/.approaches/manual-loop/content.md new file mode 100644 index 0000000000..f55e28cc07 --- /dev/null +++ b/exercises/practice/sublist/.approaches/manual-loop/content.md @@ -0,0 +1,56 @@ +# Manual looping + +This approach uses a helper function that manually loops through the lists to determine if the first list is a sublist of the second one. +This approach is the longest one by far, though it may be more comprehensible to some. + +```python +SUBLIST = 1 +SUPERLIST = 2 +EQUAL = 3 +UNEQUAL = 4 + +def check_sub_sequences(list_a, list_b): + len_a, len_b = len(list_a), len(list_b) + index_a, index_b = 0, 0 + next_index_b = 1 + + while index_a < len_a and index_b < len_b: + if list_a[index_a] == list_b[index_b]: + index_a += 1 + else: + index_a, index_b = 0, next_index_b + next_index_b += 1 + index_b += 1 + + if index_a == len_a: + if len_a == len_b: + return EQUAL + return SUBLIST + return UNEQUAL + +def sublist(list_one, list_two): + result = check_sub_sequences(list_one, list_two) + + if result == UNEQUAL and check_sub_sequences(list_two, list_one) == SUBLIST: + result = SUPERLIST + return result +``` + +~~~~exercism/note +You might wonder why the lists in the helper function are named `list_a` and `list_b` instead of `list_one` and `list_two`. +This is because if the parameters have the same name, Pylint thinks the parameters are being passed in incorrectly when we call `check_sub_sequences(list_two, list_one)`. +(The exact warning generated is [`W1114 arguments-out-of-order`][w1114].) + +[w1114]: https://pylint.readthedocs.io/en/stable/user_guide/messages/warning/arguments-out-of-order.html +~~~~ + +In this approach, the first thing `sublist()` does is call the helper function. +That function then loops through the lists, keeping track of an index for both lists so it can test all necessary combinations to determine if `list_one` is a sublist of `list_two`. + +However, the helper function only determines if `list_one` is equal to or a sublist of `list_two`, not if `list_one` is a superlist of `list_two`. +That is why if the helper function returns `UNEQUAL`, `sublist()` needs to make sure that it isn't acutally a superlist. + +`sublist()` does this by calling the helper function with its arguments reversed: `check_sub_sequences(list_two, list_one)`. +If the result is `SUBLIST`, that means `list_two` is a sublist of `list_one`, thus `list_one` must be a superlist of `list_two`. + +Thus all possibilities are covered, and `sublist()` returns the result. diff --git a/exercises/practice/sublist/.approaches/manual-loop/snippet.txt b/exercises/practice/sublist/.approaches/manual-loop/snippet.txt new file mode 100644 index 0000000000..081ed4aae1 --- /dev/null +++ b/exercises/practice/sublist/.approaches/manual-loop/snippet.txt @@ -0,0 +1,8 @@ +while index_one < len(list_one) and index_two < len(list_two): + if list_one[index_one] == list_two[index_two]: + index_one += 1 + else: + index_one = 0 + index_two = next_index_two + next_index_two += 1 + index_two += 1 \ No newline at end of file diff --git a/exercises/practice/sublist/.approaches/sort-lists/content.md b/exercises/practice/sublist/.approaches/sort-lists/content.md new file mode 100644 index 0000000000..ede91c1fac --- /dev/null +++ b/exercises/practice/sublist/.approaches/sort-lists/content.md @@ -0,0 +1,44 @@ +# Sorting lists + +This approach uses the `sorted()` function to determine which list is shorter and which is longer. +Knowing this information, one can implement a simplified version of the [list manipulation approach][approach-list-manipulation]. + +```python +SUBLIST = 1 +SUPERLIST = 2 +EQUAL = 3 +UNEQUAL = 4 + +def sublist(list_one, list_two): + if list_one == list_two: + return EQUAL + if not list_one: + return SUBLIST + if not list_two: + return SUPERLIST + + shorter, longer = sorted((list_one, list_two), key=len) + + for index in range(len(longer) - len(shorter) + 1): + if longer[index : index + len(shorter)] == shorter: + return SUPERLIST if longer is list_one else SUBLIST + + return UNEQUAL +``` + +Here, the case of the lists being equal is checked first. +Then the special cases of empty lists are handled, returning `SUBLIST` or `SUPERLIST` as necessary. + +Once those simple cases are out of the way, the `sorted()` function is used with the keyword argument `key` set to the `len()` function. +This makes `sorted()` sort the items according to their length. + +Once `sorted()` does its work, we use multiple assignment to unpack the results into the `shorter` and `longer` variables. +Then, for each slice of length `len(shorter)` in `longer`, we test if that slice is equal to `shorter`. + +If we find such a slice, that means `shorter` is a sublist of `longer`. +Then we use a [conditional expression][conditional-expression] along with the `is` operator to return `SUBLIST` or `SUPERLIST` depending on which of the original lists is `longer`. + +If we do not find such a slice, we can eliminate `SUBLIST` and `SUPERLIST` from the possible categories, thus the two lists must be `UNEQUAL`. + +[approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation +[conditional-expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions diff --git a/exercises/practice/sublist/.approaches/sort-lists/snippet.txt b/exercises/practice/sublist/.approaches/sort-lists/snippet.txt new file mode 100644 index 0000000000..f106f76255 --- /dev/null +++ b/exercises/practice/sublist/.approaches/sort-lists/snippet.txt @@ -0,0 +1,8 @@ +def sublist(list_one, list_two): + ... + shorter, longer = sorted((list_one, list_two), key=len) + + for index in range(len(longer) - len(shorter) + 1): + if longer[index : index + len(shorter)] == shorter: + return SUPERLIST if longer is list_one else SUBLIST + return UNEQUAL \ No newline at end of file diff --git a/exercises/practice/sublist/.approaches/using-strings/content.md b/exercises/practice/sublist/.approaches/using-strings/content.md index ff960902dc..60c49d168b 100644 --- a/exercises/practice/sublist/.approaches/using-strings/content.md +++ b/exercises/practice/sublist/.approaches/using-strings/content.md @@ -1,13 +1,12 @@ # Using strings + ~~~~exercism/caution -**This approach does not work, and this document exists to explain that.** +**This approach does not work (_it will not generalize to all cases_), and this document exists to explain that.** Please do not use it in your code. ~~~~ -Another seemingly clever solution is to convert the lists to strings and then -use the `in` operator to check for sub-sequences. -Note that this approach, even if it worked, is not as performant as the -previous one. +Another seemingly clever solution is to convert the lists to strings and then use the `in` operator to check for sub-sequences. + ```python SUBLIST = 1 SUPERLIST = 2 @@ -20,28 +19,36 @@ def sublist(list_one, list_two): if list_one_check == list_two_check: return EQUAL - elif list_one_check in list_two_check: + if list_one_check in list_two_check: return SUBLIST - elif list_two_check in list_one_check: + if list_two_check in list_one_check: return SUPERLIST return UNEQUAL ``` + Let's parse the code to see what it does. -In this approach, we convert the lists to strings, so `[1, 2, 3]` becomes `"[1, 2, 3]"`, remove the brackets `"1, 2, 3"`, and add a comma `"1, 2, 3,"`. +In this approach, we convert the lists to strings, so `[1, 2, 3]` becomes `"[1, 2, 3]"`, remove the brackets `"1, 2, 3"`, and add a comma `"1, 2, 3,"`. We check equality and then use the `in` operator to check for `SUBLIST` or `SUPERLIST`, and finally return `UNEQUAL`. -We add a comma because, say, we call `sublist` with `[1, 2]` and `[1, 22]`. `"1, 2" in "1, 22"` evaluates to `True`, so -the **function would wrongly mark it as `SUBLIST`**. +We add a comma because, say, we call `sublist` with `[1, 2]` and `[1, 22]`. `"1, 2" in "1, 22"` evaluates to `True`, so the **function would wrongly mark it as `SUBLIST`**. + +This case can be handled by changing the code like this: + +```python +list_one_check = str(list_one).strip("[]") + "," +list_two_check = str(list_two).strip("[]") + "," +``` + +Yet, even though this code would pass all of the tests in the Exercism test suite, it would still fail in some cases. +For example, if we call `sublist` with `[1, 2]` and `[5, "1, 2,", 7]`, the function would return `SUBLIST` when it should actually return `UNEQUAL`. + +This could be avoided by changing the code to use a separator that isn't the default one: -This test can be overridden by changing the code like this: ```python -list_one_check = str(list_one).strip("[]") + ',' -list_two_check = str(list_two).strip("[]") + ',' +list_one_check = "|".join(str(item) for item in list_one) + "|" +list_two_check = "|".join(str(item) for item in list_two) + "|" ``` -Yet, the test case (which doesn't exist in the Exercism test suite) `["1", "2"]` and `["5", "'1', '2',", "7"]` would -fail. -Students can add any arbitrary string into the representation to try to "defeat" this test - `list_one_check = str -(list_one) + TOKEN`. The test suite currently test `TOKEN = ''`, but not others. +However, this only avoids the (theoretical) test and does not fix the solution. For example, a test with the inputs `[1, 2]` and `[5, "1|2|", 7]` would now fail. -[gen-exp]: https://www.programiz.com/python-programming/generator \ No newline at end of file +No matter what separator is chosen, there will always be at least one input for which the function will return the wrong result. **This is why no approach that converts the lists to strings can ever be correct for all possible inputs.** diff --git a/exercises/practice/sublist/.approaches/using-strings/snippet.txt b/exercises/practice/sublist/.approaches/using-strings/snippet.txt index 26fc3ec0ec..4d4b643929 100644 --- a/exercises/practice/sublist/.approaches/using-strings/snippet.txt +++ b/exercises/practice/sublist/.approaches/using-strings/snippet.txt @@ -1,8 +1,8 @@ -# Failing approach +# WARNING: Failing approach def sublist(list_one, list_two): list_one_check = str(list_one).strip("[]") ... - elif list_one_check in list_two_check: + if list_one_check in list_two_check: return SUBLIST ... return UNEQUAL \ No newline at end of file From 79149c321dbfc21bffffb3c586dd346f99e8c2e4 Mon Sep 17 00:00:00 2001 From: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> Date: Tue, 19 May 2026 18:36:11 -0400 Subject: [PATCH 41/47] RNA Transcription and Atbash Cipher approach cleanup (#4191) * [Atbash Cipher] Greatly improve approaches * [RNA Transcription] Typo fix & grammar fix * Apply suggestions from code review Co-authored-by: BethanyG * fix characters vs code points --------- Co-authored-by: BethanyG --- .../atbash-cipher/.approaches/config.json | 11 ++-- .../atbash-cipher/.approaches/introduction.md | 44 ++++++++------ .../.approaches/mono-function/content.md | 58 +++++++++++-------- .../.approaches/mono-function/snippet.txt | 8 +-- .../.approaches/separate-functions/content.md | 42 +++++++++----- .../separate-functions/snippet.txt | 4 +- .../rna-transcription/.approaches/config.json | 6 +- .../.approaches/dictionary-join/content.md | 3 +- .../.approaches/introduction.md | 2 - .../translate-maketrans/content.md | 3 +- 10 files changed, 106 insertions(+), 75 deletions(-) diff --git a/exercises/practice/atbash-cipher/.approaches/config.json b/exercises/practice/atbash-cipher/.approaches/config.json index ed1edeb506..dc57da36ca 100644 --- a/exercises/practice/atbash-cipher/.approaches/config.json +++ b/exercises/practice/atbash-cipher/.approaches/config.json @@ -1,6 +1,7 @@ { "introduction": { - "authors": ["safwansamsudeen"] + "authors": ["safwansamsudeen"], + "contributors": ["yrahcaz7"] }, "approaches": [ { @@ -8,14 +9,16 @@ "slug": "mono-function", "title": "Mono-function", "blurb": "Use one function for both tasks", - "authors": ["safwansamsudeen"] + "authors": ["safwansamsudeen"], + "contributors": ["yrahcaz7"] }, { "uuid": "9a7a17e0-4ad6-4d97-a8b9-c74d47f3e000", "slug": "separate-functions", - "title": "Separate Functions", + "title": "Separate functions", "blurb": "Use separate functions, and perhaps helper ones", - "authors": ["safwansamsudeen"] + "authors": ["safwansamsudeen"], + "contributors": ["yrahcaz7"] } ] } diff --git a/exercises/practice/atbash-cipher/.approaches/introduction.md b/exercises/practice/atbash-cipher/.approaches/introduction.md index 6c7180eff9..ce9786409a 100644 --- a/exercises/practice/atbash-cipher/.approaches/introduction.md +++ b/exercises/practice/atbash-cipher/.approaches/introduction.md @@ -1,44 +1,54 @@ # Introduction + Atbash cipher in Python can be solved in many ways. ## General guidance -The first thing is to have a "key" mapping - possibly in a `dict` or `str.maketrans`, otherwise the value would have to be calculated on the fly. -Then, you have to "clean" up the string to be encoded by removing numbers/whitespace. + +The first thing is to have a "key" mapping — possibly in a `dict` or `str.maketrans()`, otherwise the value would have to be calculated on the fly. +Next, you have to "clean" up the string to be encoded by removing punctuation/whitespace. Finally, you break it up into chunks of five before returning it. -For decoding, it's similar - clean up (which automatically joins the chunks) and translate using the _same_ key - the realization that the same key can be used is crucial in solving this in an idiomatic manner. +For decoding, the process is similar — clean up (_which automatically joins the chunks_) and translate using the **_same_** key — realizing that the same key can be used is crucial in solving this in an idiomatic manner. + +## Approach: Separate functions + +We use `str.maketrans()` to create the encoding. +In `encode()`, we use a [generator expression][generator-expression] in `str.join()`. -## Approach: separate functions -We use `str.maketrans` to create the encoding. -In `encode`, we use a [generator expression][generator-expression] in `str.join`. ```python from string import ascii_lowercase + ENCODING = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) -def encode(text: str): +def encode(text): res = "".join(chr for chr in text.lower() if chr.isalnum()).translate(ENCODING) return " ".join(res[index:index+5] for index in range(0, len(res), 5)) -def decode(text: str): - return "".join(chr.lower() for chr in text if chr.isalnum()).translate(ENCODING) +def decode(text): + return "".join(chr.lower() for chr in text if not chr.isspace()).translate(ENCODING) ``` + Read more on this [approach here][approach-separate-functions]. -## Approach: mono-function -Notice that there the majority of the code is repetitive? -A fun way to solve this would be to keep it all inside the `encode` function, and merely chunk it if `decode` is False: -For variation, this approach shows a different way to translate the text. +## Approach: Mono-function + +Notice that the majority of the code is repetitive? +A fun way to solve this would be to keep it all inside the `encode()` function, and merely chunk it if `decode` is `False`: +For variation, this approach also shows a different way to translate the text. + ```python from string import ascii_lowercase as asc_low + ENCODING = {chr: asc_low[id] for id, chr in enumerate(asc_low[::-1])} -def encode(text: str, decode: bool = False): - res = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) - return res if decode else " ".join(res[index:index+5] for index in range(0, len(res), 5)) +def encode(text, decode = False): + line = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) + return line if decode else " ".join(line[index:index+5] for index in range(0, len(line), 5)) -def decode(text: str): +def decode(text): return encode(text, True) ``` + For more detail, [read here][approach-mono-function]. [approach-separate-functions]: https://exercism.org/tracks/python/exercises/atbash-cipher/approaches/separate-functions diff --git a/exercises/practice/atbash-cipher/.approaches/mono-function/content.md b/exercises/practice/atbash-cipher/.approaches/mono-function/content.md index 879664ce20..0c0da8e42a 100644 --- a/exercises/practice/atbash-cipher/.approaches/mono-function/content.md +++ b/exercises/practice/atbash-cipher/.approaches/mono-function/content.md @@ -1,46 +1,54 @@ -## Approach: Mono-function -Notice that there the majority of the code is repetitive? -A fun way to solve this would be to keep it all inside the `encode` function, and merely chunk it if `decode` is False: -For variation, this approach shows a different way to translate the text. +# Approach: Mono-function + +Notice that the majority of the code is repetitive? +A fun way to solve this would be to keep it all inside the `encode()` function, and merely chunk it if `decode` is `False`: +For variation, this approach also shows a different way to translate the text. + ```python from string import ascii_lowercase as asc_low + ENCODING = {chr: asc_low[id] for id, chr in enumerate(asc_low[::-1])} -def encode(text: str, decode: bool = False): - res = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) - return res if decode else " ".join(res[index:index+5] for index in range(0, len(res), 5)) +def encode(text, decode = False): + line = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) + return line if decode else " ".join(line[index:index+5] for index in range(0, len(line), 5)) -def decode(text: str): +def decode(text): return encode(text, True) ``` -To explain the translation: we use a `dict` comprehension in which we reverse the ASCII lowercase digits, and enumerate through them - that is, `z` is 0, `y` is 1, and so on. -We access the character at that index and set it to the value of `c` - so `z` translates to `a`. -In the calculation of the result, we try to obtain the value of the character using `dict.get`, which accepts a default parameter. -In this case, the character itself is the default - that is, numbers won't be found in the translation key, and thus should remain as numbers. +Here, we use a dictionary comprehension in which we reverse the order of the ASCII lowercase digits and enumerate through them — that is, `z` is at index 0, `y` is at index 1, and so on. +For each code point, we set the value of `chr` in the resulting dictionary to the code point at the respective index — so `z` translates to `a`. + +In the calculation of the result, we try to obtain the value of the code point using `dict.get()`, which accepts a default parameter. +In this case, the code point itself is the default — that is, numbers won't be found in the translation key, and thus should remain as numbers. + +We use a [conditional expression (also known as a ternary operator)][conditional-expression] to check if we actually mean to decode the function, in which case we return the result as is. +If not, we "chunk" the result by joining every five code points with a space. -We use a [ternary operator][ternary-operator] to check if we actually mean to decode the function, in which case we return the result as is. -If not, we chunk the result by joining every five characters with a space. +Another possible way to solve this would be to use a function that returns another function (_a higher-order function or [closure][closure]_) that encodes or decodes based on the outer function's parameter: -Another possible way to solve this would be to use a function that returns a function that encodes or decodes based on the parameters: ```python -from string import ascii_lowercase as alc +from string import ascii_lowercase as asc_low -lowercase = {chr: alc[id] for id, chr in enumerate(alc[::-1])} +ENCODING = {chr: asc_low[id] for id, chr in enumerate(asc_low[::-1])} -def code(decode=False): +def code(decode = False): def func(text): - line = "".join(lowercase.get(chr, chr) for chr in text.lower() if chr.isalnum()) + line = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) return line if decode else " ".join(line[index:index+5] for index in range(0, len(line), 5)) return func - encode = code() decode = code(True) ``` -The logic is the same - we've instead used one function that generates two _other_ functions based on the boolean value of its parameter. -`encode` is set to the function that's returned, and performs encoding. -`decode` is set a function that _decodes_. -[ternary-operator]: https://www.tutorialspoint.com/ternary-operator-in-python -[decorator]: https://realpython.com/primer-on-python-decorators/ \ No newline at end of file +The logic is the same — the only change is that now we use use one function that generates two _other_ functions based on the boolean value of its parameter. + +Here, we first call `code()` with no argument and set `encode` to the function that's returned, which performs encoding. +Then we call `code(True)` to get the decoding version of the function and set `decode` to that function. + +After that, we can call `encode()` and `decode()` as normal, and both functions successfully perform their indended task. + +[closure]: https://realpython.com/python-closure/ +[conditional-expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions diff --git a/exercises/practice/atbash-cipher/.approaches/mono-function/snippet.txt b/exercises/practice/atbash-cipher/.approaches/mono-function/snippet.txt index 84e8b79300..24ba495094 100644 --- a/exercises/practice/atbash-cipher/.approaches/mono-function/snippet.txt +++ b/exercises/practice/atbash-cipher/.approaches/mono-function/snippet.txt @@ -1,8 +1,8 @@ from string import ascii_lowercase as asc_low ENCODING = {chr: asc_low[id] for id, chr in enumerate(asc_low[::-1])} -def encode(text: str, decode: bool = False): - res = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) - return res if decode else " ".join(res[index:index+5] for index in range(0, len(res), 5)) -def decode(text: str): +def encode(text, decode = False): + line = "".join(ENCODING.get(chr, chr) for chr in text.lower() if chr.isalnum()) + return line if decode else " ".join(line[index:index+5] for index in range(0, len(line), 5)) +def decode(text): return encode(text, True) \ No newline at end of file diff --git a/exercises/practice/atbash-cipher/.approaches/separate-functions/content.md b/exercises/practice/atbash-cipher/.approaches/separate-functions/content.md index 60e02a2205..1890625819 100644 --- a/exercises/practice/atbash-cipher/.approaches/separate-functions/content.md +++ b/exercises/practice/atbash-cipher/.approaches/separate-functions/content.md @@ -1,45 +1,57 @@ -## Approach: Separate Functions -We use `str.maketrans` to create the encoding. -`.maketrans`/`.translate` is extremely fast compared to other methods of translation. -If you're interested, [read more][str-maketrans] about it. +# Approach: Separate functions + +We use `str.maketrans()` to create the encoding. +`str.maketrans()`/`str.translate()` is extremely fast compared to other methods of translation. +If you're interested, you can [read more about it here][str-maketrans]. + +In `encode()`, we use a [generator expression][generator-expression] in `str.join()`, which is more efficient — and neater — than a list comprehension. -In `encode`, we use a [generator expression][generator-expression] in `str.join`, which is more efficient - and neater - than a list comprehension. ```python from string import ascii_lowercase + ENCODING = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) -def encode(text: str): +def encode(text): res = "".join(chr for chr in text.lower() if chr.isalnum()).translate(ENCODING) return " ".join(res[index:index+5] for index in range(0, len(res), 5)) -def decode(text: str): - return "".join(chr.lower() for chr in text if chr.isalnum()).translate(ENCODING) +def decode(text): + return "".join(chr.lower() for chr in text if not chr.isspace()).translate(ENCODING) ``` -In `encode`, we first join together every character if the character is alphanumeric - as we use `text.lower()`, the characters are all lowercase as needed. -Then, we translate it and return a version joining every five characters with a space in between. -`decode` does the exact same thing, except it doesn't return a chunked output. -Instead of cleaning the input by checking that it's alphanumeric, we check that it's not a whitespace character. +In `encode()`, we first join together every code point that is an alphanumeric character — as we use `text.lower()`, the characters are all lowercase as needed. +Then, we translate it and return a version joining every five code points with a space in between. + +`decode()` does the exact same thing, except it doesn't return a chunked output and it cleans the input differently. +To clean the input, `decode()` only removes code points that are whitespace characters instead of all non-alphanumeric characters. It might be cleaner to use helper functions: + ```python from string import ascii_lowercase + ENCODING = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) + + def clean(text): return "".join([chr.lower() for chr in text if chr.isalnum()]) + def chunk(text): return " ".join(text[index:index+5] for index in range(0, len(text), 5)) + def encode(text): return chunk(clean(text).translate(ENCODING)) def decode(text): return clean(text).translate(ENCODING) ``` -Note that checking that `chr` _is_ alphanumeric achieves the same result as checking that it's _not_ whitespace, although it's not as explicit. + +Note that for `decode()`, checking that `chr` _is_ alphanumeric achieves the same result as checking that it _is not_ whitespace, although it's not as explicit. As this is a helper function, this is acceptable enough. -You can also make `chunk` recursive: +You can also make `chunk()` recursive, but this is not recommended: + ```python def chunk(text): if len(text) <= 5: @@ -48,4 +60,4 @@ def chunk(text): ``` [generator-expression]: https://www.programiz.com/python-programming/generator -[str-maketrans]: https://www.programiz.com/python-programming/methods/string/maketrans \ No newline at end of file +[str-maketrans]: https://www.programiz.com/python-programming/methods/string/maketrans diff --git a/exercises/practice/atbash-cipher/.approaches/separate-functions/snippet.txt b/exercises/practice/atbash-cipher/.approaches/separate-functions/snippet.txt index fbfe0b75fa..f57a8dda72 100644 --- a/exercises/practice/atbash-cipher/.approaches/separate-functions/snippet.txt +++ b/exercises/practice/atbash-cipher/.approaches/separate-functions/snippet.txt @@ -1,8 +1,8 @@ from string import ascii_lowercase ENCODING = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) -def encode(text: str): +def encode(text): res = "".join(chr for chr in text.lower() if chr.isalnum()).translate(ENCODING) return " ".join(res[index:index+5] for index in range(0, len(res), 5)) -def decode(text: str): +def decode(text): return "".join(chr.lower() for chr in text if not chr.isspace()).translate(ENCODING) \ No newline at end of file diff --git a/exercises/practice/rna-transcription/.approaches/config.json b/exercises/practice/rna-transcription/.approaches/config.json index 9ab4114548..7ec0363a3f 100644 --- a/exercises/practice/rna-transcription/.approaches/config.json +++ b/exercises/practice/rna-transcription/.approaches/config.json @@ -9,14 +9,16 @@ "slug": "translate-maketrans", "title": "translate maketrans", "blurb": "Use translate with maketrans to return the value.", - "authors": ["bobahop"] + "authors": ["bobahop"], + "contributors": ["yrahcaz7"] }, { "uuid": "fbc6be87-dec4-4c4b-84cf-fcc1ed2d6d41", "slug": "dictionary-join", "title": "dictionary join", "blurb": "Use a dictionary look-up with join to return the value.", - "authors": ["bobahop"] + "authors": ["bobahop"], + "contributors": ["yrahcaz7"] } ] } diff --git a/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md b/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md index fcf0c58953..ead5254ad9 100644 --- a/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md +++ b/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md @@ -6,7 +6,6 @@ LOOKUP = {'G': 'C', 'C': 'G', 'T': 'A', 'A': 'U'} def to_rna(dna_strand): return ''.join(LOOKUP[nucleotide] for nucleotide in dna_strand) - ``` This approach starts by defining a [dictionary][dictionaries] to map the DNA values to RNA values. @@ -18,7 +17,7 @@ It indicates that the value is not intended to be changed. In the `to_rna()` function, the [`join()`][join] method is called on an empty string, and is passed the list created from a [generator expression][generator-expression]. -The generator expression iterates each character in the input, +The generator expression iterates over each code point in the input, looks up the DNA character in the look-up dictionary, and outputs its matching RNA character as an element in the list. The `join()` method collects the RNA characters back into a string. diff --git a/exercises/practice/rna-transcription/.approaches/introduction.md b/exercises/practice/rna-transcription/.approaches/introduction.md index 54b4c1f7d3..032532946e 100644 --- a/exercises/practice/rna-transcription/.approaches/introduction.md +++ b/exercises/practice/rna-transcription/.approaches/introduction.md @@ -18,7 +18,6 @@ LOOKUP = str.maketrans('GCTA', 'CGAU') def to_rna(dna_strand): return dna_strand.translate(LOOKUP) - ``` For more information, check the [`translate()` with `maketrans()` approach][approach-translate-maketrans]. @@ -31,7 +30,6 @@ LOOKUP = {'G': 'C', 'C': 'G', 'T': 'A', 'A': 'U'} def to_rna(dna_strand): return ''.join(LOOKUP[nucleotide] for nucleotide in dna_strand) - ``` For more information, check the [dictionary look-up with `join()` approach][approach-dictionary-join]. diff --git a/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md b/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md index 9373cf12b2..374cadd65e 100644 --- a/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md +++ b/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md @@ -6,7 +6,6 @@ LOOKUP = str.maketrans('GCTA', 'CGAU') def to_rna(dna_strand): return dna_strand.translate(LOOKUP) - ``` This approach starts by defining a [dictionary][dictionaries] (also called a translation table in this context) by calling the [`maketrans()`][maketrans] method. @@ -18,7 +17,7 @@ It indicates that the value is not intended to be changed. The translation table that is created uses the [Unicode][Unicode] _code points_ (sometimes called the ordinal values) for each letter in the two strings. As Unicode was designed to be backwards compatible with [ASCII][ASCII] and because the exercise uses Latin letters, the code points in the translation table can be interpreted as ASCII. However, the functions can deal with any Unicode character. -You can learn more by reading about [strings and their representation in the Exercism Python syllabus][concept-string]. +You can learn more by reading about [strings and their representation in the Exercism Python syllabus][concept-strings]. The Unicode value for "G" in the first string is the key for the Unicode value of "C" in the second string, and so on. From 088a89a9134caeb96691cef253fbd3c5e3f720f7 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Wed, 20 May 2026 14:41:29 -0700 Subject: [PATCH 42/47] Update README.md (#4193) Changed supported Python versions in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 38ebda1d34..20c3bd1ce0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

Exercism Python Track

                          [![Discourse topics](https://img.shields.io/discourse/topics?color=8A08E6&label=Connect%20&labelColor=FFDF58&logo=Discourse&logoColor=8A08E6&server=https%3A%2F%2Fforum.exercism.org&style=social)](https://forum.exercism.org) -  [![Exercism_II](https://img.shields.io/badge/Exercism--Built-9101FF?logo=python&logoColor=FFDF58&labelColor=3D7AAB&label=Python%203.13.5%20Powered)](https://exercism.org) +  [![Exercism_II](https://img.shields.io/badge/Exercism--Built-9101FF?logo=python&logoColor=FFDF58&labelColor=3D7AAB&label=Python%203.13%20Powered)](https://exercism.org)   [![Exercism_III](https://img.shields.io/badge/PAUSED-C73D4E?labelColor=3D454D&label=Contributions)](https://exercism.org/blog/freeing-our-maintainers)   [![Build Status](https://github.com/exercism/python/workflows/Exercises%20check/badge.svg)](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. From 43438ca86e3e3be3a022f75575ca3f2cb99e4cfe Mon Sep 17 00:00:00 2001 From: BethanyG Date: Wed, 20 May 2026 17:55:19 -0700 Subject: [PATCH 43/47] [Bitwise Operations and Complex Numbers Concepts]: Fixed Typos & Grammar (#4194) * Fixed typos and grammar for bitwise and complex number concepts. * Applied fixes from code review. --- concepts/bitwise-operators/about.md | 14 +++++++------- concepts/bitwise-operators/introduction.md | 6 +++--- concepts/bitwise-operators/links.json | 4 ---- concepts/complex-numbers/about.md | 12 ++++++------ concepts/complex-numbers/introduction.md | 8 +++++--- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/concepts/bitwise-operators/about.md b/concepts/bitwise-operators/about.md index a68e5378f1..1cd5a237c2 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 88aba3a6a7..07833339ff 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 7c103c8463..ed251fab33 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/complex-numbers/about.md b/concepts/complex-numbers/about.md index dfe067be4e..2b0de864e7 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 a82f47cb6c..419c3f3d48 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 From b1bae5ecc2ea5578f8d60c2f43510854a0ac6434 Mon Sep 17 00:00:00 2001 From: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> Date: Wed, 20 May 2026 21:31:02 -0400 Subject: [PATCH 44/47] Remove unfinished `functools` concept (#4196) It should be reworked and re-added at a later date. --- concepts/functools/about.md | 313 +---------------------------- concepts/functools/introduction.md | 44 +--- 2 files changed, 2 insertions(+), 355 deletions(-) diff --git a/concepts/functools/about.md b/concepts/functools/about.md index e5afb577d3..cbc5cd89d9 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 c91aedc81b..bbe12ffd5e 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. From a204332d2c636f2db50ebf5a8d91f9222240b340 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Wed, 20 May 2026 18:43:53 -0700 Subject: [PATCH 45/47] [Enums and Fractions Concepts]: Fix Typos & Grammar Issues as outlined in issue 4165. (#4195) * Fix typos and grammer issues as outlined in issue 4165. * Stray backticks missing. * Applied fixes from code review. --- concepts/enums/about.md | 48 ++++++++++++++++++++---------- concepts/enums/introduction.md | 20 +++++++++---- concepts/fractions/about.md | 10 +++---- concepts/fractions/introduction.md | 10 +++---- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/concepts/enums/about.md b/concepts/enums/about.md index 27b264c22e..dacd7bfd75 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 ea9c9000e0..bf4e8b9d04 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 d41124c39c..e582c53141 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 437ccbbeb0..156aa3ff28 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') From 323ada8c36e592318485535e761d031dcda4af4f Mon Sep 17 00:00:00 2001 From: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> Date: Thu, 21 May 2026 22:07:18 -0400 Subject: [PATCH 46/47] [Wordy] Clean up Approaches (#4198) * `string-list-and-dict-methods` cleanup also some formatting fixes * `import-callables-from-operator` cleanup also more formatting fixes * `regex-with-operator-module` cleanup * `lambdas-in-a-dictionary` cleanup * `recursion` approach cleanup * add warning about `recursion` variation 2 * clean up the rest of the approaches * fix minor errors in approaches * Apply suggestion from code review Co-authored-by: BethanyG --- .../practice/wordy/.approaches/config.json | 61 +++--- .../dunder-getattribute/content.md | 54 ++--- .../dunder-getattribute/snippet.txt | 2 +- .../.approaches/functools-reduce/content.md | 90 ++++---- .../.approaches/functools-reduce/snippet.txt | 4 +- .../import-callables-from-operator/content.md | 32 ++- .../snippet.txt | 2 +- .../wordy/.approaches/introduction.md | 199 ++++++++---------- .../lambdas-in-a-dictionary/content.md | 44 ++-- .../lambdas-in-a-dictionary/snippet.txt | 10 +- .../wordy/.approaches/recursion/content.md | 188 +++++++++-------- .../wordy/.approaches/recursion/snippet.txt | 12 +- .../regex-with-operator-module/content.md | 63 +++--- .../regex-with-operator-module/snippet.txt | 2 +- .../string-list-and-dict-methods/content.md | 104 +++++---- .../string-list-and-dict-methods/snippet.txt | 8 +- .../wordy/.docs/instructions.append.md | 6 +- 17 files changed, 434 insertions(+), 447 deletions(-) diff --git a/exercises/practice/wordy/.approaches/config.json b/exercises/practice/wordy/.approaches/config.json index 670284d471..957fc960bf 100644 --- a/exercises/practice/wordy/.approaches/config.json +++ b/exercises/practice/wordy/.approaches/config.json @@ -1,7 +1,7 @@ { "introduction": { "authors": ["BethanyG"], - "contributors": ["bobahop"] + "contributors": ["bobahop", "yrahcaz7"] }, "approaches": [ { @@ -9,49 +9,56 @@ "slug": "string-list-and-dict-methods", "title": "String, List, and Dictionary Methods", "blurb": "Use Core Python Features to Solve Word Problems.", - "authors": ["BethanyG"] + "authors": ["BethanyG"], + "contributors": ["yrahcaz7"] }, - { - "uuid": "d3ff485a-defe-42d9-b9c6-c38019221ffa", + { + "uuid": "d3ff485a-defe-42d9-b9c6-c38019221ffa", "slug": "import-callables-from-operator", "title": "Import Callables from the Operator Module", "blurb": "Use Operator Module Methods to Solve Word Problems.", - "authors": ["BethanyG"] - }, - { - "uuid": "61f44943-8a12-471b-ab15-d0d10fa4f72f", + "authors": ["BethanyG"], + "contributors": ["yrahcaz7"] + }, + { + "uuid": "61f44943-8a12-471b-ab15-d0d10fa4f72f", "slug": "regex-with-operator-module", "title": "Regex with the Operator Module", "blurb": "Use Regex with the Callables from Operator to solve word problems.", - "authors": ["BethanyG"] - }, - { - "uuid": "46bd15dd-cae4-4eb3-ac63-a8b631a508d1", + "authors": ["BethanyG"], + "contributors": ["yrahcaz7"] + }, + { + "uuid": "46bd15dd-cae4-4eb3-ac63-a8b631a508d1", "slug": "lambdas-in-a-dictionary", "title": "Lambdas in a Dictionary to Return Functions", "blurb": "Use lambdas in a dictionary to return functions for solving word problems.", - "authors": ["BethanyG"] - }, - { - "uuid": "2e643b88-9b76-45a1-98f4-b211919af061", + "authors": ["BethanyG"], + "contributors": ["yrahcaz7"] + }, + { + "uuid": "2e643b88-9b76-45a1-98f4-b211919af061", "slug": "recursion", - "title": "Recursion for Iteration.", + "title": "Recursion for Iteration", "blurb": "Use recursion with other strategies to solve word problems.", - "authors": ["BethanyG"] - }, - { - "uuid": "1e136304-959c-4ad1-bc4a-450d13e5f668", + "authors": ["BethanyG"], + "contributors": ["yrahcaz7"] + }, + { + "uuid": "1e136304-959c-4ad1-bc4a-450d13e5f668", "slug": "functools-reduce", - "title": "Functools.reduce for Calculation", + "title": "functools.reduce for Calculation", "blurb": "Use functools.reduce with other strategies to calculate solutions.", - "authors": ["BethanyG"] - }, - { + "authors": ["BethanyG"], + "contributors": ["yrahcaz7"] + }, + { "uuid": "d643e2b4-daee-422d-b8d3-2cad2f439db5", "slug": "dunder-getattribute", - "title": "dunder with __getattribute__", + "title": "Dunder with __getattribute__", "blurb": "Use dunder methods with __getattribute__.", - "authors": ["bobahop"] + "authors": ["bobahop"], + "contributors": ["yrahcaz7"] } ] } diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md index 167460f2d3..26652cc693 100644 --- a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md +++ b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md @@ -1,6 +1,5 @@ # Dunder methods with `__getattribute__` - ```python OPS = { "plus": "__add__", @@ -12,11 +11,12 @@ OPS = { def answer(question): question = question.removeprefix("What is").removesuffix("?").strip() - if not question: raise ValueError("syntax error") + if not question: + raise ValueError("syntax error") if question.startswith("-") and question[1:].isdigit(): return -int(question[1:]) - elif question.isdigit(): + if question.isdigit(): return int(question) found_op = False @@ -24,30 +24,31 @@ def answer(question): if name in question: question = question.replace(name, op) found_op = True - if not found_op: raise ValueError("unknown operation") + if not found_op: + raise ValueError("unknown operation") ret = question.split() while len(ret) > 1: try: x, op, y, *tail = ret - if op not in OPS.values(): raise ValueError("syntax error") + if op not in OPS.values(): + raise ValueError("syntax error") ret = [int(x).__getattribute__(op)(int(y)), *tail] except: raise ValueError("syntax error") return ret[0] - ``` -This approach begins by defining a [dictionary][dictionaries] of the word keys with their related [`dunder-methods`][dunder] methods. -Since only whole numbers are involved, the available `dunder-methods` are those for the [`int`][int] class/namespace. +This approach begins by defining a [dictionary][dictionaries] of the word keys with their related [dunder method][dunder] values. +Since only whole numbers are involved, the available dunder methods are those for the [`int`][int] class/namespace. The supported methods for the `int()` namespace can be found by using `print(dir(int))` or `print(int.__dict__)` in a Python terminal. -See [`SO: Difference between dir() and __dict__`][dir-vs-__dict__] for more details. +See [this StackOverflow post][dir-vs-__dict__] for more details.
~~~~exercism/note The built-in [`dir`](https://docs.python.org/3/library/functions.html?#dir) function returns a list of all valid attributes for an object. -The `dunder-method` [`.__dict__`](https://docs.python.org/3/reference/datamodel.html#object.__dict__) is a mapping of an objects writable attributes. +The dunder method [`.__dict__`](https://docs.python.org/3/reference/datamodel.html#object.__dict__) is a mapping of an object's writable attributes. ~~~~
@@ -56,35 +57,37 @@ The `OPS` dictionary is defined with all uppercase letters, which is the naming It indicates that the value should not be changed. The input question to the `answer()` function is cleaned using the [`removeprefix`][removeprefix], [`removesuffix`][removesuffix], and [`strip`][strip] string methods. -The method calls are [chained][method-chaining], so that the output from one call is the input for the next call. -If the input has no characters left, -it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return a `ValueError("syntax error")`. +The method calls are [chained][method-chaining], so the output from one call is the input for the next call. +If the input has no characters left, it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return a `ValueError("syntax error")`. -Next, the [`str.startswith()`][startswith] and [`isdigit`][isdigit] methods are used to see if the remaining characters in the input are either negative or positive digits. -Because "-" is used to denote negative numbers, `str.startswith("-")` is used in the first condition and `question[1:].isdigit()` is then used for the remaining string. -If the `str.isdigit()` checks pass, the [`int()`][int-constructor] constructor is used to return the string as an integer with the proper sign. +Next, the [`str.startswith()`][startswith] and [`str.isdigit()`][isdigit] methods are used to see if the remaining characters in the input are either negative or positive digits. +Because "-" is used to denote negative numbers, `str.startswith("-")` is used in the first condition and `question[1:].isdigit()` is used for the remaining string. +If the `str.isdigit()` checks pass, the [`int()` constructor][int-constructor] is used to return the string as an integer with the proper sign. Next, the elements in the `OPS` dictionary are iterated over. -If the key name is in the input, then the [`str.replace`][replace] method is used to replace the name in the input with the `dunder-method` value. -If none of the key names are found in the input, a `ValueError("unknown operation")` is returned. +If the key name is in the input, the [`str.replace`][replace] method is used to replace the name in the input with the dunder method value. +If none of the key names are found in the input, a `ValueError("unknown operation")` is raised. + +At this point, the input question is [`split()`][split] into a `list` of its words, which is then iterated over while its [`len()`][len] is greater than 1. -At this point the input question is [`split()`][split] into a `list` of its words, which is then iterated over while its [`len()`][len] is greater than 1. +Within a [`try-except`][exception-handling] block, the list is [unpacked][unpacking] (_see also [concept:python/unpacking-and-multiple-assignment]()_) into the variables `x`, `op`, `y`, and `*tail`. +If `op` is not in the `OPS` dictionary, a `ValueError("syntax error")` is raised. -Within a [try-except][exception-handling] block, the list is [unpacked][unpacking] (_see also [Concept: unpacking][unpacking-and-multiple-assignment]_) into the variables `x, op, y, and *tail`. -If `op` is not in the supported `dunder-methods` dictionary, a `ValueError("syntax error")` is raised. -If there are any other exceptions raised within the `try` block, they are "caught"/ handled in the `except` clause by raising a `ValueError("syntax error")`. +The `except` block will catch this error (or any other error raised inside the `try` block), and `raise` a `ValueError("syntax error")` instead. +(You can look at [exception chaining in the Python docs][exception-chaining] for further detail on this subject.) -Next, `x` is converted to an `int` and [`__getattribute__`][getattribute] is called for the `dunder-method` (`op`) to apply to `x`. -`y` is then converted to an `int` and passed as the second arguemnt to `op`. +Next, `x` is converted to an `int` and [`__getattribute__`][getattribute] is called for the dunder method (`op`) to apply to `x`. +`y` is then converted to an `int` and passed as the second argument to `op`. Then `ret` is redefined to a `list` containing the result of the dunder method plus the remaining elements in `*tail`. -When the loop exhausts, the first element of the list is selected as the function return value. +When `ret` reaches `len() == 1` and the loop ends, the first element of `ret` is returned as the answer. [const]: https://realpython.com/python-constants/ [dictionaries]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries [dir-vs-__dict__]: https://stackoverflow.com/a/14361362 [dunder]: https://www.tutorialsteacher.com/python/magic-methods-in-python +[exception-chaining]: https://docs.python.org/3/tutorial/errors.html#exception-chaining [exception-handling]: https://docs.python.org/3/tutorial/errors.html#handling-exceptions [falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/ [getattribute]: https://docs.python.org/3/reference/datamodel.html?#object.__getattribute__ @@ -101,4 +104,3 @@ When the loop exhausts, the first element of the list is selected as the functio [strip]: https://docs.python.org/3/library/stdtypes.html#str.strip [startswith]: https://docs.python.org/3/library/stdtypes.html#str.startswith [unpacking]: https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/ -[unpacking-and-multiple-assignment]: https://exercism.org/tracks/python/concepts/unpacking-and-multiple-assignment diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.txt b/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.txt index d3cc3d1670..7e648873b3 100644 --- a/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.txt +++ b/exercises/practice/wordy/.approaches/dunder-getattribute/snippet.txt @@ -5,4 +5,4 @@ while len(ret) > 1: ret = [int(x).__getattribute__(op)(int(y)), *tail] except: raise ValueError("syntax error") -return ret[0] +return ret[0] \ No newline at end of file diff --git a/exercises/practice/wordy/.approaches/functools-reduce/content.md b/exercises/practice/wordy/.approaches/functools-reduce/content.md index 8bc42449fa..01c03a54f7 100644 --- a/exercises/practice/wordy/.approaches/functools-reduce/content.md +++ b/exercises/practice/wordy/.approaches/functools-reduce/content.md @@ -1,5 +1,4 @@ -# Functools.reduce for Calculation - +# `functools.reduce()` for Calculation ```python from operator import add, mul, sub @@ -12,25 +11,25 @@ OPERATORS = {"plus": add, "minus": sub, "multiplied": mul, "divided": div} def answer(question): # Check for basic validity right away, and fail out with error if not valid. - if not question.startswith( "What is") or "cubed" in question: + if not question.startswith("What is") or "cubed" in question: raise ValueError("unknown operation") - - # Using the built-in filter() to clean & split the question.. - question = list(filter(lambda x: - x not in ("What", "is", "by"), + + # Use the built-in filter() to clean and split the question. + question = list(filter(lambda x: + x not in ("What", "is", "by"), question.strip("?").split())) # Separate candidate operators and numbers into two lists. operations = question[1::2] - + # Convert candidate elements to int(), checking for "-". # All other values are replaced with None. - digits = [int(element) if - (element.isdigit() or element[1:].isdigit()) - else None for element in question[::2]] - - # If there is a mis-match between operators and numbers, toss error. - if len(digits)-1 != len(operations) or None in digits: + digits = [int(element) if + (element.isdigit() or element[1:].isdigit()) + else None for element in question[::2]] + + # If there is a mis-match between operators and numbers, throw an error. + if len(digits) - 1 != len(operations) or None in digits: raise ValueError("syntax error") # Evaluate the expression from left to right using functools.reduce(). @@ -39,67 +38,64 @@ def answer(question): ``` This approach replaces the `while-loop` or `recursion` used in many solutions with a call to [`functools.reduce`][functools-reduce]. -It requires that the question be separated into candidate digits and candidate operators, which is accomplished here via [list-slicing][sequence-operations] (_for some additional information on working with `lists`, see [concept: lists](/tracks/python/concepts/lists)_). +It requires that the question be separated into candidate digits and candidate operators, which is accomplished here via [list slicing][sequence-operations] (_for some additional information on working with `lists`, see [concept:python/lists]()_). A nested call to `filter()` and `split()` within a `list` constructor is used to clean and process the question into an initial `list` of digit and operator strings. -However, this could easily be accomplished by either using [chained][method-chaining] string methods or a `list-comprehension`: - +However, this could easily be accomplished by either using [chained][method-chaining] string methods or a list comprehension: ```python # Alternative 1 is chaining various string methods together. - # The wrapping () invoke implicit concatenation for the chained functions - return (question.removeprefix("What is") + # The wrapping () invoke implicit concatenation for the chained functions. + question = (question.removeprefix("What is") .removesuffix("?") .replace("by", "") - .strip()).split() # <-- this split() turns the string into a list. - - - # Alternative 2 to the nested calls to filter and split is to use a list-comprehension: - return [item for item in - question.strip("?").split() - if item not in ("What", "is", "by")] #<-- The [] of the comprehension invokes implicit concatenation. -``` + .strip()).split() # <-- This split() turns the string into a list. + # Alternative 2 to the nested calls is to use a list comprehension: + question = [item for item in + question.strip("?").split() + if item not in ("What", "is", "by")] # <-- The [] of the comprehension invokes implicit concatenation. +``` + Since "valid" questions are all in the form of `digit-operator-digit` (_and so on_), it is safe to assume that every other element beginning at index 0 is a "number", and every other element beginning at index 1 is an operator. -By that definition, the operators `list` is 1 shorter in `len()` than the digits list. -Anything else (_or having None/an unknown operation in the operations list_) is a `ValueError("syntax error")`. +By that definition, the `operators` list is 1 shorter in `len()` than the `digits` list. +Anything else (_or having `None`/an unknown operation in the operations list_) is a `ValueError("syntax error")`. -The final call to `functools.reduce` essentially performs the same steps as the `while-loop` implementation, with the `lambda-expression` passing successive items of the digits `list` to the popped and looked-up operation from the operations `list` (_made [callable][callable] by adding `()`_), until it is reduced to one number and returned. +The final call to `functools.reduce` essentially performs the same steps as the `while-loop` implementation, with the `lambda-expression` passing successive items of the `digits` list to the popped and looked-up operation from the operations `list` (_used as a [callable][callable] with `()`_), until it is reduced to one number and returned. A `try-except` is not needed here because the error scenarios are already filtered out in the `if` check right before the call to `reduce()`. -`functools.reduce` is certainly convenient, and makes the solution much shorter. -But it is also hard to understand what is happening if you have not worked with a reduce or foldl function in the past. +`functools.reduce` is certainly convenient, and it makes the solution much shorter. +However, it is also hard to understand what is happening if you have not worked with a `reduce` or `foldl` function in the past. It could be argued that writing the code as a `while-loop` or recursive function is easier to reason about for non-functional programmers.
-## Variation 1: Use a Dictionary of `lambdas` instead of importing from operator +## Variation 1: Use a dictionary of `lambdas` instead of importing from `operator` - -The imports from operator can be swapped out for a dictionary of `lambda-expressions` (or calls to `dunder-methods`), if so desired. +The imports from the `operator` module can be swapped out for a dictionary of `lambda-expressions` (or calls to `dunder-methods`), if so desired. The same cautions apply here as were discussed in the [lambdas in a dictionary][approach-lambdas-in-a-dictionary] approach: - ```python from functools import reduce # Define a lookup table for mathematical operations -OPERATORS = {"plus": lambda x, y: x + y, - "minus": lambda x, y: x - y, - "multiplied": lambda x, y: x * y, - "divided": lambda x, y: x / y} +OPERATORS = { + "plus": lambda x, y: x + y, + "minus": lambda x, y: x - y, + "multiplied": lambda x, y: x * y, + "divided": lambda x, y: x / y +} def answer(question): - # Check for basic validity right away, and fail out with error if not valid. - if not question.startswith( "What is") or "cubed" in question: + if not question.startswith("What is") or "cubed" in question: raise ValueError("unknown operation") # Clean and split the question into a list for processing. - question = [item for item in - question.strip("?").split() if + question = [item for item in + question.strip("?").split() if item not in ("What", "is", "by")] # Separate candidate operators and numbers into two lists. @@ -107,8 +103,8 @@ def answer(question): # Convert candidate elements to int(), checking for "-". # All other values are replaced with None. - digits = [int(element) if - (element.isdigit() or element[1:].isdigit()) + digits = [int(element) if + (element.isdigit() or element[1:].isdigit()) else None for element in question[::2]] # If there is a mis-match between operators and numbers, toss error. @@ -116,7 +112,7 @@ def answer(question): raise ValueError("syntax error") # Evaluate the expression from left to right using functools.reduce(). - # Look up each operation in the operation dictionary. + # Look up each operation in the OPERATORS dictionary. result = reduce(lambda x, y: OPERATORS[operations.pop(0)](x, y), digits) return result diff --git a/exercises/practice/wordy/.approaches/functools-reduce/snippet.txt b/exercises/practice/wordy/.approaches/functools-reduce/snippet.txt index f8d5a29419..2c1cb3afbd 100644 --- a/exercises/practice/wordy/.approaches/functools-reduce/snippet.txt +++ b/exercises/practice/wordy/.approaches/functools-reduce/snippet.txt @@ -1,7 +1,7 @@ OPERATORS = {"plus": add, "minus": sub, "multiplied": mul, "divided": div} - +... operations = question[1::2] digits = [int(element) if (element.isdigit() or element[1:].isdigit()) - else None for element in question[::2]] + else None for element in question[::2]] ... return reduce(lambda x, y: OPERATORS[operations.pop(0)](x, y), digits) \ No newline at end of file diff --git a/exercises/practice/wordy/.approaches/import-callables-from-operator/content.md b/exercises/practice/wordy/.approaches/import-callables-from-operator/content.md index 9fdf3e20e0..618b7daf19 100644 --- a/exercises/practice/wordy/.approaches/import-callables-from-operator/content.md +++ b/exercises/practice/wordy/.approaches/import-callables-from-operator/content.md @@ -1,5 +1,4 @@ -# Import Callables from the Operator Module - +# Import Callables from the `operator` Module ```python from operator import add, mul, sub @@ -21,7 +20,7 @@ def answer(question): if (question.startswith("-") and question[1:].isdigit()) or question.isdigit(): return int(question) - equation = [word for word in question.split() if word != 'by'] + equation = [word for word in question.split() if word != "by"] while len(equation) > 1: try: @@ -34,44 +33,41 @@ def answer(question): return equation[0] ``` - This approach is nearly identical to the [string, list, and dict methods][approach-string-list-and-dict-methods] approach, so it is recommended to review that before going over this one. The two major differences are the `operator` module, and the elimination of the `if-elif-else` block. The solution begins by importing basic mathematical operations as methods from the [`operator`][operator] module. -These functions (_floordiv is [aliased][aliasing] to "div"_) are stored in a dictionary that serves as a lookup table when the problems are processed. -These operations are later made [callable][callable] by using `()` after the name, and supplying arguments. +`add`, `mul` and `sub` keep their original names, while `floordiv` is [aliased][aliasing] to `div`. +These functions are then stored in a dictionary that serves as a lookup table when the problems are processed. +These operations are later used as [callables][callable] by putting `()` after the name, and supplying arguments between the parentheses. -In `answer()`, the question is first checked for validity, cleaned, and finally split into a `list` using [`str.startswith`][startswith], [`str.removeprefix`][removeprefix]/[`str.removesuffix`][removesuffix], [strip][strip], and [split][split]. -Checks for digits and an empty string are done, and the word "by" is filtered from the equation `list` using a [`list-comprehension`][list-comprehension]. +In `answer()`, the question is first checked for validity, cleaned, and finally split into a `list` using [`str.startswith`][startswith], [`str.removeprefix`][removeprefix]/[`str.removesuffix`][removesuffix], [`str.strip()`][strip], and [`str.split()`][split]. +Next, checks for digits and an empty string are done, and the word "by" is filtered from the equation `list` by using a [list comprehension][list-comprehension]. -The equation `list` is then processed in a `while-loop` within a [try-except][handling-exceptions] block. -The `list` is [unpacked][unpacking] (_see also [concept: unpacking and multiple assignment](/tracks/python/concepts/unpacking-and-multiple-assignment)_) into `x_value`, `operation`, `y_value`, and `*rest`, and reduced by looking up and calling the mathematical function in the OPERATIONS dictionary and passing in `int(x_value)` and `int(y_value)` as arguments. +The equation `list` is then processed in a `while-loop` within a [`try-except`][handling-exceptions] block. +The `list` is [unpacked][unpacking] (_see also [concept:python/unpacking-and-multiple-assignment]()_) into `x_value`, `operation`, `y_value`, and `*rest`, and reduced by looking up and calling the mathematical function in the `OPERATIONS` dictionary and passing in `int(x_value)` and `int(y_value)` as arguments. -The processing of the equation `list` continues until it is of `len()` 1, at which point the single element is returned as the answer. +The processing of the equation `list` continues until its `len() == 1`, at which point the single element is returned as the answer. To walk through this step-by-step, you can interact with this code on [`pythontutor.com`][pythontutor]. -Using a `list-comprehension` to filter out "by" can be replaced with the [`str.replace`][str-replace] method during question cleaning. +Using a list comprehension to filter out "by" can be replaced with the [`str.replace`][str-replace] method during question cleaning. [Implicit concatenation][implicit-concatenation] can be used to improve the readability of the [chained][chaining-method-calls] method calls: - ```python question = (question.removeprefix("What is") .removesuffix("?") .replace("by", "") - .strip()) #<-- Enclosing () means these lines are automatically joined by the interpreter. + .strip()) # <-- Enclosing parentheses means these lines are automatically joined by the interpreter. ``` - -The call to `str.replace` could instead be chained to the call to `split` when creating the equation `list`: - +The call to `str.replace` could instead be chained with the call to `str.split` when creating the equation `list`: ```python equation = question.replace("by", "").split() @@ -85,7 +81,7 @@ equation = question.replace("by", "").split() [implicit-concatenation]: https://docs.python.org/3/reference/lexical_analysis.html#implicit-line-joining [list-comprehension]: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions [operator]: https://docs.python.org/3/library/operator.html#module-operator -[pythontutor]: https://pythontutor.com/render.html#code=from%20operator%20import%20add,%20mul,%20sub%0Afrom%20operator%20import%20floordiv%20as%20div%0A%0AOPERATIONS%20%3D%20%7B%22plus%22%3A%20add,%20%22minus%22%3A%20sub,%20%22multiplied%22%3A%20mul,%20%22divided%22%3A%20div%7D%0A%0Adef%20answer%28question%29%3A%0A%20%20%20%20if%20not%20question.startswith%28%22What%20is%22%29%20or%20%22cubed%22%20in%20question%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22unknown%20operation%22%29%0A%20%20%20%20%0A%20%20%20%20question%20%3D%20question.removeprefix%28%22What%20is%22%29.removesuffix%28%22%3F%22%29.strip%28%29%0A%0A%20%20%20%20if%20question.isdigit%28%29%3A%20%0A%20%20%20%20%20%20%20%20return%20int%28question%29%0A%20%20%20%20%0A%20%20%20%20if%20not%20question%3A%20%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%20%20%20%20%0A%20%20%20%20equation%20%3D%20%5Bword%20for%20word%20in%20question.split%28%29%20if%20word%20!%3D%20'by'%5D%0A%20%20%20%20%0A%20%20%20%20while%20len%28equation%29%20%3E%201%3A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20x_value,%20operation,%20y_value,%20*rest%20%3D%20equation%0A%20%20%20%20%20%20%20%20%20%20%20%20equation%20%3D%20%5BOPERATIONS%5Boperation%5D%28int%28x_value%29,%20int%28y_value%29%29,%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*rest%5D%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%20%20%20%20%0A%20%20%20%20return%20equation%5B0%5D%0A%20%20%20%20%0Aprint%28answer%28%22What%20is%202%20plus%202%20plus%203%3F%22%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false +[pythontutor]: https://pythontutor.com/visualize.html#code=from%20operator%20import%20add,%20mul,%20sub%0Afrom%20operator%20import%20floordiv%20as%20div%0A%0AOPERATIONS%20%3D%20%7B%22plus%22%3A%20add,%20%22minus%22%3A%20sub,%20%22multiplied%22%3A%20mul,%20%22divided%22%3A%20div%7D%0A%0Adef%20answer%28question%29%3A%0A%20%20%20%20if%20not%20question.startswith%28%22What%20is%22%29%20or%20%22cubed%22%20in%20question%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22unknown%20operation%22%29%0A%20%20%20%20%0A%20%20%20%20question%20%3D%20question.removeprefix%28%22What%20is%22%29.removesuffix%28%22%3F%22%29.strip%28%29%0A%0A%20%20%20%20if%20question.isdigit%28%29%3A%20%0A%20%20%20%20%20%20%20%20return%20int%28question%29%0A%20%20%20%20%0A%20%20%20%20if%20not%20question%3A%20%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%20%20%20%20%0A%20%20%20%20equation%20%3D%20%5Bword%20for%20word%20in%20question.split%28%29%20if%20word%20!%3D%20%22by%22%5D%0A%20%20%20%20%0A%20%20%20%20while%20len%28equation%29%20%3E%201%3A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20x_value,%20operation,%20y_value,%20*rest%20%3D%20equation%0A%20%20%20%20%20%20%20%20%20%20%20%20equation%20%3D%20%5BOPERATIONS%5Boperation%5D%28int%28x_value%29,%20int%28y_value%29%29,%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*rest%5D%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%20%20%20%20%0A%20%20%20%20return%20equation%5B0%5D%0A%20%20%20%20%0Aprint%28answer%28%22What%20is%202%20plus%202%20plus%203%3F%22%29%29&curInstr=0&mode=display&origin=opt-frontend.js&py=311 [removeprefix]: https://docs.python.org/3.9/library/stdtypes.html#str.removeprefix [removesuffix]: https://docs.python.org/3.9/library/stdtypes.html#str.removesuffix [split]: https://docs.python.org/3.9/library/stdtypes.html#str.split diff --git a/exercises/practice/wordy/.approaches/import-callables-from-operator/snippet.txt b/exercises/practice/wordy/.approaches/import-callables-from-operator/snippet.txt index d5cb5a1354..de87954eae 100644 --- a/exercises/practice/wordy/.approaches/import-callables-from-operator/snippet.txt +++ b/exercises/practice/wordy/.approaches/import-callables-from-operator/snippet.txt @@ -2,7 +2,7 @@ OPERATIONS = {"plus": add, "minus": sub, "multiplied": mul, "divided": div} while len(equation) > 1: try: x_value, operation, y_value, *rest = equation - equation = [OPERATIONS[operation](int(x_value), int(y_value)),*rest] + equation = [OPERATIONS[operation](int(x_value), int(y_value)), *rest] except: raise ValueError("syntax error") return equation[0] \ No newline at end of file diff --git a/exercises/practice/wordy/.approaches/introduction.md b/exercises/practice/wordy/.approaches/introduction.md index 821b122842..1383427885 100644 --- a/exercises/practice/wordy/.approaches/introduction.md +++ b/exercises/practice/wordy/.approaches/introduction.md @@ -6,18 +6,15 @@ This means that for some of the test cases, the solution will not be the same as
- ## General Guidance The key to a Wordy solution is to remove the "question" portion of the sentence (_"What is", "?"_) and process the remaining words between numbers as [operators][mathematical operators]. If a single number remains after removing the "question" pieces, it should be converted to an [`int`][int] and returned as the answer. - -Any words or word-number combinations that do not fall into the simple mathematical evaluation pattern (_number-operator-number_) should [`raise`][raise-statement] a ["ValueError('syntax error")`][value-error] with a message. +Any words or word-number combinations that do not fall into the simple mathematical evaluation pattern (_number-operator-number_) should [`raise`][raise-statement] a [`ValueError`][value-error] with a message. This includes any "extra" spaces between numbers. As shown in various approaches, there are multiple strategies for validating questions, with no one "canonical" solution. - A whole class of error can be eliminated up front by checking if a question starts with "What is", ends with "?", and does not include the word "cubed". Any other question formulation becomes a `ValueError("unknown operation")`. This could lead to future maintenance issues if the definition of a question ever changes or operations are added, but for the purposes of passing the current Wordy tests, it works well. @@ -26,18 +23,18 @@ This could lead to future maintenance issues if the definition of a question eve ~~~~exercism/note There are many Pythonic ways to go about the cleaning, parsing, and calculation steps of Wordy. -However, solutions all follow the same general steps: +However, solutions all follow the same general steps: -1. Remove the parts of the question string that do not apply to calculating the answer. -2. Iterate over the question, determining which words are numbers, and which are meant to be mathematical operations. - — _Converting the question string into a `list` of words is hugely helpful here._ -3. **_Starting from the left_**, take the first three elements and convert number strings to `int` and operations words to the mathematical operations +, -, *, and /. +1. Remove the parts of the question string that do not apply to calculating the answer. +2. Iterate over the question, determining which words are numbers, and which are meant to be mathematical operations. + - _Converting the question string into a `list` of words is hugely helpful here._ +3. **_Starting from the left_**, take the first three elements and convert number strings to `int`s and operation words to the mathematical operations `+`, `-`, `*`, and `/`. 4. Apply the operation to the numbers, which should result in a single number. - — _Employing a `try-except` block can trap any errors thrown and make the code both "safer" and less complex._ -5. Use the calculated number from step 4 as the start for the next "trio" (_number, operation, number_) in the question. The calculated number + the remainder of the question becomes the question being worked on in the next iteration. - — _Using a `while-loop` with a test on the length of the question to do calculation is a very common strategy._ -6. Once the question is calculated down to a single number, that is the answer. Anything else that happens in the loop/iteration or within the accumulated result is a `ValueError("syntax error")`. + - _Employing a `try-except` block can trap any errors thrown and make the code both "safer" and less complex._ +5. Use the calculated number from step 4 as the start for the next "trio" (_number, operation, number_) in the question. The calculated number plus the remainder of the question becomes the question being worked on in the next iteration. + - _Using a `while-loop` with a test on the length of the question to do calculation is a very common strategy._ +6. Once the question is calculated down to a single number, that is the answer. Anything else that happens in the loop/iteration or within the accumulated result is a `ValueError("syntax error")`. ~~~~
@@ -45,61 +42,54 @@ However, solutions all follow the same general steps: For question cleaning, [`str.removeprefix`][removeprefix] and [`str.removesuffix`][removesuffix] introduced in `Python 3.9` can be very useful: - ```python ->>> 'Supercalifragilisticexpialidocious'.removeprefix('Super') +>>> "Supercalifragilisticexpialidocious".removeprefix("Super") 'califragilisticexpialidocious' ->>> 'Supercalifragilisticexpialidocious'.removesuffix('expialidocious') +>>> "Supercalifragilisticexpialidocious".removesuffix("expialidocious") 'Supercalifragilistic' -#The two methods can be chained to remove both a suffix and prefix in "one line". -#The line has been broken up here for better display. ->>> ('Supercalifragilisticexpialidocious' - .removesuffix('expialidocious') - .removeprefix('Super')) +# The two methods can be chained to remove both a suffix and prefix in "one line". +# The line has been broken up here for better display. +>>> ("Supercalifragilisticexpialidocious" + .removesuffix("expialidocious") + .removeprefix("Super")) 'califragilistic' ``` - You can also use [`str.startswith`][startswith] and [`str.endswith`][endswith] in conjunction with [string slicing][sequence-operations] for cleaning: - ```python ->>> if 'Supercalifragilisticexpialidocious'.startswith('Super'): - new_string = 'Supercalifragilisticexpialidocious'[5:] +>>> if "Supercalifragilisticexpialidocious".startswith("Super"): + new_string = "Supercalifragilisticexpialidocious"[5:] >>> new_string 'califragilisticexpialidocious' ->>> if new_string.endswith('expialidocious'): +>>> if new_string.endswith("expialidocious"): new_string = new_string[:15] >>> new_string 'califragilistic' ``` - Different combinations of [`str.find`][find], [`str.rfind`][rfind], or [`str.index`][index] with string slicing could also be used to clean up the initial question. A [regex][regex] could be used to process the question as well, but might be considered overkill given the fixed nature of the prefix/suffix and operations. Finally, [`str.strip`][strip] and its variants are very useful for cleaning up any leftover leading or trailing whitespace. Many solutions then use [`str.split`][split] to process the remaining "cleaned" question into a `list` for convenient looping/iteration, although other strategies can also be used: - ```python >>> sentence = "The quick brown fox jumped over the lazy dog 10 times" >>> sentence.split() ['The', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy', 'dog', '10', 'times'] ``` - For math operations, many solutions involve importing and using methods from the [operator][operator] module. Some solutions use either [lambda][lambdas] expressions, [dunder/"special" methods][dunder-methods], or even `eval()` to replace words with arithmetic operations. - However, the exercise can be solved without using `operator`, `lambdas`, `dunder-methods` or `eval`. - It is recommended that you first start by solving it _without_ "advanced" strategies, and then refine your solution into something more compact or complex as you learn and practice. +It is recommended that you first start by solving it _without_ "advanced" strategies, and then refine your solution into something more compact or complex as you learn and practice.
@@ -116,10 +106,8 @@ It is also entirely unnecessary, as the other methods described here are safer a _____________ - ## Approach: String, List, and Dictionary Methods - ```python def answer(question): if not question.startswith("What is") or "cubed" in question: @@ -157,18 +145,21 @@ def answer(question): return int(formula[0]) ``` -This approach uses only data structures and methods (_[str methods][str-methods], [list()][list], loops, etc._) from core Python, and does not import any extra modules. +This approach uses only data structures and methods (_[`str` methods][str-methods], [`list()`][list], loOPERATORS, etc._) from core Python, and does not import any extra modules. It may have more lines of code than average, but it is clear to follow and fairly straightforward to reason about. -It does use a [try-except][handling-exceptions] block for handling unknown operators. -Alternatives could use a [dictionary][dict] to store word --> operator mappings that could be looked up in the `while-loop` using [`.get()`][dict-get], among other strategies. +This approach uses a [`try-except`][handling-exceptions] statement for handling unknown operators. +It does this by raising an error inside the `try` block when `symbol` does not match any operator word. +The `except` block will catch this error (or any other error raised inside the `try` block), and `raise` a `ValueError("syntax error")` instead. +(You can look at [exception chaining in the Python docs][exception-chaining] for further detail on this subject.) + +Alternatives could use a [dictionary][dict] to store word to operator mappings that could be looked up in the `while-loop` using [`.get()`][dict-get], among other strategies. For more details and variations, read the [String, List and Dictionary Methods][approach-string-list-and-dict-methods] approach.
-## Approach: Import Callables from the Operator Module - +## Approach: Import Callables from the `operator` Module ```python from operator import add, mul, sub @@ -185,10 +176,10 @@ def answer(question): if (question.startswith("-") and question[1:].isdigit()) or question.isdigit(): return int(question) - if not question: + if not question: raise ValueError("syntax error") - equation = [word for word in question.split() if word != 'by'] + equation = [word for word in question.split() if word != "by"] while len(equation) > 1: try: @@ -202,15 +193,14 @@ def answer(question): ``` This solution imports methods from the `operator` module, and uses them in a dictionary/lookup map. -Like the first approach, it uses a [try-except][handling-exceptions] block for handling unknown operators. - It also uses a [list-comprehension][list-comprehension] to create the parsed "formula" and employs [concept: unpacking and multiple assignment](/tracks/python/concepts/unpacking-and-multiple-assignment). +Like the first approach, it uses a [`try-except`][handling-exceptions] block for handling unknown operators. +It also uses a [list comprehension][list-comprehension] to create the parsed "formula" and employs [concept:python/unpacking-and-multiple-assignment](). -For more details and options, take a look at the [Import Callables from the Operator Module][approach-import-callables-from-operator] approach. +For more details and options, take a look at the [Import Callables from the `operator` Module][approach-import-callables-from-operator] approach.
-## Approach: Regex and the Operator Module - +## Approach: Regex with the `operator` Module ```python import re @@ -218,21 +208,22 @@ from operator import add, mul, sub from operator import floordiv as div OPERATIONS = {"plus": add, "minus": sub, "multiplied by": mul, "divided by": div} + REGEX = { - 'number': re.compile(r'-?\d+'), - 'operator': re.compile(f'(?:{"|".join(OPERATIONS)})\\b') + "number": re.compile(r"-?\d+"), + "operator": re.compile(f"(?:{'|'.join(OPERATIONS)})\\b") } def get_number(question): - pattern = REGEX['number'].match(question) + pattern = REGEX["number"].match(question) if not pattern: raise ValueError("syntax error") - return [question.removeprefix(pattern.group(0)).lstrip(), + return [question.removeprefix(pattern.group(0)).lstrip(), int(pattern.group(0))] def get_operation(question): - pattern = REGEX['operator'].match(question) + pattern = REGEX["operator"].match(question) if not pattern: raise ValueError("unknown operation") return [question.removeprefix(pattern.group(0)).lstrip(), @@ -242,12 +233,12 @@ def answer(question): prefix = "What is" if not question.startswith(prefix): raise ValueError("unknown operation") - + question = question.removesuffix("?").removeprefix(prefix).lstrip() question, result = get_number(question) while len(question) > 0: - if REGEX['number'].match(question): + if REGEX["number"].match(question): raise ValueError("syntax error") question, operation = get_operation(question) @@ -258,26 +249,24 @@ def answer(question): return result ``` - This approach uses a dictionary of regex patterns for matching numbers and operators, paired with a dictionary of operations imported from the `operator` module. It pulls number and operator processing out into separate functions and uses a while loop in `answer()` to evaluate the word problem. It also uses multiple assignment for various variables. It is longer than some solutions, but clearer and potentially easier to maintain due to the separate `get_operation()` and `get_number()` functions. -For more details, take a look at the [regex-with-operator-module][approach-regex-with-operator-module] approach. +For more details, take a look at the [Regex with the `operator` Module][approach-regex-with-operator-module] approach.
-## Approach: Lambdas in a Dictionary to return Functions - +## Approach: Lambdas in a Dictionary to Return Functions ```python OPERATIONS = { - 'minus': lambda a, b: a - b, - 'plus': lambda a, b: a + b, - 'multiplied': lambda a, b: a * b, - 'divided': lambda a, b: a / b - } + "minus": lambda a, b: a - b, + "plus": lambda a, b: a + b, + "multiplied": lambda a, b: a * b, + "divided": lambda a, b: a / b +} def answer(question): @@ -289,10 +278,10 @@ def answer(question): if (question.startswith("-") and question[1:].isdigit()) or question.isdigit(): return int(question) - if not question: + if not question: raise ValueError("syntax error") - equation = [word for word in question.split() if word != 'by'] + equation = [word for word in question.split() if word != "by"] while len(equation) > 1: try: @@ -305,8 +294,7 @@ def answer(question): return equation[0] ``` - -Rather than import methods from the `operator` module, this approach defines a series of [`lambda expressions`][lambdas] in the OPERATIONS dictionary. +Rather than import methods from the `operator` module, this approach defines a series of [`lambda expressions`][lambdas] in the `OPERATIONS` dictionary. These `lambdas` then return a function that takes two numbers as arguments, returning the result. One drawback of this strategy over using named functions or methods from `operator` is the lack of debugging information should something go wrong with evaluation. @@ -320,7 +308,6 @@ For more details, take a look at the [Lambdas in a Dictionary][approach-lambdas- ## Approach: Recursion - ```python from operator import add, mul, sub from operator import floordiv as div @@ -338,34 +325,32 @@ def clean(question): .removesuffix("?") .replace("by", "") .strip()).split() - + def calculate(equation): if len(equation) == 1: return int(equation[0]) - else: - try: - x_value, operation, y_value, *rest = equation - equation = [OPERATIONS[operation](int(x_value), - int(y_value)), *rest] - except: - raise ValueError("syntax error") - - return calculate(equation) -``` + try: + x_value, operation, y_value, *rest = equation + equation = [OPERATIONS[operation](int(x_value), + int(y_value)), *rest] + except: + raise ValueError("syntax error") + + return calculate(equation) +``` -Like previous approaches that substitute methods from `operator` for `lambdas` or `list-comprehensions` for `loops` that append to a `list` -- `recursion` can be substituted for the `while-loop` that many solutions use to process a parsed word problem. +Like previous approaches that substitute methods from `operator` for `lambdas` or list comprehensions for `loOPERATORS` that append to a `list` — `recursion` can be substituted for the `while-loop` that many solutions use to process a parsed word problem. Depending on who is reading the code, `recursion` may or may not be easier to reason about. It may also be more (_or less!_) performant than using a `while-loop` or `functools.reduce` (_see below_), depending on how the various cleaning and error-checking actions are performed. -The dictionary in this example could use functions from `operator`, `lambdas`, `dunder-methods`, or other strategies -- as long as they can be applied in the `calculate()` function. +The dictionary in this example could use functions from `operator`, `lambdas`, `dunder-methods`, or other strategies — as long as they can be applied in the `calculate()` function. -For more details, take a look at the [recursion][approach-recursion] approach. +For more details, take a look at the [Recursion for Iteration][approach-recursion] approach.
-## Approach: functools.reduce() - +## Approach: `functools.reduce()` ```python from operator import add, mul, sub @@ -376,17 +361,17 @@ from functools import reduce OPERATORS = {"plus": add, "minus": sub, "multiplied": mul, "divided": div} def answer(question): - if not question.startswith( "What is") or "cubed" in question: + if not question.startswith("What is") or "cubed" in question: raise ValueError("unknown operation") - question = list(filter(lambda x: - x not in ("What", "is", "by"), - question.strip("?").split())) + question = list(filter(lambda x: + x not in ("What", "is", "by"), + question.strip("?").split())) operations = question[1::2] - digits = [int(element) if (element.isdigit() or - element[1:].isdigit()) else None for - element in question[::2]] + digits = [int(element) if (element.isdigit() or + element[1:].isdigit()) else None for + element in question[::2]] if len(digits)-1 != len(operations) or None in digits: raise ValueError("syntax error") @@ -396,20 +381,18 @@ def answer(question): return result ``` - This approach replaces the `while-loop` used in many solutions (_or the `recursion` strategy outlined in the approach above_) with a call to [`functools.reduce`][functools-reduce]. -It also employs a lookup dictionary for methods imported from the `operator` module, as well as a `list-comprehension`, the built-in [`filter`][filter] function, and multiple string [slices][sequence-operations]. +It also employs a lookup dictionary for methods imported from the `operator` module, as well as a list comprehension, the built-in [`filter`][filter] function, and multiple string [slices][sequence-operations]. If desired, the `operator` imports can be replaced with a dictionary of `lambda` expressions or `dunder-methods`. This solution may be a little less clear to follow or reason about due to the slicing syntax and the particular syntax of both `filter` and `fuctools.reduce`. -For more details and variations, take a look at the [functools.reduce for Calculation][approach-functools-reduce] approach. +For more details and variations, take a look at the [`functools.reduce` for Calculation][approach-functools-reduce] approach.
## Approach: Dunder methods with `__getattribute__` - ```python OPS = { "plus": "__add__", @@ -421,11 +404,12 @@ OPS = { def answer(question): question = question.removeprefix("What is").removesuffix("?").strip() - if not question: raise ValueError("syntax error") - + if not question: + raise ValueError("syntax error") + if question.startswith("-") and question[1:].isdigit(): return -int(question[1:]) - elif question.isdigit(): + if question.isdigit(): return int(question) found_op = False @@ -433,31 +417,31 @@ def answer(question): if name in question: question = question.replace(name, op) found_op = True - if not found_op: raise ValueError("unknown operation") + if not found_op: + raise ValueError("unknown operation") ret = question.split() while len(ret) > 1: try: x, op, y, *tail = ret - if op not in OPS.values(): raise ValueError("syntax error") + if op not in OPS.values(): + raise ValueError("syntax error") ret = [int(x).__getattribute__(op)(int(y)), *tail] except: raise ValueError("syntax error") return ret[0] - ``` -This approach uses the [`dunder methods`][dunder-methods] / ["special methods"][special-methods] / "magic methods" associated with the `int()` class, using the `dunder-method` called [`.__getattribute__`][getattribute] to find the [callable][callable] operation in the `int()` class [namespace][namespace] / dictionary. -This works because the operators for basic math (_"+, -, *, /, //, %, **"_) have been implemented as callable methods for all integers (_as well as floats and other number types_) and are automatically loaded when the Python interpreter is loaded. - -As described in the first link, it is considered bad form to directly call a `dunder method` (_there are some exceptions_), as they are intended mostly for internal Python use, user-defined class customization, and operator overloading (_a specific form of class-customization_). +This approach uses the [dunder methods][dunder-methods] / ["special methods"][special-methods] / "magic methods" associated with the `int` class, using the dunder method called [`.__getattribute__`][getattribute] to find the [callable][callable] operation in the `int` class [namespace][namespace] / dictionary. +This works because the operators for basic math (_`+`, `-`, `*`, `/`, `//`, `%`, `**`_) have been implemented as callable methods for all integers (_as well as floats and other numeric types_) and are automatically loaded when the Python interpreter is loaded. -This is why the `operator` module exists - as a vehicle for providing callable methods for basic math when **not** overloading or customizing class functionality. +As described in the first link, it is considered bad form to directly call a dunder method (_there are some exceptions_), as they are intended mostly for internal Python use, user-defined class customization, and operator overloading (_a specific form of class-customization_). -For more detail on this solution, take a look at the [dunder method with `__getattribute__` approach][approach-dunder-getattribute]. +This is why the `operator` module exists — It is a vehicle for providing callable methods for basic math when **not** overloading or customizing class functionality. +For more detail on this solution, take a look at the [dunder methods with `__getattribute__` approach][approach-dunder-getattribute]. -[PEMDAS]: https://www.mathnasium.com/math-centers/eagan/news/what-pemdas-e +[PEMDAS]: https://www.mathnasium.com/blog/what-is-pemdas [approach-dunder-getattribute]: https://exercism.org/tracks/python/exercises/wordy/approaches/dunder-getattribute [approach-functools-reduce]: https://exercism.org/tracks/python/exercises/wordy/approaches/functools-reduce [approach-import-callables-from-operator]: https://exercism.org/tracks/python/exercises/wordy/approaches/import-callables-from-operator @@ -468,8 +452,9 @@ For more detail on this solution, take a look at the [dunder method with `__geta [callable]: https://treyhunner.com/2019/04/is-it-a-class-or-a-function-its-a-callable/ [dict-get]: https://docs.python.org/3/library/stdtypes.html#dict.get [dict]: https://docs.python.org/3/library/stdtypes.html#dict -[dunder-methods]: https://www.pythonmorsels.com/what-are-dunder-methods/?watch +[dunder-methods]: https://www.pythonmorsels.com/what-are-dunder-methods/ [endswith]: https://docs.python.org/3.9/library/stdtypes.html#str.endswith +[exception-chaining]: https://docs.python.org/3/tutorial/errors.html#exception-chaining [filter]: https://docs.python.org/3/library/functions.html#filter [find]: https://docs.python.org/3.9/library/stdtypes.html#str.find [functools-reduce]: https://docs.python.org/3/library/functools.html#functools.reduce diff --git a/exercises/practice/wordy/.approaches/lambdas-in-a-dictionary/content.md b/exercises/practice/wordy/.approaches/lambdas-in-a-dictionary/content.md index d78f3e7db8..75f5da8a26 100644 --- a/exercises/practice/wordy/.approaches/lambdas-in-a-dictionary/content.md +++ b/exercises/practice/wordy/.approaches/lambdas-in-a-dictionary/content.md @@ -1,13 +1,12 @@ # Lambdas in a Dictionary to Return Functions - ```python OPERATIONS = { - 'minus': lambda a, b: a - b, - 'plus': lambda a, b: a + b, - 'multiplied': lambda a, b: a * b, - 'divided': lambda a, b: a / b - } + "minus": lambda a, b: a - b, + "plus": lambda a, b: a + b, + "multiplied": lambda a, b: a * b, + "divided": lambda a, b: a / b +} def answer(question): @@ -19,7 +18,7 @@ def answer(question): if (question.startswith("-") and question[1:].isdigit()) or question.isdigit(): return int(question) - if not question: + if not question: raise ValueError("syntax error") equation = question.replace("by", "").split() @@ -35,32 +34,31 @@ def answer(question): return equation[0] ``` -This approach is nearly identical to the [string, list, and dict methods][approach-string-list-and-dict-methods] and the [import callables from the operator][approach-import-callables-from-operator] approaches, so it is recommended that you review those before going over this one. -The major difference here is the use of [`lambda expressions`][lambdas] in place of `operator` methods or string representations in the OPERATIONS dictionary. +This approach is nearly identical to the [string, list, and dict methods][approach-string-list-and-dict-methods] and the [import callables from the `operator` module][approach-import-callables-from-operator] approaches, so it is recommended that you review those before going over this one. +The major difference here is the use of [`lambda expressions`][lambdas] in place of `operator` methods or string representations in the `OPERATIONS` dictionary. `lambda expressions` are small "throwaway" expressions that are simple enough to not require a formal function definition or name. They are most commonly used in [`key functions`][key-functions], the built-ins [`map`][map] and [`filter`][filter], and in [`functools.reduce`][functools-reduce]. - `lambdas` are also often defined in areas where a function is needed for one-time use or callback but it would be onerous or confusing to create a full function definition. +`lambdas` are also often defined in areas where a function is needed for one-time use or callback but it would be onerous or confusing to create a full function definition. The two forms are parsed identically (_they are both function definitions_), but in the case of [`lambdas`][lambda], the function name is always "lambda" and the expression cannot contain statements or annotations. For example, the code above could be re-written to include user-defined functions as opposed to `lambda expressions`: - ```python -def add_(x, y): +def _add(x, y): return x + y -def mul_(x, y): +def _mul(x, y): return x * y -def div_(x, y): - return x//y +def _div(x, y): + return x // y -def sub_(x, y): +def _sub(x, y): return x - y def answer(question): - operations = {'minus': sub_,'plus': add_,'multiplied': mul_,'divided': div_} + operations = {"minus": _sub, "plus": _add, "multiplied": _mul, "divided": _div} if not question.startswith("What is") or "cubed" in question: raise ValueError("unknown operation") @@ -70,11 +68,11 @@ def answer(question): if (question.startswith("-") and question[1:].isdigit()) or question.isdigit(): return int(question) - if not question: + if not question: raise ValueError("syntax error") - equation = question.replace("by", "").split() - + equation = question.replace("by", "").split() + while len(equation) > 1: try: x_value, operation, y_value, *rest = equation @@ -87,8 +85,9 @@ def answer(question): ``` However, this makes the code more verbose and does not improve readability. -In addition, the functions need to carry a trailing underscore to avoid potential shadowing or name conflict. -It is better and cleaner in this circumstance to use `lambda expressions` for the functions - although it could be argued that importing and using the methods from `operator` is even better and clearer. +In addition, the functions need to have a leading underscore to indicate that they are internal functions for the module, which helps avoid potential shadowing or name conflict (see [PEP 8][pep-8-naming-styles] for more detail). + +It is better and cleaner in this circumstance to use `lambda expressions` for the functions — although it could be argued that importing and using the methods from `operator` is even better and clearer. [approach-import-callables-from-operator]: https://exercism.org/tracks/python/exercises/wordy/approaches/import-callables-from-operator [approach-string-list-and-dict-methods]: https://exercism.org/tracks/python/exercises/wordy/approaches/string-list-and-dict-methods @@ -98,3 +97,4 @@ It is better and cleaner in this circumstance to use `lambda expressions` for th [lambda]: https://docs.python.org/3/reference/expressions.html#lambda [lambdas]: https://docs.python.org/3/howto/functional.html#small-functions-and-the-lambda-expression [map]: https://docs.python.org/3/library/functions.html#map +[pep-8-naming-styles]: https://peps.python.org/pep-0008/#descriptive-naming-styles diff --git a/exercises/practice/wordy/.approaches/lambdas-in-a-dictionary/snippet.txt b/exercises/practice/wordy/.approaches/lambdas-in-a-dictionary/snippet.txt index 3769bef8c5..1364338e16 100644 --- a/exercises/practice/wordy/.approaches/lambdas-in-a-dictionary/snippet.txt +++ b/exercises/practice/wordy/.approaches/lambdas-in-a-dictionary/snippet.txt @@ -1,6 +1,6 @@ OPERATIONS = { - 'minus': lambda a, b: a - b, - 'plus': lambda a, b: a + b, - 'multiplied': lambda a, b: a * b, - 'divided': lambda a, b: a / b - } \ No newline at end of file + "minus": lambda a, b: a - b, + "plus": lambda a, b: a + b, + "multiplied": lambda a, b: a * b, + "divided": lambda a, b: a / b +} \ No newline at end of file diff --git a/exercises/practice/wordy/.approaches/recursion/content.md b/exercises/practice/wordy/.approaches/recursion/content.md index 794f1b41c1..4cad29778d 100644 --- a/exercises/practice/wordy/.approaches/recursion/content.md +++ b/exercises/practice/wordy/.approaches/recursion/content.md @@ -1,6 +1,5 @@ # Recursion for Iteration - [Any function that can be written iteratively (_with loops_) can be written using recursion][recursion-and-iteration], and [vice-versa][recursion-is-not-a-superpower]. A recursive strategy [may not always be obvious][looping-vs-recursion] or easy — but it is always possible. So the `while-loop`s used in other approaches to Wordy can be re-written to use recursive calls. @@ -13,9 +12,9 @@ That being said, Python famously does not perform [tail-call optimization][tail-
Recursion works best with problem spaces that resemble trees, include [backtracking][backtracking], or become progressively smaller. - Some examples include financial processes like calculating [amortization][amortization] and [depreciation][depreciation], tracking [radiation reduction through nuclei decay][nuclei-decay], and algorithms like [biscetion search][bisection-search], [depth-first search][dfs], and [merge sort][merge-sort]. +Some examples include financial processes like calculating [amortization][amortization] and [depreciation][depreciation], tracking [radiation reduction through nuclei decay][nuclei-decay], and algorithms like [biscetion search][bisection-search], [depth-first search][dfs], and [merge sort][merge-sort]. -
+
Other algorithms such as [breadth-first search][bfs], [Dijkstra's algorithm][dijkstra], and the [Bellman-Ford Algorithm][bellman-ford] lend themselves better to loops. @@ -25,63 +24,63 @@ Other algorithms such as [breadth-first search][bfs], [Dijkstra's algorithm][dij from operator import add, mul, sub from operator import floordiv as div -# Define a lookup table for mathematical operations +# Define a lookup table for mathematical operations. OPERATIONS = {"plus": add, "minus": sub, "multiplied": mul, "divided": div} def answer(question): - # Call clean() and feed it to calculate() + # Call clean() and feed it to calculate(). return calculate(clean(question)) def clean(question): - # It's not a question unless it starts with 'What is'. + # It's not a question unless it starts with "What is". if not question.startswith("What is") or "cubed" in question: raise ValueError("unknown operation") # Remove the unnecessary parts of the question and # parse the cleaned question into a list of items to process. - # The wrapping () invoke implicit concatenation for the chained functions + # The wrapping () invoke implicit concatenation for the chained functions. return (question.removeprefix("What is") .removesuffix("?") .replace("by", "") - .strip()).split() # <-- this split() turns the string into a list. + .strip()).split() # <-- This split() turns the string into a list. -# Recursively calculate the first piece of the equation, calling -# calculate() on the product + the remainder. +# Recursively calculate the first piece of the equation, +# calling calculate() on the product plus the remainder. # Return the solution when len(equation) is one. def calculate(equation): if len(equation) == 1: return int(equation[0]) - else: - try: - # Unpack the equation into first int, operator, and second int. - # Stuff the remainder into *rest - x_value, operation, y_value, *rest = equation - - # Redefine the equation list as the product of the first three - # variables concatenated with the unpacked remainder. - equation = [OPERATIONS[operation](int(x_value), - int(y_value)), *rest] - except: - raise ValueError("syntax error") - - # Call calculate() with the redefined/partially reduced equation. - return calculate(equation) + + try: + # Unpack the equation into first int, operator, and second int. + # Stuff the remainder into *rest. + x_value, operation, y_value, *rest = equation + + # Redefine the equation list as the product of the first three + # variables concatenated with the unpacked remainder. + equation = [OPERATIONS[operation](int(x_value), + int(y_value)), *rest] + except: + raise ValueError("syntax error") + + # Call calculate() with the redefined/partially reduced equation. + return calculate(equation) ``` This approach separates the solution into three functions: -1. `answer()`, which takes the question and calls `calculate(clean())`, returning the answer to the question. -2. `clean()`, which takes a question string and returns a `list` of parsed words and numbers to calculate from. -3. `calculate()`, which performs the calculations on the `list` recursively, until a single number (_the base case check_) is returned as the answer — or an error is thrown. +1. `answer()`, which takes the question and calls `calculate(clean())`, returning the answer to the question. +2. `clean()`, which takes a question string and returns a `list` of parsed words and numbers to calculate from. +3. `calculate()`, which performs the calculations on the `list` recursively, until a single number (_the [base case][base-case] check_) is returned as the answer — or an error is thrown.
The cleaning logic is separate from the processing logic so that the cleaning steps aren't repeated over and over with each recursive `calculate()` call. This separation also makes it easier to make changes without creating conflict or confusion. -`calculate()` performs the same steps as the `while-loop` from [Import Callables from the Operator Module][approach-import-callables-from-operator] and others. -The difference being that the `while-loop` test for `len()` 1 now occurs as an `if` condition in the function (_the base case_), and the "looping" is now a call to `calculate()` in the `else` condition. +`calculate()` performs the same steps as the `while-loop` from the [Import Callables from the `operator` Module][approach-import-callables-from-operator] approach and others. +The difference being that the `while-loop` test for `len() == 1` now occurs as an `if` condition in the function (_the base case_), and the "looping" is now a call to `calculate()` in the `else` condition. `calculate()` can also use many of the strategies detailed in other approaches, as long as they work with the recursion.
@@ -89,25 +88,23 @@ The difference being that the `while-loop` test for `len()` 1 now occurs as an ` `clean()` can also use any of the strategies detailed in other approaches, two of which are below: ```python - # Alternative 1 to the chained calls is to use a list-comprehension: - return [item for item in - question.strip("?").split() - if item not in ("What", "is", "by")] #<-- The [] of the comprehension invokes implicit concatenation. + # Alternative 1 to the chained calls is to use a list comprehension: + return [item for item in + question.strip("?").split() + if item not in ("What", "is", "by")] # <-- The [] of the comprehension invokes implicit concatenation. # Alternative 2 is the built-in filter(), but it can be somewhat hard to read. - return list(filter(lambda x: - x not in ("What", "is", "by"), - question.strip("?").split())) #<-- The () in list() also invokes implicit concatenation. + return list(filter(lambda x: + x not in ("What", "is", "by"), + question.strip("?").split())) # <-- The () in list() also invokes implicit concatenation. ```
-## Variation 1: Use Regex for matching, cleaning, and calculating - +## Variation 1: Use regex for matching, cleaning, and calculating ```python - import re from operator import add, mul, sub from operator import floordiv as div @@ -115,13 +112,13 @@ from operator import floordiv as div # This regex looks for any number 0-9 that may or may not have a - in front of it. DIGITS = re.compile(r"-?\d+") -# These regex look for a number (x or y) before and after a phrase or word. +# These regex look for a number (x or y) before and after a phrase or word. OPERATORS = { - mul: re.compile(r"(?P-?\d+) multiplied by (?P-?\d+)"), - div: re.compile(r"(?P-?\d+) divided by (?P-?\d+)"), - add: re.compile(r"(?P-?\d+) plus (?P-?\d+)"), - sub: re.compile(r"(?P-?\d+) minus (?P-?\d+)"), - } + mul: re.compile(r"(?P-?\d+) multiplied by (?P-?\d+)"), + div: re.compile(r"(?P-?\d+) divided by (?P-?\d+)"), + add: re.compile(r"(?P-?\d+) plus (?P-?\d+)"), + sub: re.compile(r"(?P-?\d+) minus (?P-?\d+)"), +} # This regex looks for any digit 0-9 (optionally negative) followed by any valid operation, # ending in any digit (optionally negative). @@ -129,76 +126,83 @@ VALIDATE = re.compile(r"(?P-?\d+) (multiplied by|divided by|plus|minus) (?P -## Variation 2: Use Regex, Recurse within the For-loop +## Variation 2: Use regex, recurse within the for-loop + +~~~~exercism/caution +As of the time of writing, this variation of the approach passes all of the tests that are currently in the Exercism test suite. +**However**, it gives an incorrect answer to any sufficiently long problem, such as the following: "What is 1 plus -10 multiplied by 3 minus 4?" + +Parsing this example from left to right, we get `((1 + -10) * 3) - 4 == -31`. +However, this variation returns `9` in this case, [as seen here][recursion-in-loop-pythontutor]. +[recursion-in-loop-pythontutor]: https://pythontutor.com/visualize.html#code=import%20re%0Afrom%20operator%20import%20add,%20mul,%20sub%0Afrom%20operator%20import%20floordiv%20as%20div%0A%0ADIGITS%20%3D%20re.compile%28r%22-%3F%5Cd%2B%22%29%0A%0AOPERATORS%20%3D%20%28%0A%20%20%20%20%28mul,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20multiplied%20by%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%28div,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20divided%20by%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%28add,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20plus%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%28sub,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20minus%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%29%0A%0Adef%20answer%28question%29%3A%0A%20%20%20%20if%20not%20question.startswith%28%22What%20is%22%29%20or%20%22cubed%22%20in%20question%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22unknown%20operation%22%29%0A%0A%20%20%20%20question%20%3D%20question.removeprefix%28%22What%20is%22%29.removesuffix%28%22%3F%22%29.strip%28%29%0A%0A%20%20%20%20if%20not%20question%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%0A%20%20%20%20return%20calculate%28question%29%0A%0Adef%20calculate%28question%29%3A%0A%20%20%20%20if%20DIGITS.fullmatch%28question%29%3A%0A%20%20%20%20%20%20%20%20return%20int%28question%29%0A%0A%20%20%20%20for%20operation,%20pattern%20in%20OPERATORS%3A%0A%20%20%20%20%20%20%20%20if%20matches%20%3A%3D%20pattern.match%28question%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20operation%28calculate%28matches%5B%22x%22%5D%29,%20calculate%28matches%5B%22y%22%5D%29%29%20%23%20%3C--%20the%20loop%20is%20paused%20here%20to%20make%20the%20two%20recursive%20calls.%0A%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%0Aprint%28answer%28%22What%20is%201%20plus%20-10%20multiplied%20by%203%20minus%204%3F%22%29%29%0A%0Aprint%28%22Answer%20should%20be%3A%22,%20%28%281%20%2B%20-10%29%20*%203%29%20-%204%29%0A%0Aprint%28%22Result%20from%20wrong%20order%3A%22,%20%281%20%2B%20-10%29%20*%20%283%20-%204%29%29&curInstr=0&mode=display&origin=opt-frontend.js&py=311 +~~~~ ```python import re @@ -206,55 +210,57 @@ from operator import add, mul, sub from operator import floordiv as div DIGITS = re.compile(r"-?\d+") + OPERATORS = ( (mul, re.compile(r"(?P.*) multiplied by (?P.*)")), (div, re.compile(r"(?P.*) divided by (?P.*)")), (add, re.compile(r"(?P.*) plus (?P.*)")), (sub, re.compile(r"(?P.*) minus (?P.*)")), - ) +) def answer(question): - if not question.startswith( "What is") or "cubed" in question: + if not question.startswith("What is") or "cubed" in question: raise ValueError("unknown operation") - - question = question.removeprefix( "What is").removesuffix("?").strip() + + question = question.removeprefix("What is").removesuffix("?").strip() if not question: raise ValueError("syntax error") - + return calculate(question) def calculate(question): if DIGITS.fullmatch(question): return int(question) - + for operation, pattern in OPERATORS: - if match := pattern.match(question): - return operation(calculate(match['x']), calculate(match['y'])) #<-- the loop is paused here to make the two recursive calls. + if matches := pattern.match(question): + return operation(calculate(matches["x"]), calculate(matches["y"])) # <-- The loop is paused here to make the two recursive calls. raise ValueError("syntax error") ``` -This solution uses a `tuple` of nested `tuples` containing the operators from `operator` and regex in place of the dictionaries that have been used in the previous approaches. -This saves some space, but requires that the nested `tuples` be unpacked as the main `tuple` is iterated over (_note the `for operation, pattern in OPERATORS:` in the `for-loop`_ ) so that operations can be matched to strings in the question. - The regex is also more generic than the example above (_anything before and after the operation words is allowed_). +This solution uses a `tuple` of nested `tuple`s containing the operators and the regex in place of the dictionaries that have been used in the previous approaches. +This saves some space, but requires that the nested `tuple`s be unpacked as the main `tuple` is iterated over (_note the `for operation, pattern in OPERATORS:` in the `for-loop`_) so that operations can be matched to strings in the question. +The regex is also more generic than the example above (_anything before and after the operation words is allowed_). Recursion is used a bit differently here from the previous variations — the calls are placed [within the `for-loop`][recursion-within-loops]. -Because the regex are more generic, they will match a `digit-operation-digit` trio in a longer question, so the line `return operation(calculate(match['x']), calculate(match['y']))` is effectively splitting a question into parts that can then be worked on in their own stack frames. +Because the regex are more generic, they will match a `digit-operation-digit` trio in a longer question, so the line `return operation(calculate(matches["x"]), calculate(matches["y"]))` is effectively splitting a question into parts that can then be worked on in their own stack frames. For example: 1. "1 plus -10 multiplied by 13 divided by 2" would match on "1 plus -10" (_group x_) **multiplied by** "13 divided by 2" (_group y_). -2. This is re-arranged to `mul(calculate("1 plus -10"), calculate("13 divided by 2"))` -3. At this point, the loop pauses as the two recursive calls to `calculate()` spawn +2. This is re-arranged to `mul(calculate("1 plus -10"), calculate("13 divided by 2"))`. +3. At this point, the loop pauses as the two recursive calls to `calculate()` spawn. 4. The loop runs again — and so do the calls to `calculate()` — until there isn't any match that splits the question or any errors. 5. One at a time, the numbers are returned from the `calculate()` calls on the stack, until the main `mul(calculate("1 plus -10"), calculate("13 divided by 2"))` is solved, at which point the answer is returned. -For a more visual picture, you can step through the code on [pythontutor.com][recursion-in-loop-pythontutor]. +For a more visual picture of the process (including how it fails on long inputs), you can step through the code on [pythontutor.com][recursion-in-loop-pythontutor]. [amortization]: https://www.investopedia.com/terms/a/amortization.asp [approach-import-callables-from-operator]: https://exercism.org/tracks/python/exercises/wordy/approaches/import-callables-from-operator [backtracking]: https://en.wikipedia.org/wiki/Backtracking +[base-case]: https://en.wikipedia.org/wiki/Recursion_(computer_science)#Base_case [bellman-ford]: https://www.programiz.com/dsa/bellman-ford-algorithm [bfs]: https://en.wikipedia.org/wiki/Breadth-first_search [bisection-search]: https://en.wikipedia.org/wiki/Bisection_method @@ -272,7 +278,7 @@ For a more visual picture, you can step through the code on [pythontutor.com][re [re-match]: https://docs.python.org/3/library/re.html#re.match [re]: https://docs.python.org/3/library/re.html [recursion-and-iteration]: https://web.mit.edu/6.102/www/sp23/classes/11-recursive-data-types/recursion-and-iteration-review.html#:~:text=The%20converse%20is%20also%20true,we%20are%20trying%20to%20solve. -[recursion-in-loop-pythontutor]: https://pythontutor.com/render.html#code=import%20re%0Afrom%20operator%20import%20add,%20mul,%20sub%0Afrom%20operator%20import%20floordiv%20as%20div%0A%0ADIGITS%20%3D%20re.compile%28r%22-%3F%5Cd%2B%22%29%0AOPERATORS%20%3D%20%28%0A%20%20%20%20%28mul,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20multiplied%20by%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%28div,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20divided%20by%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%28add,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20plus%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%28sub,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20minus%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%29%0A%0Adef%20answer%28question%29%3A%0A%20%20%20%20if%20not%20question.startswith%28%20%22What%20is%22%29%20or%20%22cubed%22%20in%20question%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22unknown%20operation%22%29%0A%20%20%20%20%0A%20%20%20%20question%20%3D%20question.removeprefix%28%20%22What%20is%22%29.removesuffix%28%22%3F%22%29.strip%28%29%0A%0A%20%20%20%20if%20not%20question%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%20%20%20%20%0A%20%20%20%20return%20calculate%28question%29%0A%0Adef%20calculate%28question%29%3A%0A%20%20%20%20if%20DIGITS.fullmatch%28question%29%3A%0A%20%20%20%20%20%20%20%20return%20int%28question%29%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20for%20operation,%20pattern%20in%20OPERATORS%3A%0A%20%20%20%20%20%20%20%20if%20match%20%3A%3D%20pattern.match%28question%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20operation%28calculate%28match%5B'x'%5D%29,%20calculate%28match%5B'y'%5D%29%29%20%23%3C--%20the%20loop%20is%20paused%20here%20to%20make%20the%20two%20recursive%20calls.%0A%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%0Aprint%28answer%28%22What%20is%201%20plus%20-10%20multiplied%20by%2013%20divided%20by%202%3F%22%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false +[recursion-in-loop-pythontutor]: https://pythontutor.com/visualize.html#code=import%20re%0Afrom%20operator%20import%20add,%20mul,%20sub%0Afrom%20operator%20import%20floordiv%20as%20div%0A%0ADIGITS%20%3D%20re.compile%28r%22-%3F%5Cd%2B%22%29%0A%0AOPERATORS%20%3D%20%28%0A%20%20%20%20%28mul,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20multiplied%20by%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%28div,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20divided%20by%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%28add,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20plus%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%20%20%20%20%28sub,%20re.compile%28r%22%28%3FP%3Cx%3E.*%29%20minus%20%28%3FP%3Cy%3E.*%29%22%29%29,%0A%29%0A%0Adef%20answer%28question%29%3A%0A%20%20%20%20if%20not%20question.startswith%28%22What%20is%22%29%20or%20%22cubed%22%20in%20question%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22unknown%20operation%22%29%0A%0A%20%20%20%20question%20%3D%20question.removeprefix%28%22What%20is%22%29.removesuffix%28%22%3F%22%29.strip%28%29%0A%0A%20%20%20%20if%20not%20question%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%0A%20%20%20%20return%20calculate%28question%29%0A%0Adef%20calculate%28question%29%3A%0A%20%20%20%20if%20DIGITS.fullmatch%28question%29%3A%0A%20%20%20%20%20%20%20%20return%20int%28question%29%0A%0A%20%20%20%20for%20operation,%20pattern%20in%20OPERATORS%3A%0A%20%20%20%20%20%20%20%20if%20matches%20%3A%3D%20pattern.match%28question%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20operation%28calculate%28matches%5B%22x%22%5D%29,%20calculate%28matches%5B%22y%22%5D%29%29%20%23%20%3C--%20the%20loop%20is%20paused%20here%20to%20make%20the%20two%20recursive%20calls.%0A%20%20%20%20raise%20ValueError%28%22syntax%20error%22%29%0A%0Aprint%28answer%28%22What%20is%201%20plus%20-10%20multiplied%20by%203%20minus%204%3F%22%29%29%0A%0Aprint%28%22Answer%20should%20be%3A%22,%20%28%281%20%2B%20-10%29%20*%203%29%20-%204%29%0A%0Aprint%28%22Result%20from%20wrong%20order%3A%22,%20%281%20%2B%20-10%29%20*%20%283%20-%204%29%29&curInstr=0&mode=display&origin=opt-frontend.js&py=311 [recursion-is-not-a-superpower]: https://inventwithpython.com/blog/2021/09/05/recursion-is-not-a-superpower-an-iterative-ackermann/ [recursion-within-loops]: https://stackoverflow.com/questions/4795527/how-recursion-works-inside-a-for-loop [tail-call-optimization]: https://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html diff --git a/exercises/practice/wordy/.approaches/recursion/snippet.txt b/exercises/practice/wordy/.approaches/recursion/snippet.txt index 373481f8f4..0a6a15c20d 100644 --- a/exercises/practice/wordy/.approaches/recursion/snippet.txt +++ b/exercises/practice/wordy/.approaches/recursion/snippet.txt @@ -1,8 +1,8 @@ def calculate(equation): if len(equation) == 1: return int(equation[0]) - else: - try: - x_value, operation, y_value, *rest = equation - equation = [OPERATIONS[operation](int(x_value), int(y_value)), *rest] - except: raise ValueError("syntax error") - return calculate(equation) \ No newline at end of file + try: + x_value, operation, y_value, *rest = equation + equation = [OPERATIONS[operation](int(x_value), int(y_value)), *rest] + except: + raise ValueError("syntax error") + return calculate(equation) \ No newline at end of file diff --git a/exercises/practice/wordy/.approaches/regex-with-operator-module/content.md b/exercises/practice/wordy/.approaches/regex-with-operator-module/content.md index d3d5c21430..0bf7362cf1 100644 --- a/exercises/practice/wordy/.approaches/regex-with-operator-module/content.md +++ b/exercises/practice/wordy/.approaches/regex-with-operator-module/content.md @@ -1,5 +1,4 @@ -# Regex and the Operator Module - +# Regex with the `operator` Module ```python import re @@ -9,85 +8,85 @@ from operator import floordiv as div OPERATIONS = {"plus": add, "minus": sub, "multiplied by": mul, "divided by": div} REGEX = { - 'number': re.compile(r'-?\d+'), - 'operator': re.compile(f'(?:{"|".join(OPERATIONS)})\\b') + "number": re.compile(r"-?\d+"), + "operator": re.compile(f"(?:{'|'.join(OPERATIONS)})\\b") } # Helper function to extract a number from the question. def get_number(question): # Match a number. - pattern = REGEX['number'].match(question) - + pattern = REGEX["number"].match(question) + # Toss an error if there is no match. if not pattern: raise ValueError("syntax error") - - # Remove the matched pattern from the question, and convert - # that same pattern to an int. Return the modified question and the int. - return [question.removeprefix(pattern.group(0)).lstrip(), + + # Remove the matched pattern from the question, and convert that + # same pattern to an int. Return the modified question and the int. + return [question.removeprefix(pattern.group(0)).lstrip(), int(pattern.group(0))] # Helper function to extract an operation from the question. def get_operation(question): - # Match an operation word - pattern = REGEX['operator'].match(question) - + # Match an operation word. + pattern = REGEX["operator"].match(question) + # Toss an error if there is no match. if not pattern: raise ValueError("unknown operation") - - # Remove the matched pattern from the question, and look up - # that same pattern in OPERATIONS. Return the modified question and the operator. + + # Remove the matched pattern from the question, and look up that same + # pattern in OPERATIONS. Return the modified question and the operator. return [question.removeprefix(pattern.group(0)).lstrip(), OPERATIONS[pattern.group(0)]] def answer(question): prefix = "What is" - + # Toss an error right away if the question isn't valid. if not question.startswith(prefix): raise ValueError("unknown operation") - + # Clean the question by removing the suffix and prefix and whitespace. question = question.removesuffix("?").removeprefix(prefix).lstrip() - # the question should start with a number + # The question should start with a number. question, result = get_number(question) # While there are portions of the question left, continue to process. while len(question) > 0: - # can't have a number followed by a number - if REGEX['number'].match(question): + # Can't have a number followed by a number. + if REGEX["number"].match(question): raise ValueError("syntax error") # Call get_operation and unpack the result # into question and operation. question, operation = get_operation(question) - - # Call get_number and unpack the result + + # Call get_number and unpack the result # into question and num question, num = get_number(question) - # Perform the calculation, using result and num as - # arguments to operation. + # Perform the calculation, using result and num + # as arguments to operation. result = operation(result, num) return result ``` -This approach uses two dictionaries: one of operations imported from `operators`, and another that holds regex for matching digits and matching operations in the text of a question. +This approach uses two dictionaries: one of operations imported from `operators`, and another that holds regex for matching digits and matching operations in the text of a question. -It defines two "helper" functions, `get_number()` and `get_operation`, that take a question and use the regex patterns to remove, convert, and return a number (_`get_number()`_) or an operation (_`get_operation()`_), along with a modified "new question". +It defines two "helper" functions (`get_number()` and `get_operation()`), that both take a question and use the regex patterns to remove, convert, and return a number (_`get_number()`_) or an operation (_`get_operation()`_), along with a modified "new question". -In the `answer()` function, the question is checked for validity (_does it start with "What is"_), and a `ValueError("unknown operation")` it raised if it is not a valid question. -Next, the question is cleaned with [`str.removeprefix`][removeprefix] & [`str.removesuffix`][removesuffix], removing "What is" and "?". -Left-trailing white space is stripped with the help of [`lstrip()`][lstrip]. +In the `answer()` function, the question is checked for validity (_if it starts with "What is"_), and a `ValueError("unknown operation")` it raised if it is not a valid question. +Next, the question is cleaned with [`str.removeprefix`][removeprefix] and [`str.removesuffix`][removesuffix], removing "What is" and "?". +Left-trailing whitespace is stripped with the help of [`str.lstrip()`][lstrip]. After that, the variable `result` is declared with an initial value from `get_number()`. The question is then iterated over via a `while-loop`, which calls `get_operation()` and `get_number()` — "reducing" the question by removing the leading numbers and operator. The return values from each call are [unpacked][unpacking] into a "leftover" question portion, and the number or operator. -The returned operation is then made [callable][callable] using `()`, with result and the "new" number (_returned from `get_number()`_) passed as arguments. -The `loop` then proceeds with processing of the "new question", until the `len()` is 0. +The returned operation is then used as a [callable][callable] by putting `()` after the name, with `result` and the "new" number (_returned from `get_number()`_) passed as arguments. +The loop then proceeds with the processing of the "new question", until the `len()` is 0. Once there is no more question to process, `result` is returned as the answer. diff --git a/exercises/practice/wordy/.approaches/regex-with-operator-module/snippet.txt b/exercises/practice/wordy/.approaches/regex-with-operator-module/snippet.txt index 4d89edb537..e9f549370a 100644 --- a/exercises/practice/wordy/.approaches/regex-with-operator-module/snippet.txt +++ b/exercises/practice/wordy/.approaches/regex-with-operator-module/snippet.txt @@ -1,5 +1,5 @@ while len(question) > 0: - if REGEX['number'].match(question): + if REGEX["number"].match(question): raise ValueError("syntax error") question, operation = get_operation(question) diff --git a/exercises/practice/wordy/.approaches/string-list-and-dict-methods/content.md b/exercises/practice/wordy/.approaches/string-list-and-dict-methods/content.md index cce88a4bb0..eaa5b33404 100644 --- a/exercises/practice/wordy/.approaches/string-list-and-dict-methods/content.md +++ b/exercises/practice/wordy/.approaches/string-list-and-dict-methods/content.md @@ -1,6 +1,5 @@ # String, List, and Dictionary Methods - ```python def answer(question): if not question.startswith("What is") or "cubed" in question: @@ -43,33 +42,34 @@ This eliminates all the [current cases][unknown-operation-tests] where a [`Value Should the definition of a question expand or change, this strategy would need to be revised. -The question is then "cleaned" by removing the prefix "What is" and the suffix "?" ([`str.removeprefix`][removeprefix], [`str.removesuffix`][removesuffix]), replacing "by" with "" ([`str.replace`][str-replace]), and [stripping][strip] any leading or trailing whitespaces. +The question is then "cleaned" by removing the prefix `"What is"` and the suffix `"?"` ([`str.removeprefix`][removeprefix], [`str.removesuffix`][removesuffix]), replacing `"by"` with `""` ([`str.replace`][str-replace]), and [stripping][strip] any leading or trailing whitespace. If the question is now an empty string, a `ValueError("syntax error")` is raised. -The remaining question string is then converted into a `list` of elements via [`str.split`][split], and that `list` is iterated over using a `while-loop` with a `len()` > 1 condition. +The remaining question string is then converted into a `list` of elements via [`str.split`][split], and that `list` is iterated over using a `while-loop` with a `len() > 1` condition. Within a [`try-except`][handling-exceptions] block to trap/handle any errors (_which will all map to `ValueError("syntax error")`_), the question `list` is divided up among 4 variables using [bracket notation][bracket-notation]: 1. The first element, `x_value`. This is assumed to be a number, so it is converted to an `int()` 2. The third element, `y_value`. This is also assumed to be a number and converted to an `int()`. -3. The second element, `symbol`. This is assumed to be an operator, and is left as-is. -4. The `remainder` of the question, if there is any. This is a [slice][list-slice] starting at index 3, and going to the end. +3. The second element, `symbol`. This is assumed to be an operator, and is left as-is. +4. The `remainder` of the question, if there is any. This is a [slice][list-slice] starting at index 3 and going to the end. -`symbol` is then tested for "plus, minus, multiplied, or divided", and the `formula` list is modified by applying the given operation, and creating a new `formula` `list` by concatenating a `list` of the first product with the `remainder` list. +`symbol` is then tested for "plus", "minus", "multiplied", or "divided", and the `formula` list is modified by applying the given operation, and creating a new `formula` `list` by concatenating a `list` of the first product with the `remainder` list. If `symbol` doesn't match any known operators, a `ValueError("syntax error")` is raised. +(Note that this is still inside the `try-except` block, so the [exception is chained][exception-chaining].) Once `len(formula) == 1`, the first element (`formula[0]`) is converted to an `int()` and returned as the answer. +
-## Variation 1: Use a Dictionary for Lookup/Replace - +## Variation 1: Use a Dictionary for Lookup/Replace ```python -OPERATIONS = {"plus": '+', "minus": '-', "multiplied": '*', "divided": '/'} +OPERATIONS = {"plus": "+", "minus": "-", "multiplied": "*", "divided": "/"} def answer(question): @@ -108,73 +108,67 @@ def answer(question): return int(formula[0]) ``` +~~~~exercism/note +[Method chaining][method-chaining] is used in the clean step for this variation, and is the equivalent of assigning and re-assigning `question` as is done in the initial approach. +This is because `str.startswith`, `str.endswith`, and `str.replace` all return strings, so the output of one can be used as the input to the next. -````exercism/note -[chaining][method-chaining] is used in the clean step for this variation, and is the equivalent of assigning and re-assigning `question` as is done in the initial approach. - This is because `str.startswith`, `str.endswith`, and `str.replace` all return strings, so the output of one can be used as the input to the next. - - [method-chaining]: https://www.tutorialspoint.com/Explain-Python-class-method-chaining -```` - +[method-chaining]: https://www.tutorialspoint.com/Explain-Python-class-method-chaining +~~~~ This variation creates a dictionary to map operation words to symbols. It pre-processes the question string into a `formula` list by looking up the operation words and replacing them with the symbols via the [`.get`][dict-get] method, which takes a [default argument][default-argument] for when a [`KeyError`][keyerror] is thrown. -Here the default for `dict.get()` is set to the element being iterated over, which is effectively _"if not found, skip it"_. -This means the number strings will be passed through, even though they would otherwise toss an error. - The results of iterating through the question are appended to `formula` via [`list.append`][list-append]. +Here the default for `dict.get()` is set to the element being iterated over, which is effectively _"if not found, skip it"_. +This means that the number strings will be passed through, even though they would otherwise toss an error. +The results of iterating through the question are appended to `formula` via [`list.append`][list-append]. -This dictionary is not necessary, but does potentially make adding/tracking future operations easier, although the `if-elif-else` block in the `while-loop` is equally awkward for maintenance (_see the [import callables from operator][approach-import-callables-from-operator] for a way to replace the block_). +This dictionary is not necessary, but does potentially make adding/tracking future operations easier, although the `if-elif-else` block in the `while-loop` is equally awkward for maintenance (_see the [import callables from operator approach][approach-import-callables-from-operator] for a way to replace the block_). The `while-loop`, `if-elif-else` block, and the `try-except` block are then the same as in the initial approach. - -````exercism/note +~~~~exercism/note There are a couple of common alternatives to the `loop-append` used here: -1. [`list-comprehensions`][list-comprehension] duplicate the same process in a more succinct and declarative fashion. This one also includes filtering out "by": +1. [List comprehensions][list-comprehension] duplicate the same process in a more succinct and declarative fashion. This one also includes filtering out "by": ```python - - formula = [OPERATIONS.get(operation, operation) for - operation in question.split() if operation != 'by'] - ``` + formula = [OPERATIONS.get(operation, operation) for + operation in question.split() if operation != "by"] + ``` -2. The built-in [`filter()`][filter] and [`map()`][map] functions used with a [`lambda`][lambdas] to process the elements of the list. - This is identical in process to both the `loop-append` and the `list-comprehension`, but might be easier to reason about for those coming from a more functional programming language: +2. The built-in [`filter()`][filter] and [`map()`][map] functions used with a [`lambda`][lambdas] to process the elements of the list. + This is identical in process to both the `loop-append` and the list comprehension, but might be easier to reason about for those coming from a more functional programming language: ```python - formula = list(map(lambda x : OPERATIONS.get(x, x), - filter(lambda x: x != "by", question.split()))) + formula = list(map(lambda op: OPERATIONS.get(op, op), + filter(lambda op: op != "by", question.split()))) ``` [list-comprehension]: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions [lambdas]: https://docs.python.org/3/howto/functional.html#small-functions-and-the-lambda-expression [filter]: https://docs.python.org/3/library/functions.html#filter [map]: https://docs.python.org/3/library/functions.html#map -```` - - Rather than indexing and slicing, [concept: unpacking and multiple assignment](/tracks/python/concepts/unpacking-and-multiple-assignment) can be used to assign the variables. - However, this does require a modification to the returned formula `list`: +~~~~ +Rather than indexing and slicing, [concept:python/unpacking-and-multiple-assignment]() can be used to assign the variables. +However, this does require a modification to the returned formula `list`: - ```python - x_value, operation, y_value, *remainder = formula # <-- Unpacking won't allow conversion to int() here. +```python + x_value, symbol, y_value, *remainder = formula # <-- Unpacking won't allow conversion to int() here. ... - if symbol == "+": - formula = [int(x_value) + int(y_value)] + remainder # <-- Instead, conversion to int() must happen here. + if symbol == "+": + formula = [int(x_value) + int(y_value)] + remainder # <-- Instead, conversion to int() must happen here. ... - return int(formula[0]) - ``` - + return int(formula[0]) +``` -## Variation 2: Structural Pattern Matching to Replace `if-elif-else` +
+## Variation 2: Structural Pattern Matching to Replace `if-elif-else` Introduced in Python 3.10, [structural pattern matching][structural-pattern-matching] can be used to replace the `if-elif-else` chain in the `while-loop` used in the two approaches above. -In some circumstances, this could be easier to read and/or reason about: - +In some circumstances, this could be easier to read and/or reason about: ```python def answer(question): @@ -189,29 +183,31 @@ def answer(question): formula = question.split() while len(formula) > 1: try: - x_value, symbol, y_value, *remainder = formula #<-- unpacking and multiple assignment. + x_value, symbol, y_value, *remainder = formula # <-- Unpacking and multiple assignment. match symbol: - case "plus": + case "plus": formula = [int(x_value) + int(y_value)] + remainder - case "minus": + case "minus": formula = [int(x_value) - int(y_value)] + remainder - case "multiplied": + case "multiplied": formula = [int(x_value) * int(y_value)] + remainder - case "divided": + case "divided": formula = [int(x_value) / int(y_value)] + remainder - case _: - raise ValueError("syntax error") #<-- "fall through case for no match." - except: raise ValueError("syntax error") # <-- error handling for anything else that goes wrong. + case _: + raise ValueError("syntax error") # <-- Fall through case for no match. + except: + raise ValueError("syntax error") # <-- Error handling for anything else that goes wrong. return int(formula[0]) -``` +``` [approach-import-callables-from-operator]: https://exercism.org/tracks/python/exercises/wordy/approaches/import-callables-from-operator [bracket-notation]: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations [default-argument]: https://docs.python.org/3/tutorial/controlflow.html#default-argument-values [dict-get]: https://docs.python.org/3/library/stdtypes.html#dict.get [endswith]: https://docs.python.org/3.9/library/stdtypes.html#str.endswith +[exception-chaining]: https://docs.python.org/3/tutorial/errors.html#exception-chaining [handling-exceptions]: https://docs.python.org/3.11/tutorial/errors.html#handling-exceptions [keyerror]: https://docs.python.org/3/library/exceptions.html#KeyError [list-append]: https://docs.python.org/3/tutorial/datastructures.html#more-on-lists diff --git a/exercises/practice/wordy/.approaches/string-list-and-dict-methods/snippet.txt b/exercises/practice/wordy/.approaches/string-list-and-dict-methods/snippet.txt index 700804b6d1..ccf9cc3052 100644 --- a/exercises/practice/wordy/.approaches/string-list-and-dict-methods/snippet.txt +++ b/exercises/practice/wordy/.approaches/string-list-and-dict-methods/snippet.txt @@ -1,8 +1,8 @@ try: x_value, y_value, symbol, remainder = int(formula[0]), int(formula[2]), formula[1], formula[3:] - if symbol == "+": formula = [x_value + y_value] + remainder - elif symbol == "-": formula = [x_value - y_value] + remainder - elif symbol == "*": formula = [x_value * y_value] + remainder - elif symbol == "/": formula = [x_value / y_value] + remainder + if symbol == "+": formula = [x_value + y_value] + remainder + elif symbol == "-": formula = [x_value - y_value] + remainder + elif symbol == "*": formula = [x_value * y_value] + remainder + elif symbol == "/": formula = [x_value / y_value] + remainder else: raise ValueError("syntax error") except: raise ValueError("syntax error") \ No newline at end of file diff --git a/exercises/practice/wordy/.docs/instructions.append.md b/exercises/practice/wordy/.docs/instructions.append.md index d26afab5ff..c5972700aa 100644 --- a/exercises/practice/wordy/.docs/instructions.append.md +++ b/exercises/practice/wordy/.docs/instructions.append.md @@ -20,14 +20,14 @@ raise ValueError("syntax error") ``` To _handle_ a raised error within a particular code block, one can use a [try-except][handling-exceptions]. - `try-except` blocks "wrap" the code that could potentially cause an error, mapping all the exceptions to one error, multiple errors, or other pieces of code to deal with the problem: +`try-except` blocks "wrap" the code that could potentially cause an error, mapping all the exceptions to one error, multiple errors, or other pieces of code to deal with the problem: ```python while len(equation) > 1: - try: + try: # The questionable/error-prone code goes here,in an indented block - # It can contain statements, loops, if-else blocks, or other executable code. + # It can contain statements, loops, if-else blocks, or other executable code. x_value, operation, y_value, *rest = equation ... ... From dece97433c3b62d80df8a42bcb7e56567fab51a2 Mon Sep 17 00:00:00 2001 From: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> Date: Thu, 21 May 2026 22:54:10 -0400 Subject: [PATCH 47/47] [Wordy] Add additional tests for long questions (#4201) * add additional tests for long questions * add self to contributors array --- .../wordy/.meta/additional_tests.json | 24 +++++++++++++++++++ exercises/practice/wordy/.meta/config.json | 3 ++- exercises/practice/wordy/wordy_test.py | 13 +++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/exercises/practice/wordy/.meta/additional_tests.json b/exercises/practice/wordy/.meta/additional_tests.json index 089dd45046..447b532c05 100644 --- a/exercises/practice/wordy/.meta/additional_tests.json +++ b/exercises/practice/wordy/.meta/additional_tests.json @@ -1,5 +1,29 @@ { "cases": [ + { + "description": "4 number question", + "property": "answer", + "input": { + "question": "What is 1 plus -10 multiplied by 3 minus 4?" + }, + "expected": -31 + }, + { + "description": "4 number question with zero", + "property": "answer", + "input": { + "question": "What is 12 minus 0 divided by 6 plus 5?" + }, + "expected": 7 + }, + { + "description": "5 number question", + "property": "answer", + "input": { + "question": "What is 3 multiplied by 6 minus 2 divided by 4 plus 11?" + }, + "expected": 15 + }, { "description": "Missing operation", "property": "answer", diff --git a/exercises/practice/wordy/.meta/config.json b/exercises/practice/wordy/.meta/config.json index 07c6341707..33783d1737 100644 --- a/exercises/practice/wordy/.meta/config.json +++ b/exercises/practice/wordy/.meta/config.json @@ -21,7 +21,8 @@ "rootulp", "sjakobi", "tqa236", - "yawpitch" + "yawpitch", + "yrahcaz7" ], "files": { "solution": [ diff --git a/exercises/practice/wordy/wordy_test.py b/exercises/practice/wordy/wordy_test.py index c34ed27cda..b49b1e7120 100644 --- a/exercises/practice/wordy/wordy_test.py +++ b/exercises/practice/wordy/wordy_test.py @@ -1,6 +1,6 @@ # These tests are auto-generated with test data from: # https://github.com/exercism/problem-specifications/tree/main/exercises/wordy/canonical-data.json -# File last updated on 2025-06-20 +# File last updated on 2026-05-22 import unittest @@ -111,6 +111,17 @@ def test_reject_prefix_notation(self): # Additional tests for this track + def test_4_number_question(self): + self.assertEqual(answer("What is 1 plus -10 multiplied by 3 minus 4?"), -31) + + def test_4_number_question_with_zero(self): + self.assertEqual(answer("What is 12 minus 0 divided by 6 plus 5?"), 7) + + def test_5_number_question(self): + self.assertEqual( + answer("What is 3 multiplied by 6 minus 2 divided by 4 plus 11?"), 15 + ) + def test_missing_operation(self): with self.assertRaises(ValueError) as err: answer("What is 2 2 minus 3?")