I have a Python function that will terminate when some condition is fulfilled.
That could happen after the first (easy) step. But more steps (of increasing complexity) may follow.
Below is the simplified example function find_slugs
.
For each person in database
it finds a slug, which is unique and as short as possible.
To avoid code repetition it was necessary to create the subfunction maybe_finish
.
It checks if the slugs are already unique, and returns the result if they are.
Otherwise it prepares the next step by creating blocks_to_refine
, and returns None
.
Between the steps are conditional returns, contracted to one line:
if maybe_result := maybe_finish(): return maybe_result
In three lines it would look like this:
maybe_result = maybe_finish()
if maybe_result is not None:
return maybe_result
Repeating this line does not seem like an elegant solution to me.
(It is essentially repeating three lines of code again and again.)
I would be interested in different approaches to this example problem.
For the given list of people the result is {0: 'JohnSpam85a', 1: 'JohnSpam85b', 2: 'JohnEggs91', 3: 'JohnEggs92', 4: 'EmmaFish95a', 5: 'EmmaFish95b', 6: 'MaryBeer', 7: 'MaryWine', 8: 'Owen', 9: 'Ruth'}
. For the first three it is {0: 'JohnSpam85a', 1: 'JohnSpam85b', 2: 'JohnEggs'}
. For the last three {7: 'Mary', 8: 'Owen', 9: 'Ruth'}
.
(There is a corresponding question on Stackoverflow.)
from collections import defaultdict
database = {
0: {'name1': 'John', 'name2': 'Spam', 'born': '1985'}, # JohnSpam85a
1: {'name1': 'John', 'name2': 'Spam', 'born': '1985'}, # JohnSpam85b
2: {'name1': 'John', 'name2': 'Eggs', 'born': '1991'}, # JohnEggs91
3: {'name1': 'John', 'name2': 'Eggs', 'born': '1992'}, # JohnEggs92
4: {'name1': 'Emma', 'name2': 'Fish', 'born': '1995'}, # EmmaFish95a
5: {'name1': 'Emma', 'name2': 'Fish', 'born': '1995'}, # EmmaFish95b
6: {'name1': 'Mary', 'name2': 'Beer', 'born': '2000'}, # MaryBeer
7: {'name1': 'Mary', 'name2': 'Wine', 'born': '2000'}, # MaryWine
8: {'name1': 'Owen', 'name2': 'Wine', 'born': '2000'}, # Owen
9: {'name1': 'Ruth', 'name2': 'Milk', 'born': '2000'} # Ruth
}
alphabetical_letters = ['a', 'b', 'c', 'd']
def find_slugs():
def maybe_finish():
# A block is a list of numbers with (currently) the same slug.
slug_to_block = defaultdict(list)
for number, slug in number_to_slug.items():
slug_to_block[slug].append(number)
if len(slug_to_block) == len(database):
return number_to_slug
nonlocal blocks_to_refine
blocks_to_refine = []
for block in slug_to_block.values():
if len(block) > 1:
blocks_to_refine.append(block)
##################################################################
blocks_to_refine = None # created and updated in `maybe_finish`
number_to_slug = dict() # result
# step 1: start with first name
for number, table_row in database.items():
number_to_slug[number] = table_row['name1']
if maybe_result := maybe_finish(): return maybe_result
# step 2: add last name
for block in blocks_to_refine:
for number in block:
number_to_slug[number] += database[number]['name2']
if maybe_result := maybe_finish(): return maybe_result
# step 3: add year of birth
for block in blocks_to_refine:
for number in block:
number_to_slug[number] += database[number]['born'][2:4]
if maybe_result := maybe_finish(): return maybe_result
# step 4: append letter to year of birth
for block in blocks_to_refine:
for i, number in enumerate(block):
number_to_slug[number] += alphabetical_letters[i]
return number_to_slug