Function extraction moves a coherent block of code into a separately named function. The name clarifies behavior, reduces duplication, and makes each concern easier to understand independently. section 2.2.1 section 2.3
Clarity and separation matter more than immediate reuse. After extracting a function, generalize arguments when the code mixes a specific value with a reusable operation, then extract smaller responsibilities when one function still does too much. section 2.2.1 section 9.2.2
A practical extraction sequence:
- Identify a coherent block and give its behavior a clear name. section 2.2.1
- Move the block into a function with the values it needs as parameters. section 2.2.1
- Separate fixed details from general behavior by extracting them into arguments. section 2.2.1
- Extract further functions when decision-making and implementation details grow together. section 9.2.2
- Preserve the function’s inputs and expected behavior while improving its body. section 2.2.1
From a specific introduction to a reusable one
Instead of duplicating a loop for different lineups, define introduce(title, names). The same function can introduce the Three Stooges, the Teenage Mutant Ninja Turtles, or the Chipmunks. A second extraction, join_names(names), can isolate name-list formatting from the group introduction. section 2.2.1
Extraction in application code
A random-food endpoint can map response formats to functions such as to_json and to_xml, then delegate format selection to get_format_function. Each function has a smaller responsibility and lower cyclomatic complexity. section 9.2.2
def introduce_stooges(names): #1
message = 'The Three Stooges: '
for index, name in enumerate(names):
if index > 0:
message += ', '
if index == len(names) - 1:
message += 'and '
message += name
print(message)
introduce_stooges(['Moe', 'Larry', 'Shemp']) #2
introduce_stooges(['Larry', 'Curly', 'Moe'])
Compare duplicated introduction logic with a named introduce_stooges function and its reusable calls. section 2.2.1
Listing 2.2. Code with extracted functions
import random
OPTIONS = ['rock', 'paper', 'scissors']
def get_computer_choice():
return random.choice(OPTIONS)
def get_human_choice():
choice_number = int(input('Enter the number of your choice: '))
return OPTIONS[choice_number - 1]
def print_options():
print('\n'.join(f'({i}) {option.title()}' for i,
option in enumerate(OPTIONS)))
def print_choices(human_choice, computer_choice):
print(f'You chose {human_choice}')
print(f'The computer chose {computer_choice}')
def print_win_lose(human_choice, computer_choice, human_beats,
human_loses_to):
if computer_choice == human_loses_to:
print(f'Sorry, {computer_choice} beats {human_choice}')
elif computer_choice == human_beats:
print(f'Yes, {human_choice} beats {computer_choice}!')
def print_result(human_choice, computer_choice):
if human_choice == computer_choice:
print('Draw!')
if human_choice == 'rock':
print_win_lose('rock', computer_choice, 'scissors', 'paper')
elif human_choice == 'paper':
print_win_lose('paper', computer_choice, 'rock', 'scissors')
elif human_choice == 'scissors':
print_win_lose('scissors', computer_choice, 'paper', 'rock')
print_options()
human_choice = get_human_choice()
computer_choice = get_computer_choice()
print_choices(human_choice, computer_choice)
print_result(human_choice, computer_choice)
See a large procedural rock-paper-scissors flow divided into functions for choices, options, output, and results. section 2.2.1
