Function / Definition / Call

6주차 1교시: 함수 정의와 호출

같은 그림 세 장의 수정 문제를 해결하며, 함수의 필요성과 정의·호출을 익힙니다.

여섯 종이 도형을 파란 함수 틀로 묶고 같은 배열을 결과 카드로 꺼낸 6주차 1교시 표지
함수의 개념 · 여섯 도형을 하나의 작업으로 묶고 호출

함수의 구조와 재사용

학습 방식
개념 해설 · 코드 시연 · 단계별 실습
출발점
5주차의 반복문·조건문과 여섯 도형
결과물
이미지 함수 · 선택·조합 · 배지 함수
다음 학습
매개변수와 반환값

학습 목표

  1. 코드 중복의 문제를 함수의 재사용과 연결한다.
  2. 정의·호출·들여쓰기와 작업 준비를 구분하고 이미지 함수를 구성한다.
  3. 생성·반복·표시의 위치에 따른 출력 차이를 설명한다.
  4. 조건에 따라 함수를 선택하고 여러 함수를 하나의 작업으로 조합한다.
  5. 새 이미지 코드의 작업 범위를 설계하고 공통 수정과 오류 진단을 수행한다.

학습 내용

공통 실습 노트북을 Colab의 파일 → 노트 업로드로 열고 Drive 사본을 week06_학번_이름.ipynb로 저장합니다. 1·2·3교시는 같은 사본에서 이어갑니다. 교재와 노트북의 절 제목 및 실습 번호는 같은 순서입니다.

이번 시간은 해설 → 코드 시연 → 결과 확인의 흐름으로 함수의 구조를 이해합니다. 각 실습에서 수정 전 코드와 완성 코드를 함께 보며 바뀐 줄의 역할을 확인합니다. A·B·C의 결과 비교도 시연과 함께 펼쳐 코드의 차이가 그림에 어떻게 나타나는지 봅니다. 오류 진단 셀은 메시지를 확인한 뒤 같은 셀을 수정합니다. 전체 실행은 모든 실습을 완성한 뒤에 수행합니다.

A·B·C 시각 변형

이미지 실습의 A·B·C는 같은 제작 원리로 만든 세 가지 결과입니다. 예를 들어 1-4B는 1교시 실습 4의 B 버전입니다. A를 기준으로 B·C의 변경 지점을 각각 비교합니다. 정의가 공통인 실습은 먼저 정의를 실행한 뒤 세 호출을 이어서 사용합니다. 실습 코드·완성 코드·결과 이미지는 해설과 함께 펼쳐 확인하며, 그림을 누르면 원본 크기로 볼 수 있습니다.

1. 함수의 필요성

핵심 질문

같은 그림의 강조색을 세 장 모두 바꾸려면 몇 곳을 수정해야 할까요?

코드 중복과 수정 누락

5주차에는 크기 목록을 반복하고 조건에 따라 사각형·채운 원·윤곽 원을 그렸습니다. 기준 배열은 30, 46, 64, 46, 60, 38이며 큰 경계는 60, 중간 경계는 40입니다. 같은 그림을 세 장 만들려고 이 작업을 A·B·C에 각각 복사한 상황에서 시작합니다.

전시 안내 카드의 강조색을 산호색에서 황토색으로 통일해 달라는 요청이 왔습니다. A·B는 수정했지만 C에는 이전 색이 남았습니다. 컴퓨터가 한 장의 색을 임의로 바꾼 것이 아니라, 세 복사본에 서로 다른 RGB 값이 적힌 결과입니다. 같은 규칙을 여러 곳에 두면 수정해야 할 위치도 함께 늘어납니다.

실행 결과 비교같은 코드와 입력으로 확인
수정 전 기준 · 여섯 도형과 산호색 사각형
수정 전 기준 · 여섯 도형과 산호색 사각형
복사본의 팔레트 비교 · 그리기 본문 생략
복사본의 팔레트 비교 · 그리기 본문 생략
palette_a = [(37, 62, 78), (111, 143, 126), (211, 155, 42)]
palette_b = [(37, 62, 78), (111, 143, 126), (211, 155, 42)]
palette_c = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]

노트북의 “복사본의 수정 누락” 셀에는 팔레트와 그리기 본문이 세 벌 있습니다. 해설에서 비교할 곳은 C의 세 번째 RGB 값입니다. 두 사각형이 palette[2]를 사용하므로 세 번째와 다섯 번째 도형에 이전 색이 남습니다. 이 연결을 코드와 이미지에서 함께 확인합니다.

실습 순서
  1. 기준 이미지와 세 복사본의 팔레트를 비교합니다.
  2. 수정이 빠진 C의 색을 고치고 같은 요청을 여러 곳에서 관리하는 문제를 기록합니다.

기록: 수정 누락 위치: ___ / 복사본 세 벌의 관리 문제: ___

복사본 비교 결과
실행 결과 비교같은 코드와 입력으로 확인
수정 전 A·B·C · C에만 남은 산호색
수정 전 A·B·C · C에만 남은 산호색

C의 (201, 111, 93)을 (211, 155, 42)로 고치면 당장의 누락은 해결됩니다. 그러나 다음 요청이 윤곽선 두께 변경이라면 세 본문에서 그리기 명령을 다시 찾아야 합니다. 색 하나의 실수가 아니라 공통 작업이 여러 곳에 흩어져 있다는 점이 구조의 문제입니다.

함수의 역할

함수는 어떤 작업을 수행하는 코드 묶음에 이름을 붙이는 방법입니다. 작업을 한 곳에 정의하고 필요한 위치에서 그 이름으로 호출합니다. 이미지 한 장을 완성하는 본문을 한 번만 두면, 공통 규칙의 수정은 그 본문에서 하고 새 결과는 호출로 만들 수 있습니다.

함수의 이점은 수정 위치를 줄이는 것과 작업의 의미를 드러내는 것입니다. show_shape_row()라는 호출은 도형 행을 보여 주는 작업이라는 뜻을 전달합니다. 호출하는 쪽에서는 모든 좌표와 색을 다시 읽지 않고도 전체 제작 순서를 파악할 수 있습니다. 함수로 묶는 것 자체가 그림의 색이나 모양을 바꾸는 것은 아닙니다.

도장과 함수개념 비유
손에 든 도장 하나와 같은 원·막대 무늬가 찍힌 카드 세 장, 다음 인쇄를 기다리는 빈 카드
정의는 도장의 무늬를 준비하는 일, 호출은 준비한 도장을 찍는 동작, 재사용은 같은 도장을 다른 종이에 다시 찍는 일에 비유할 수 있습니다.

도장을 만들어 두기만 하면 종이에 무늬가 생기지는 않습니다. 함수를 정의한 뒤 호출해야 본문이 실행되는 것과 같습니다. 이번 실습에서는 입력 없이 고정된 제작 규칙을 사용하므로, 새 캔버스에 같은 그림을 만드는 작업을 반복합니다.

복사와 함수의 구조
비교 항목본문 복사함수 정의와 호출
작업 규칙의 위치A·B·C의 세 본문함수 본문 한 곳
실행 요청각 본문을 직접 실행같은 이름으로 세 번 호출
공통 수정세 본문에 같은 수정한 본문 수정 후 다시 호출
결과 검증각 복사본의 규칙과 출력 확인공통 본문과 각 호출 결과 확인

반복과 재사용

for는 지정한 작업을 반복하고, 함수는 다시 사용할 작업에 이름을 붙입니다. 한 장 안에 도형 여섯 개를 그리는 for를 함수 안에 둘 수도 있고, 완성 이미지가 여러 장 필요하면 함수 호출을 for로 반복할 수도 있습니다. 도입에서는 두 역할을 구분하고, 뒤의 반복 호출에서 실제 캔버스·도형·출력 횟수를 확인합니다.

재사용의 기준

같아 보이는 몇 줄이 있다는 이유만으로 곧바로 같은 함수로 묶지는 않습니다. 전시 카드 세 장의 강조색은 같은 요청에 함께 바뀌므로 공통 규칙으로 관리할 이유가 있습니다. 반면 안내 제목과 이미지 배경은 우연히 같은 색을 쓰더라도 서로 다른 이유로 바뀔 수 있습니다. 제작 목적과 수정 이유를 먼저 확인합니다.

함수로 묶은 뒤 호출 세 줄이 남는 것은 중복 오류가 아닙니다. 세 번 실행해야 한다는 요청은 각각 필요합니다. 줄여야 할 중복은 같은 제작 규칙을 여러 본문에 복사하여 따로 고쳐야 하는 구조입니다. 정의의 수, 호출의 수, 결과물의 수를 같은 숫자로 만들려고 하지 않습니다.

이제 긴 이미지 코드를 바로 옮기기 전에, 두 문장을 출력하는 작은 작업에서 정의와 호출의 의미를 확인합니다. 한 작업의 시작과 끝을 읽을 수 있어야 그 안에 반복문과 조건문이 있는 이미지 코드도 같은 방식으로 묶을 수 있습니다.

2. 정의와 호출

핵심 질문

함수를 정의한 직후와 호출한 직후에 각각 어떤 출력이 나타날까요?

정의문의 구성

def show_message():는 show_message라는 이름으로 작업을 정의하는 문장입니다. def 뒤에 이름을 쓰고 괄호와 콜론을 붙입니다. 아래의 들여쓴 print가 이 함수의 본문입니다. 이번 함수는 입력을 받지 않으므로 괄호 안이 비어 있습니다.

정의와 호출은 같은 이름을 사용하지만 문장의 역할이 다릅니다. 정의에는 def와 콜론이 있고, 호출에는 이름 뒤의 괄호가 있습니다. 함수 정의는 실행 준비이고 함수 호출은 본문 실행입니다. 준비한 작업은 호출을 만날 때 수행됩니다.

정의만 있는 예제
정의만 있는 예제
def show_message():
    print("도형 한 장")

정의문과 호출문
부분역할읽는 방법
def정의 시작이 이름으로 작업을 준비한다
show_message함수 이름호출할 작업을 구분한다
()입력 자리지금은 입력을 받지 않는다
:본문 시작 표시다음 들여쓴 줄이 작업 내용이다
들여쓴 print함수 본문호출할 때 실행할 명령이다
show_message()함수 호출준비한 본문을 지금 실행한다
작업 지시와 호출개념 비유
그림으로 작업 순서를 정리한 지시서, 제작을 요청하는 호출 벨, 지시서에 따라 카드를 만드는 공방 장면
작업 지시서는 정의해 둔 작업, 벨 누르기는 함수 호출, 지시서에 따른 제작은 본문 실행에 비유합니다. 지시서를 준비하는 일과 실제로 제작하는 일을 구분합니다.

기존 함수와 직접 정의

지금까지 사용한 print와 display에도 이름 뒤의 괄호가 있었습니다. 이미 준비된 도구를 이름으로 호출해 사용한 것입니다. 이번에는 도구를 사용하는 위치에서 한 단계 나아가, 자신의 작업을 수행할 함수를 직접 정의합니다. print를 새로 만드는 것이 아니라 print 등의 명령을 원하는 순서로 묶어 새 작업을 만드는 것입니다.

예를 들어 announce_steps() 안에 print가 두 줄 있으면, 바깥에서는 작업을 한 번 호출하고 본문에서는 출력 도구를 두 번 호출합니다. 함수 본문에 다른 함수의 호출이 들어갈 수 있으므로 이미지 준비, 그리기, 표시도 하나의 작업으로 조합할 수 있습니다. 호출 한 번이 반드시 명령 한 줄이나 출력 한 개를 뜻하지는 않습니다.

이미 준비된 도구라도 사용 조건은 구분합니다. print는 별도 import 없이 사용할 수 있지만 이 자료의 display는 IPython.display에서 불러옵니다. 직접 작성한 announce_steps는 정의 셀을 먼저 실행해야 합니다. 함수 이름을 안다는 것과 현재 실행 환경에 그 도구가 준비되어 있다는 것은 별개의 조건입니다.

함수 이름

이름은 동작과 대상을 드러내는 편이 읽기 쉽습니다. show_message는 문장 표시, show_shape_row는 도형 행 표시입니다. function1처럼 번호만 붙인 이름은 호출만 보고 역할을 알기 어렵습니다. 소문자와 밑줄을 사용하면 단어의 경계도 드러납니다.

정의와 호출의 철자는 일치해야 합니다. show_message를 정의하고 show_messages를 호출하면 다른 이름을 찾게 됩니다. 공백이나 첫 글자의 숫자는 이름에 사용할 수 없고 def·for 같은 문법 단어도 이름으로 쓰지 않습니다. print나 len처럼 이미 쓰는 도구 이름을 새 함수 이름으로 덮어쓰는 일도 피합니다.

실습 1 · 함수 정의

두 문장을 하나의 작업으로 묶습니다. 정의의 이름과 마지막 호출의 이름을 같은 철자로 적으세요. 정의만 실행할 때와 호출까지 실행할 때의 출력 수를 구분합니다.

수정 전 코드 · 해설과 함께 완성
수정 전 코드 · 해설과 함께 완성
def ____():
    print("캔버스 준비")
    print("도형 제작")

____()

해설과 완성 코드
완성 코드
완성 코드
def announce_steps():
    print("캔버스 준비")
    print("도형 제작")

announce_steps()

정의만 준비하면 출력은 없습니다. announce_steps()를 호출하면 캔버스 준비와 도형 제작이 순서대로 출력됩니다. 이름은 다른 것으로 정해도 되지만 정의와 호출이 일치해야 합니다. 출력 문장이 그림을 대신 만드는 것은 아니며, 지금은 실행 구조를 확인합니다.

호출과 복귀

정의문을 만나면 함수가 준비되고 다음 바깥 문장으로 진행합니다. 호출문을 만나면 본문으로 들어가 명령을 수행하고, 본문 끝에 도달하면 방금 호출한 문장 다음으로 돌아옵니다. 정의가 위에 적혀 있다는 이유만으로 그 본문이 먼저 출력되는 것은 아닙니다.

다음 예제의 출력은 시작 → 정의 완료 → 본문 → 종료입니다. 정의 줄에서는 본문을 준비하고 바깥 코드로 진행하며, show_message()를 만났을 때 본문을 실행합니다. 아래 실행 경로 해설과 코드를 함께 읽으면 본문으로 들어갔다 돌아오는 위치가 드러납니다.

핵심 질문

정의에서는 출력이 없고, 호출에서 본문 출력이 추가되는 이유는 무엇일까요?

실행 순서 예제
실행 순서 예제
print("1. 시작")

def show_message():
    print("3. 본문")

print("2. 정의 완료")
show_message()
print("4. 종료")

실행 경로 해설
호출 전후의 실행
만난 문장수행한 일누적 출력
시작 print바깥 문장 출력1줄
def show_message():작업 준비1줄
정의 완료 print다음 바깥 문장 출력2줄
show_message()본문 실행 후 호출 다음으로 복귀3줄
종료 print바깥 문장 출력4줄

출력은 시작 → 정의 완료 → 본문 → 종료입니다. 호출을 지우면 본문만 빠집니다. 호출을 종료 print 뒤로 옮기면 본문도 종료 뒤에 나옵니다. 작업의 정의 위치와 실제 수행 위치를 구분할 수 있어야 여러 작업을 원하는 순서로 배치할 수 있습니다.

두 호출의 실행 추적

한 번의 호출을 읽을 수 있다면, 중간에 다른 문장이 있는 두 호출을 추적합니다. 위에서 아래로 본문을 모두 한 번씩 읽는 방법으로는 실제 순서를 얻을 수 없습니다. 정의를 지날 때는 본문을 건너뛰고, 호출을 만날 때마다 본문의 시작으로 들어간 뒤 돌아옵니다.

아래 코드의 정의는 한 개이고 호출은 두 개입니다. 각 호출에서 제작·확인 두 문장이 출력되어 전체 출력은 여덟 줄입니다. 코드를 실행하며 표의 누적 출력 수와 대조하면 정의의 위치와 실제 실행 위치를 구분할 수 있습니다.

두 호출의 실행 순서 시연
두 호출의 실행 순서 시연
print("접수")

def show_notice():
    print("제작")
    print("확인")

print("대기")
show_notice()
print("추가 요청")
show_notice()
print("종료")

실행 경로 해설
도달한 위치이번에 추가할 출력누적 출력 수
첫 print접수1
함수 정의출력 없음1
대기 print대기2
첫 호출제작 → 확인4
추가 요청 print추가 요청5
두 번째 호출제작 → 확인7
마지막 print종료8
두 호출의 해설

접수 → 대기 → 제작 → 확인 → 추가 요청 → 제작 → 확인 → 종료로 총 여덟 줄입니다. 본문은 한 곳에 있지만 두 호출에서 각각 두 줄을 출력합니다. 함수 정의 자체는 제작·확인 문장을 화면에 내보내지 않습니다.

두 번째 호출만 지우면 추가 요청과 종료 사이의 제작·확인 두 줄이 빠집니다. 정의를 지우고 호출을 남기면 같은 결과가 더 짧은 코드로 나오는 것이 아니라 준비된 이름을 찾지 못합니다. 줄 삭제의 결과를 구분하는 것이 정의와 호출을 이해했는지 확인하는 방법입니다.

노트북의 실행 상태

Colab은 셀을 골라 실행할 수 있습니다. 아래쪽에 정의 코드가 보이더라도 그 셀을 실행하지 않았다면 위쪽 호출에서 이름을 찾을 수 없습니다. 이전에 실행한 정의가 남아 있으면 호출만 실행해도 작동할 수 있으므로, 화면의 위치와 런타임의 준비 상태를 구분합니다.

이 자료에서는 공통 준비 → 정의 → 호출 순으로 셀을 배치합니다. 중간 셀만 실행해서 우연히 작동한 상태에 의존하지 않도록 마지막에는 새 세션에서 위에서 아래로 실행합니다. 지금은 정의 셀 다음 호출 셀이라는 기본 순서를 지키는 것부터 시작합니다.

3. 들여쓰기와 범위

핵심 질문

제목은 한 번, 도형 안내는 두 번 출력하려면 어떤 문장을 함수 안에 두어야 할까요?

함수 본문의 경계

함수의 본문은 들여쓰기로 구분합니다. def 다음 줄부터 네 칸 안쪽의 문장이 본문이며, 다시 바깥 수준으로 돌아온 줄은 함수 밖입니다. 끝을 표시하는 별도의 닫는 기호는 없습니다. 빈 줄만 추가해도 들여쓰기 수준이 같으면 함수 본문이 계속됩니다.

함수 안에는 호출할 때마다 다시 수행할 일을 두고, 밖에는 전체 흐름에서 수행할 일을 둡니다. 두 문장이 나란히 적혀 있다고 반드시 같은 작업인 것은 아닙니다. 자료 제목은 한 번 알리고 카드 제작은 여러 번 수행할 수 있으므로, 무엇을 다시 사용할지 먼저 결정합니다.

실습 2 · 함수의 범위

“수업 자료”는 한 번, “도형 한 장”은 두 번 출력하려고 합니다. 함수 본문은 유지하고 바깥의 제목과 호출 두 줄을 작성하세요.

수정 전 코드 · 해설과 함께 완성
수정 전 코드 · 해설과 함께 완성
def show_card():
    print("도형 한 장")

# 제목을 한 번 출력하고 show_card를 두 번 호출하세요.

해설과 완성 코드
완성 코드
완성 코드
def show_card():
    print("도형 한 장")

print("수업 자료")
show_card()
show_card()

바깥의 제목은 한 번 실행되고 본문은 두 호출에 의해 두 번 실행됩니다. 제목을 함수 안에 넣으면 제목도 두 번 나옵니다. 전체 자료의 안내와 매번 반복할 작업을 구분해서 묶습니다.

중첩된 블록

이미지 코드에는 이미 for와 if가 있습니다. 함수로 옮길 때는 원래 포함 관계에 들여쓰기 한 단계를 더합니다. 모든 줄을 네 칸으로 나란히 맞추는 것이 아닙니다. 함수 안의 준비와 for는 네 칸, 반복 안의 좌표와 if는 여덟 칸, 조건 안의 그리기 명령은 열두 칸이 됩니다.

이 관계를 읽을 때는 줄의 길이보다 왼쪽 시작 위치를 봅니다. 반복문보다 안쪽에 있는 명령은 매 반복에서 실행될 수 있고, 반복문과 같은 수준인 다음 명령은 반복이 끝난 뒤 실행됩니다. 함수 안인지 확인한 다음, 그 안의 어느 반복이나 조건에 속하는지도 확인합니다.

이미지 함수의 포함 관계
들여쓰기대표 문장소속
0칸def show_shape_row():함수 정의
4칸image = Image.new(...)함수 본문
4칸for i in range(6):함수 본문
8칸if size >= 60:반복문 안
12칸draw.rectangle(...)조건문 안
4칸display(image)함수 안·반복문 밖
0칸show_shape_row()함수 밖의 호출
핵심 질문

다음 코드에서 “준비”, “반복”, “가운데”, “완료”는 각각 몇 번 나올까요? 완료 문장을 여덟 칸으로 옮기면 어떤 차이가 생길까요?

블록 구조의 실행
블록 구조의 실행
def show_structure():
    print("준비")
    for i in range(3):
        print("반복", i)
        if i == 1:
            print("가운데")
    print("완료")

show_structure()

포함 관계 해설

준비 1번, 반복 3번, 가운데 1번, 완료 1번입니다. 가운데는 i가 1인 반복에서만 실행됩니다. 완료는 함수 안이지만 for 밖에 있으므로 모든 반복이 끝난 뒤 한 번 실행됩니다. 여덟 칸으로 옮기면 반복 안에 들어가 완료도 세 번 나옵니다.

이 구분은 다음 이미지 코드에서 준비를 캔버스 생성, 반복을 도형 그리기, 완료를 화면 표시로 바꾸어 읽을 수 있습니다. 출력 문구 대신 이미지 명령을 넣더라도 블록의 실행 관계는 같습니다.

pass와 미완성 본문

작성용 코드의 pass는 아무 작업도 하지 않는 문장입니다. 함수 본문을 완전히 비워 둘 수 없으므로 작업을 넣을 자리를 표시할 때 사용합니다. pass만 있는 함수를 호출하면 오류 없이 끝나더라도 그림은 나오지 않습니다. 오류가 없다는 사실과 작업이 완성되었다는 사실을 구분합니다.

이미지 함수 실습에서는 pass를 지우고 기준 이미지의 작업을 옮깁니다. 먼저 다음 절에서 호출마다 필요한 준비를 확인합니다. 코드 편집기에서 선택한 블록을 한 단계 들여쓰면 내부의 상대적인 간격을 보존하기 쉽습니다. 옮긴 뒤에는 맨 앞 준비 줄, for와 if의 관계, 마지막 display의 위치를 확인합니다.

4. 함수의 작업 준비

핵심 질문

이미지 한 장을 만드는 함수는 호출할 때 어떤 준비부터 시작해야 할까요?

한 장의 제작 과정

종이에 그림 한 장을 만들 때는 종이 크기와 사용할 색을 정하고, 새 종이와 도구를 준비한 뒤, 도형을 그려 완성본을 보여 줍니다. 함수도 이 순서를 하나의 작업으로 묶습니다. 호출은 그리기만 시작하는 것이 아니라 한 장에 필요한 준비부터 다시 실행하는 요청입니다.

작업 키트개념 비유
종이·잉크·롤러·도장이 정리된 작업 키트와 깨끗한 작업 매트 위에 새 종이를 놓는 손
작업을 시작할 때 필요한 재료를 준비하고 새 종이를 놓는 일에 비유합니다. 이번 이미지 함수도 본문에서 크기·색을 정하고 Image.new로 새 캔버스를 마련합니다. 이전 결과물은 새 작업 공간과 구분합니다.
제작 과정과 코드
제작 과정이미지 코드한 호출에서의 역할
크기·색 결정sizes, palette그릴 내용의 기준 준비
새 종이·도구 준비Image.new, ImageDraw.Draw이번 한 장을 그릴 작업 공간 생성
도형 제작for와 if·elif·else같은 캔버스에 여섯 도형 누적
완성본 표시display(image)도형을 모두 그린 뒤 한 장 표시

준비의 위치

새 캔버스를 만드는 Image.new를 본문에 두면 함수를 부를 때마다 빈 이미지에서 작업을 시작합니다. 이전에 그린 종이 위에 계속 그리려는 작업이 아니므로, 한 장의 제작에 필요한 준비를 그리기와 함께 묶습니다.

sizes와 palette도 이번 작업의 고정 규칙으로 본문에 둡니다. Image·ImageDraw·display를 불러오는 import는 여러 함수가 함께 쓰는 공통 도구 준비이므로 밖에 둡니다. 수업 제목이나 학번 안내도 그림 한 장마다 수행할 일이 아니므로 본문에서 제외합니다.

준비의 소속
항목이번 수업의 위치판단 근거
Image·ImageDraw·display 불러오기공통 준비여러 함수가 같은 도구 사용
카드의 너비·높이·색함수 안이 작업의 고정 제작 규칙
새 이미지·그리기 도구함수 안호출마다 새 작업 공간 필요
수업 제목·학번 안내함수 밖그림을 만들 때마다 반복할 일 아님
함수 호출함수 밖준비한 작업을 실행할 시점 결정

실습 3 · 작업 준비

이 원리를 너비 한 줄로 확인합니다. 아래 원본은 canvas_width = 320이 함수 밖에 있어 그 대입문을 먼저 실행해야 합니다. 완성 코드에서는 같은 줄을 본문 첫 위치로 옮깁니다. 그러면 호출할 때 너비 준비와 표시가 함께 실행됩니다. 두 코드 모두 출력값은 320이며, 달라진 것은 준비를 실행하는 위치입니다.

수정 전 · 바깥에서 너비 준비
수정 전 · 바깥에서 너비 준비
canvas_width = 320

def announce_canvas():
    print("캔버스 너비:", canvas_width)

announce_canvas()
announce_canvas()

준비 위치의 비교
항목수정 전수정 후
너비 준비함수 밖의 대입문함수 본문 첫 줄
정의 직후 출력없음없음
첫 호출의 출력캔버스 너비: 320캔버스 너비: 320
두 번째 호출바깥에서 준비한 값을 읽음본문에서 320을 다시 준비하고 읽음
준비 위치의 해설
완성 코드
완성 코드
def announce_canvas():
    canvas_width = 320
    print("캔버스 너비:", canvas_width)

announce_canvas()
announce_canvas()

canvas_width = 320을 네 칸 들여써 본문 첫 줄로 둡니다. print는 같은 수준에 두고 두 호출은 밖에 둡니다. 두 출력 모두 “캔버스 너비: 320”이어야 합니다. 출력값은 같지만 필요한 준비를 보관하는 위치가 달라졌습니다.

수정 뒤에는 이 코드 전체를 새 셀에서 실행합니다. 이전 대입문이 남아 있어서 우연히 성공한 것인지 구분하려면, 정의와 호출만으로 필요한 이름을 모두 준비하는지 읽어야 합니다. 오류가 없다는 사실에 더해 어떤 줄이 값을 준비했는지를 설명합니다.

이미지 함수의 준비

다음 절에서는 이 준비 과정을 여섯 도형의 전체 코드에 적용합니다. sizes부터 display까지를 함께 옮겨, 호출 한 번으로 새 이미지 한 장이 완성되도록 구성합니다. 실제 이미지를 확인한 뒤에는 함수 안에서 준비한 이름과 호출별 시작 상태를 짧은 예제로 살펴봅니다.

5. 이미지 함수 구성

핵심 질문

기준 코드의 어떤 부분을 함수로 옮겨야 한 번의 호출로 이미지 한 장이 완성될까요?

작업의 구성

이제 5주차의 여섯 도형을 실제 함수로 옮깁니다. 이번 함수의 약속은 “호출하면 새 이미지 한 장을 완성해 보여 준다”입니다. 이 약속에 필요한 준비, 도형 반복, 결과 표시를 함께 묶습니다. 도구를 불러오는 import는 공통 준비이므로 바깥에 둡니다.

반복문만 함수에 넣으면 캔버스와 그리기 도구를 밖에서 먼저 준비해야 합니다. 호출하는 사람이 그 준비를 빠뜨리면 작업을 끝낼 수 없습니다. 반대로 학번 입력이나 제출 안내까지 묶으면 그림을 다시 만들 때마다 관련 없는 작업도 수행됩니다. 이번에는 독립적으로 다시 사용할 이미지 한 장을 경계로 정합니다.

함수로 이동할 범위
구성대표 코드배치 이유
도구 준비from PIL import Image, ImageDraw함수 밖에서 도구 준비
데이터 준비sizes, palette본문에서 사용할 기준값 준비
작업 공간Image.new, ImageDraw.Draw한 장을 그릴 캔버스와 도구 생성
표현 규칙for, if·elif·else여섯 크기의 위치와 표현 결정
결과 표시display(image)반복 종료 뒤 완성 이미지 확인
실행 요청show_shape_row()함수 밖에서 작업 호출

변환 전후 코드

왼쪽은 기준 코드이고 오른쪽은 같은 작업을 함수로 옮긴 코드입니다. 크기 목록, 색, 좌표 계산, 조건의 숫자는 같습니다. 오른쪽에서 새로 생긴 것은 def 한 줄, 본문 전체의 한 단계 들여쓰기, 마지막 호출문입니다. 긴 코드를 외우기보다 이 세 변화와 나머지 규칙의 보존을 확인합니다.

코드를 비교할 때 먼저 import가 두 쪽 모두 바깥에 있는지 봅니다. 다음으로 sizes부터 display까지의 덩어리가 오른쪽에서 한 단계 들어갔는지 확인합니다. 마지막 호출은 다시 맨 왼쪽에서 시작합니다. 호출이 함수 안에 들어가면 지금 의도한 바깥 실행 요청이 아니므로 위치를 주의합니다.

변환 전 · 기준 코드
변환 전 · 기준 코드
from PIL import Image, ImageDraw
from IPython.display import display

sizes = [30, 46, 64, 46, 60, 38]
palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
image = Image.new("RGB", (660, 220), (245, 240, 228))
draw = ImageDraw.Draw(image)

for i in range(6):
    x = 50 + i * 100
    y = 80
    size = sizes[i]
    if size >= 60:
        draw.rectangle((x, y, x + size, y + size), fill=palette[2])
    elif size >= 40:
        draw.ellipse((x, y, x + size, y + size), fill=palette[1])
    else:
        draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

display(image)

변환 후 · 함수와 호출
변환 후 · 함수와 호출
from PIL import Image, ImageDraw
from IPython.display import display

def show_shape_row():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

show_shape_row()

변환 절차

첫째, 작동하는 기준 코드를 보존합니다. 원래 결과를 모르는 상태에서 구조와 색을 동시에 고치면 차이의 원인을 구분하기 어렵습니다. 이번에는 산호색 사각형 두 개가 있는 원본을 비교 기준으로 사용합니다.

둘째, 새 셀에서 def show_shape_row():를 작성하고 pass 자리에 sizes부터 display까지 옮깁니다. 원래 for 안의 명령도 함께 선택하여 전체를 네 칸 더 들여씁니다. 줄마다 새로 들여쓰기 수준을 추측하지 않고 기존 포함 관계를 보존합니다.

셋째, 정의 셀만 실행합니다. 오류 메시지가 없다면 작업이 준비된 상태입니다. 이어서 바로 다음 셀에 show_shape_row()를 작성합니다. 정의와 첫 호출 사이에 다른 이미지 코드를 실행하지 않아야 지금 작성한 함수의 결과를 분명하게 확인할 수 있습니다.

실습 4 · 이미지 함수 구성

공통 원리
이미지 한 장의 준비·반복·표시를 입력 없는 함수로 묶습니다.
유지 요소
캔버스 660 × 220 · 크기 목록 · 여섯 번의 반복 · 분류 기준

완성 코드와 함께 A의 pass를 지우고 기준 코드의 준비부터 display까지 네 칸 들여써 옮깁니다. 정의 셀 다음 호출 셀을 실행합니다. B와 C에서는 각각 색상 줄과 y 계산 줄을 대조합니다. 세 이름은 고정 규칙의 차이를 비교하기 위한 것이며, 하나의 함수에 설정을 전달하는 방법은 2교시에서 다룹니다.

A 기준 배열

1교시의 원래 팔레트와 y=80을 사용합니다.

결과 해설 산호색 사각형과 원이 한 줄에 배치됩니다.

1-4A · 실습 코드
A-1 · 본문 작성과 정의
A-1 · 본문 작성과 정의
from PIL import Image, ImageDraw
from IPython.display import display

def show_shape_row():
    # pass를 지우고 기준 작업을 옮겨 네 칸 들여쓰세요.
    pass

완성 코드
해설용 완성 코드
해설용 완성 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_shape_row():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

A-2 · 호출
A-2 · 호출
# TODO: 위에서 정의한 show_shape_row를 한 번 호출하세요.

완성 코드
해설용 완성 코드
해설용 완성 코드
show_shape_row()

B 색상 변형

팔레트의 RGB 값만 보라·파랑·황토 계열로 바꿉니다.

결과 해설 A와 같은 위치·분류이며 색만 달라집니다.

1-4B · 실습 코드
B-1 · 색상 함수 정의
B-1 · 색상 함수 정의
from PIL import Image, ImageDraw
from IPython.display import display

def show_row_color():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(61, 58, 105), (119, 136, 208), (211, 155, 42)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

B-2 · 호출
B-2 · 호출
show_row_color()

C 계단 배치

y=80을 y=45+i*20으로 바꿉니다.

결과 해설 A의 색과 분류를 유지하며 아래로 이어지는 배열이 됩니다.

1-4C · 실습 코드
C-1 · 배치 함수 정의
C-1 · 배치 함수 정의
from PIL import Image, ImageDraw
from IPython.display import display

def show_row_stairs():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 45 + i * 20
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

C-2 · 호출
C-2 · 호출
show_row_stairs()

A·B·C 결과 비교

비교 기록

공통 원리
___
바꾼 줄
___
A와 B의 차이
___
A와 C의 차이
___
정의와 호출
실행 결과 비교같은 코드와 입력으로 확인
A · 정의 직후 출력 0장, 호출 직후 출력 1장
A · 정의 직후 출력 0장, 호출 직후 출력 1장

결과의 동등성

함수로 옮기는 작업은 제작 규칙의 구조를 바꾸는 것입니다. 실습 4의 A는 시각 규칙을 유지했으므로 원본과 같은 크기·위치·색이 나와야 합니다. B는 색상, C는 세로 배치만 다르게 만들었습니다. 먼저 A와 원본의 여섯 도형을 대조하고, B·C에서는 지정한 요소만 달라졌는지 확인합니다.

i가 2인 세 번째 도형은 크기 64, x가 250입니다. 큰 경계 60 이상이므로 사각형이며 오른쪽 아래 좌표는 (314, 144)입니다. i가 4인 다섯 번째 도형은 크기 60, x가 450이며 (510, 140)까지 사각형을 그립니다. 두 예의 계산이 함수 변환 뒤에도 유지되는지 확인합니다.

A의 대조 기준
항목기준다를 때 확인할 부분
이미지 크기660 × 220Image.new의 크기
도형 수6개range와 캔버스 생성 위치
왼쪽 좌표50·150·250·350·450·550x = 50 + i * 100
분류윤곽·채움·사각형·채움·사각형·윤곽sizes와 if·elif 순서
사각형 색산호색palette[2]
표시 횟수1회display의 위치와 호출 수

고정 규칙과 시각 변형

실습 4의 A·B·C는 같은 함수 구조에 서로 다른 고정 규칙을 넣은 비교 예제입니다. A는 기준 배열, B는 색상 변경, C는 세로 배치 변경입니다. 각 정의를 다른 이름으로 남겨 어느 본문이 어떤 결과를 만드는지 나란히 확인합니다.

각 버전 안에서는 같은 함수를 다시 호출하여 그 규칙을 재사용합니다. 다만 세 버전 사이의 공통 본문까지 하나로 합친 상태는 아닙니다. 같은 그리기 작업에서 설정만 다른 결과를 계속 만들려면, 함수를 복사할 때마다 공통 부분도 함께 관리해야 하는 문제가 남습니다.

2교시에서는 이 문제를 큰 경계 60과 63의 비교로 이어갑니다. 본문 한 곳을 유지하면서 호출에 다른 경계값을 전달하도록 매개변수를 추가합니다. 1교시는 한 작업의 정의와 호출을 구분하고, 2교시는 그 작업의 설정을 호출마다 달리하는 단계입니다.

두 교시의 연결
구분1교시의 고정 규칙 비교2교시의 설정 전달
정의버전별 본문과 이름을 대조같은 본문에 입력 자리 추가
변경 위치색·배치 등 본문의 고정값호출에서 전달하는 값
재사용각 버전의 작업을 다시 호출한 함수에 다른 설정으로 호출
학습 초점작업의 정의와 실행 구분공통 작업과 개별 설정 분리

이름과 사용 범위

앞의 이미지 함수는 sizes·palette·image를 본문에서 준비했습니다. 함수 안에서 값을 대입한 이름은 그 함수 안에서 사용하는 지역 이름입니다. 바깥에 철자가 같은 이름이 있어도 안쪽의 대입이 바깥의 값을 자동으로 바꾸지는 않습니다. 아래 짧은 예제에서는 안쪽 width의 320과 바깥 width의 660을 구분합니다.

먼저 바깥 width에 660이 들어갑니다. 정의를 지날 때는 본문에 적힌 width = 320을 실행하지 않습니다. describe_badge()를 호출한 순간 본문에서 320을 준비하고 첫 print가 그 값을 읽습니다. 호출이 끝난 뒤 바깥 print는 여전히 바깥의 660을 읽습니다.

보충 시연 · 안쪽 320, 바깥 660
보충 시연 · 안쪽 320, 바깥 660
width = 660

def describe_badge():
    width = 320
    print("함수 안:", width)

describe_badge()
print("함수 밖:", width)

이름 추적
실행 위치읽거나 준비하는 이름값
바깥 첫 대입바깥 width660
함수 정의본문의 대입은 아직 미실행출력 없음
호출 후 본문 대입함수 안 width320
본문의 print함수 안 width320
호출 다음 print바깥 width660
값의 추적

출력은 “함수 안: 320”, “함수 밖: 660”입니다. 안쪽 대입을 지웠을 때의 동작까지 같은 원리로 단정하면 안 됩니다. Python은 본문에 없는 이름을 바깥에서 찾을 수도 있습니다. 이번에는 그런 의존을 추가하지 않고 필요한 고정값을 본문에서 명시적으로 준비합니다.

함수 안의 값을 밖에서 쓰려고 같은 이름을 계속 만드는 방식은 사용하지 않습니다. 지금은 작업에 필요한 준비를 함수 안에 모으는 데 집중합니다. 완성 결과를 밖에서 받는 방법은 2교시의 반환값에서 다룹니다.

호출별 시작 상태

앞의 이미지 함수는 본문에서 Image.new를 실행하므로 각 호출이 새 캔버스로 시작합니다. 같은 원리를 숫자로 보면, 본문 첫 줄의 count = 0도 호출할 때마다 다시 실행됩니다. 아래 두 호출은 각각 1·2를 출력합니다.

첫 호출의 반복이 끝났을 때 count가 2라는 사실만 보면 다음 값이 3일 것처럼 느껴질 수 있습니다. 그러나 두 번째 호출은 본문의 첫 문장으로 다시 들어갑니다. 준비 → 반복 → 완료라는 작업의 시작 지점을 따라 읽어야 합니다.

두 호출의 번호 추적
두 호출의 번호 추적
def show_count():
    count = 0
    for i in range(2):
        count = count + 1
        print("도형", count)

show_count()
show_count()

두 번째 호출의 시작

출력은 도형 1 → 도형 2 → 도형 1 → 도형 2입니다. 각 호출에서 count를 0으로 준비하고 두 번 증가시킵니다. 반복 변수 i와 화면에 출력한 count도 같은 이름이 아니므로 실제 출력문이 어느 값을 사용하는지 확인합니다.

같은 본문을 다시 실행해도 이전 실행의 마지막 번호에서 이어지는 것은 아닙니다. 다음 절에서는 이 실행 단위를 실제 이미지의 캔버스 수·도형 수·표시 횟수로 확인합니다.

정상 결과를 확인한 뒤 노트북을 저장합니다. 다음 절에서는 이 함수를 다시 정의하지 않고 호출 수만 바꿉니다. 작업을 정의하는 쪽과 사용하는 쪽을 구분해 두면, 어떤 수정이 새 결과를 만들었는지 추적할 수 있습니다.

6. 반복 호출

핵심 질문

호출 수, 캔버스 수, 도형 수, 표시 횟수는 어떻게 연결될까요?

함수와 반복문

앞서 만든 show_shape_row를 두 번 호출하면 한 장 만들기가 두 번 수행됩니다. 각 호출에서 Image.new는 한 번, 도형 반복은 여섯 번, display는 한 번 실행됩니다. 따라서 캔버스는 두 개, 전체 도형 처리는 열두 번, 표시는 두 번입니다.

본문의 for는 한 장 안의 도형 수를 정하고, 바깥 호출은 한 장 만들기를 시작합니다. 아래에서는 이 두 실행 단위를 실제 결과와 대조합니다. 정의를 한 번 작성했다는 사실과 본문을 두 번 실행한다는 사실을 구분합니다.

핵심 질문

기존 show_shape_row를 두 번 호출합니다. Image.new, 도형 그리기, display는 각각 몇 번 실행될까요? 같은 이미지에 열두 도형을 그리는 것인지도 설명하세요.

기존 정의 다음 셀 · 두 번 호출
기존 정의 다음 셀 · 두 번 호출
show_shape_row()
show_shape_row()

반복 호출 해설
호출 수와 실행 횟수
호출 수새 캔버스전체 도형 처리display
11개6회1회
22개12회2회
33개18회3회

Image.new가 함수 안·for 밖에 있어 각 호출이 새 캔버스 하나를 만듭니다. 두 호출은 같은 캔버스에 이어 그리는 일이 아닙니다. 사용하는 크기 목록과 절차가 같아 같은 모양의 두 이미지가 나옵니다.

생성·반복·표시의 위치

이미지를 만드는 과정에는 서로 다른 세 작업이 있습니다. Image.new는 빈 캔버스를 준비하고, draw의 명령은 그 캔버스에 내용을 누적하고, display는 그 순간의 상태를 화면에 보여 줍니다. 셋을 어디에 두는지에 따라 캔버스의 내용과 화면에 나온 이미지 수가 달라집니다.

아래 A·B·C는 크기·색·좌표·분류를 유지하고 캔버스 생성 또는 표시 위치만 바꾼 비교입니다. A는 완성본 한 장이 필요한 작업, B는 매 반복마다 작업 공간을 새로 만드는 구조, C는 제작 과정을 단계별로 보여 주는 구조입니다. 목적이 완성본인지 중간 과정인지에 따라 적절한 위치가 달라질 수 있습니다.

실습 5 · 생성·표시 위치

공통 원리
캔버스 생성·도형 그리기·화면 표시의 위치로 출력 구성을 조절합니다.
유지 요소
크기 목록 · 색 · 좌표 · 도형 분류

아래 결과표와 코드에서 Image.new, ImageDraw.Draw, display의 위치를 함께 확인합니다. A는 완성본 한 장, B는 마지막 도형 한 개, C는 도형이 누적되는 여섯 출력을 만듭니다. C의 여섯 출력은 왼쪽 위부터 행 순서로 비교합니다.

출력 결과
버전캔버스 수출력 장수마지막 도형 수
A1개1장6개
B6개1장1개
C1개6장6개

A 완성본

캔버스 생성과 display를 모두 for 밖에 둡니다.

결과 해설 여섯 도형을 누적한 이미지 한 장입니다.

1-5A · 실습 코드
실습 코드
실습 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_layout_a():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

show_layout_a()

B 마지막 도형

A에서 캔버스 생성만 for 안으로 옮깁니다.

결과 해설 마지막 캔버스에 여섯 번째 윤곽 원 하나만 남습니다.

1-5B · 실습 코드
실습 코드
실습 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_layout_b():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    for i in range(6):
        image = Image.new("RGB", (660, 220), (245, 240, 228))
        draw = ImageDraw.Draw(image)
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

show_layout_b()

C 제작 과정

A에서 display만 for 안으로 옮깁니다.

결과 해설 한 캔버스의 도형이 1개부터 6개까지 누적되는 여섯 출력입니다.

1-5C · 실습 코드
실습 코드
실습 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_layout_c():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

        display(image)

show_layout_c()

A·B·C 결과 비교

비교 기록

공통 원리
___
바꾼 줄
___
A와 B의 차이
___
A와 C의 차이
___

실행 구조의 추론

B의 원인은 반복 횟수가 부족한 것이 아닙니다. 그리기 명령은 여섯 번 실행되지만 그 대상이 매번 새 캔버스로 바뀝니다. range를 늘리면 더 많이 그리는 대신 더 많은 캔버스를 만들고 여전히 마지막 것만 표시합니다. 원하는 결과가 한 장의 누적 그림이라면 반복 횟수가 아니라 생성 위치를 고쳐야 합니다.

C에서 출력이 많다고 display를 모두 지우면 이미지는 만들어져도 화면에서 볼 수 없습니다. 완성본 한 장이 목적이라면 display 한 줄을 for 밖·함수 안으로 옮깁니다. 제작 과정을 보여 주는 것이 목적이라면 C의 위치도 사용할 수 있습니다. 증상에 반응해 줄을 지우기 전에 요구한 결과와 실행 단위를 연결합니다.

호출의 반복

동일한 함수를 세 줄로 호출하는 대신 for문 안에서 호출할 수도 있습니다. 바깥 for는 이미지의 장수를 정하고 함수 안의 for는 한 장의 도형 수를 정합니다. 두 반복은 대상이 다르므로 중복된 문법이 아닙니다.

호출 두 번을 range(2)의 반복으로 바꾸면 이번 예제의 실행 횟수는 같습니다. 반면 함수 본문의 range(6)을 range(2)로 바꾸면 한 장에 그리는 도형 수가 달라집니다. 장수를 바꾸라는 요청과 요소 수를 바꾸라는 요청을 코드의 다른 위치에 연결할 수 있어야 합니다.

기존 함수의 반복 호출 · 두 줄 호출과 비교
기존 함수의 반복 호출 · 두 줄 호출과 비교
for page_number in range(2):
    show_shape_row()

핵심 질문

바깥 반복 3회와 안쪽 반복 6회가 이미지 세 장·도형 처리 18회로 연결되는 이유는 무엇일까요?

호출 반복의 해설

바깥 호출은 3회, 각 호출 안의 도형 반복은 6회이므로 전체 도형 처리는 18회입니다. display는 함수 안·안쪽 for 밖에 두어 각 호출에서 한 번 실행합니다. 캔버스 생성도 같은 수준에 두면 세 장을 독립적으로 완성합니다.

7. 공통 수정

핵심 질문

함수의 색 한 곳을 수정하면 다음 세 호출에 어떤 변화가 적용될까요?

공통 수정의 적용

도입의 요청으로 돌아갑니다. 이번에는 세 복사본 대신 함수 본문 한 곳에서 산호색을 황토색으로 바꿉니다. 팔레트의 세 번째 값만 (201, 111, 93)에서 (211, 155, 42)로 수정하고, 크기 목록·좌표·조건·호출 이름은 유지합니다. 수정한 정의를 실행한 뒤 세 번 호출합니다.

실습 노트북의 수정 셀에는 기준 함수가 다시 제공되어 있습니다. 앞에서 완성한 정의와 출력을 지우지 않고, 이 셀에 변경 버전을 남깁니다. 같은 이름의 새 정의를 실행하면 이후 호출은 새 본문을 사용합니다. 이전 출력은 수정 전 기록으로 보존할 수 있습니다.

실습 6 · 공통 수정

공통 원리
함수 본문 한 곳의 시각 규칙을 수정하고 같은 함수를 세 번 호출합니다.
유지 요소
크기 목록 · 위치 · 분류 · 호출 세 번

A는 수정 전 기준입니다. B는 TODO에 따라 본문의 강조색 한 곳을 고칩니다. C는 A에서 윤곽선 두께만 바꾼 코드입니다. 각 버전의 정의를 실행하고 세 번 호출하여 같은 수정이 세 출력에 모두 적용되는지 확인합니다. 아래 비교 이미지는 각 버전의 첫 번째 출력입니다.

A 원래 색상

산호색과 윤곽선 두께 2를 사용합니다.

결과 해설 세 장 모두 원래 색과 얇은 윤곽선을 사용합니다.

1-6A · 실습 코드
실습 코드
실습 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_shape_row():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

show_shape_row()

show_shape_row()
show_shape_row()

B 강조색 변형

A의 산호색 한 곳을 황토색으로 바꿉니다.

결과 해설 세 장의 사각형 두 개가 모두 황토색으로 바뀝니다.

1-6B · 실습 코드
실습 코드
실습 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_shape_row():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

# TODO: 위 함수의 산호색을 황토색으로 한 번 바꾸세요.
show_shape_row()
show_shape_row()
show_shape_row()

완성 코드
해설용 완성 코드
해설용 완성 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_shape_row():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (211, 155, 42)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

# TODO: 위 함수의 산호색을 황토색으로 한 번 바꾸세요.
show_shape_row()
show_shape_row()
show_shape_row()

C 윤곽선 변형

A의 팔레트를 유지하고 else의 width만 6으로 바꿉니다.

결과 해설 첫 번째·여섯 번째 원의 선만 두꺼워집니다.

1-6C · 실습 코드
실습 코드
실습 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_row_outline():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=6)

    display(image)

show_row_outline()

show_row_outline()
show_row_outline()

A·B·C 결과 비교

비교 기록

공통 원리
___
바꾼 줄
___
A와 B의 차이
___
A와 C의 차이
___

편집·정의 실행·호출

코드를 편집하는 것, 새 정의를 실행하는 것, 그 함수를 호출하는 것은 다른 단계입니다. 편집만 하면 화면의 글자는 바뀌어도 런타임은 이전 정의를 사용할 수 있습니다. 정의만 다시 실행하면 새 작업은 준비되지만 이미 출력된 이미지가 다시 만들어지지는 않습니다. 새 호출에서 변경된 규칙으로 이미지를 생성합니다.

예를 들어 첫 출력이 산호색인 상태에서 팔레트를 수정하고 호출 셀만 실행했다면 여전히 산호색이 나올 수 있습니다. 이때 RGB 숫자를 여러 번 바꾸거나 호출문을 복사하기보다 수정한 정의 셀을 실행했는지 확인합니다. Colab에서 정의 셀을 수정했으면 그 셀을 실행한 뒤 호출 셀을 실행합니다.

수정 단계와 상태
수행한 일현재 사용할 정의이미 표시된 결과
코드 글자만 편집이전 정의일 수 있음수정 전 출력 유지
편집한 정의 실행수정한 새 정의수정 전 출력 유지
새 정의로 호출수정한 새 정의새 결과 추가
새 결과 저장정의와 별도의 파일 작업지정한 결과를 파일로 보관
핵심 질문

아래 두 호출의 출력 색을 적으세요. 두 번째 정의가 실행되면 첫 번째 출력도 바뀔까요?

재정의와 호출의 실행
재정의와 호출의 실행
def announce_color():
    print("산호색")

announce_color()

def announce_color():
    print("황토색")

announce_color()

재정의 해설

첫 호출은 산호색, 두 번째 호출은 황토색입니다. 두 번째 정의는 이후 호출에서 사용할 작업을 바꾸므로 첫 번째 출력은 그대로 남습니다. 그림에서도 수정 전·후의 출력이 함께 보일 수 있으며, 각각 어떤 호출에서 만들어졌는지를 기준으로 비교합니다.

변경과 유지의 검증

실습 6의 B에서 만든 세 출력은 모두 세 번째·다섯 번째 도형인 사각형의 채움색이 바뀌어야 합니다. 두 사각형이 같은 팔레트 항목을 사용하므로 모두 황토색이어야 합니다. 첫 번째와 여섯 번째 도형의 윤곽색, 나머지 원의 채움색, 모든 크기와 위치는 A와 같아야 합니다.

“더 밝아졌다”와 같은 인상만 적으면 의도한 수정인지 확인하기 어렵습니다. 어느 도형이 어떤 RGB 값으로 바뀌었는지 기록하고, 바꾸지 않은 요소도 한 가지 이상 지목합니다. 공통 수정을 검증할 때는 변화가 생겼다는 사실과 변화의 범위가 맞다는 사실을 함께 확인합니다.

공통 수정 결과
실행 결과 비교같은 코드와 입력으로 확인
함수 본문 한 곳 수정 후 세 호출 · 모두 황토색
함수 본문 한 곳 수정 후 세 호출 · 모두 황토색

세 번째·다섯 번째 도형은 바뀌고 나머지 요소는 유지됩니다. 복사 방식에서는 공통 규칙 세 벌을 확인했지만 함수 방식에서는 본문 한 곳을 수정합니다. 검증할 이미지가 세 장이라는 사실은 같아도 규칙을 관리하는 위치 수는 줄었습니다.

수정 항목의 선택

다음 요청이 “작은 원의 윤곽선을 더 두껍게”라면 palette가 아니라 else 안의 width를 바꿔야 합니다. “모든 도형을 오른쪽으로 이동”이라면 x 계산의 시작값을 바꿉니다. 제작 문장을 코드의 역할에 연결하면 색 실습에서 배운 공통 수정 원리를 다른 요청에도 적용할 수 있습니다.

가령 시작 x를 50에서 70으로 바꾸면 모든 도형이 오른쪽으로 20만큼 이동하고, i에 곱하는 간격 100은 유지됩니다. 반면 간격을 바꾸면 첫 도형의 위치는 그대로 두고 뒤쪽 도형의 간격이 달라집니다. 서로 다른 수정 요청을 같은 “위치 변경”으로 뭉뚱그리지 않고, 어떤 계산을 바꿀지 판단합니다.

제작 요청과 수정 위치
요청수정 위치결과 확인
사각형의 강조색 변경palette[2]세 번째·다섯 번째 도형의 색
작은 원의 선 두께 변경else의 width첫 번째·여섯 번째 도형의 선
전체 위치의 이동x의 시작값모든 도형의 같은 이동량
가로 간격의 변경i에 곱하는 간격첫 도형과 뒤 도형의 상대 위치

실습 6의 B는 강조색, C는 윤곽선 두께의 공통 수정입니다. 다음 절부터는 그리기 본문을 유지하면서 호출하는 쪽을 바꿉니다. 먼저 세 작업 중 하나를 선택하고, 이어서 두 작업을 순서대로 조합합니다. 선택과 조합을 확인한 뒤 오류 진단에서 실행이 어긋난 원인을 정리합니다.

8. 조건에 따른 호출

핵심 질문

세 이미지 함수 중 지금 필요한 작업 하나만 실행하려면 호출을 어디에 두어야 할까요?

작업의 선택

지금까지는 어떤 함수가 실행될지 사람이 호출문을 골라 정했습니다. 안내 화면에서 막대 카드, 채운 원 카드, 윤곽 원 카드 중 하나를 보여 주려면 그 선택을 코드로 남길 수 있습니다. 5주차의 if·elif·else가 이번에는 그리기 명령 하나가 아니라 함수 호출 하나를 선택합니다.

세 함수를 정의했다는 사실만으로 세 그림이 모두 나오지는 않습니다. 정의는 사용할 수 있는 작업의 목록을 준비합니다. 실제 출력 장수는 이후 어떤 호출을 실행했는지에 따라 정해집니다. 조건문을 읽을 때는 선택한 분기와 그 분기 안의 호출을 연결합니다.

도구의 선택개념 비유
막대·채운 원·윤곽 원 도장 중 채운 원 도장만 집어 든 손과 원 세 개가 찍힌 카드 한 장
세 도장은 준비된 세 함수, 도장 하나를 고르는 일은 조건에 따른 호출 선택에 해당합니다. 채운 원 도장을 사용하면 카드 한 장이 나오고, 선택하지 않은 두 도장의 작업은 실행되지 않습니다.

카드의 제작 규칙

세 함수는 모두 320 × 160의 종이색 캔버스를 만들고 요소 세 개를 그린 뒤 한 번 표시합니다. show_bars는 산호색 막대, show_dots는 세이지색 채운 원, show_rings는 짙은 윤곽 원입니다. 이름만 보고도 선택 결과를 말할 수 있도록 동작과 대상을 연결했습니다.

각 함수는 자신의 card와 pen을 준비합니다. 이름의 철자가 같아도 한 함수가 다른 함수의 그림을 이어 그리는 구조가 아닙니다. 두 함수를 차례로 부르면 독립적인 카드 두 장이 나옵니다. 먼저 공통 정의 셀만 실행하여 그림이 아직 표시되지 않는 것을 확인합니다.

함수별 작업 약속
함수한 번 호출한 결과공통 준비
show_bars()산호색 막대 세 개 · 한 장새 캔버스·그리기 도구
show_dots()세이지색 원 세 개 · 한 장새 캔버스·그리기 도구
show_rings()짙은 윤곽 원 세 개 · 한 장새 캔버스·그리기 도구

조건과 호출의 연결

style에는 이번에 선택한 작업의 이름을 문자열로 넣습니다. style == "bars"가 참이면 첫 호출만 실행하고 뒤의 elif·else는 건너뜁니다. 첫 조건이 거짓이면 "dots"인지 검사하며, 이 조건도 거짓일 때 else의 윤곽 원 작업으로 이동합니다.

style = "dots"처럼 값을 정하는 대입과 style == "dots"처럼 같은지 검사하는 비교를 구분합니다. show_dots라는 함수 이름 자체와 선택용 문자열 "dots"도 같은 것이 아닙니다. 문자열을 적었다고 함수가 실행되는 것은 아니며, 조건 안의 show_dots()가 실행 요청입니다.

실습 7 · 조건별 호출

공통 원리
세 함수의 제작 규칙을 유지하고 조건문으로 실행할 작업 하나를 선택합니다.
유지 요소
함수 정의 세 개 · 캔버스 320 × 160 · 각 카드의 요소 세 개

공통 정의를 먼저 실행합니다. A·B·C의 조건문에는 세 분기가 모두 show_bars를 호출하도록 되어 있습니다. 선택값 bars·dots·rings에 맞는 함수를 연결하고 각 버전에서 이미지 한 장만 나오는지 확인합니다. 함수의 그리기 본문은 고치지 않습니다.

공통 · 카드 함수 정의
공통 · 카드 함수 정의
공통 · 카드 함수 정의
from PIL import Image, ImageDraw
from IPython.display import display

def show_bars():
    card = Image.new("RGB", (320, 160), (245, 240, 228))
    pen = ImageDraw.Draw(card)
    for i in range(3):
        x = 35 + i * 90
        pen.rectangle((x, 45, x + 30, 115), fill=(201, 111, 93))
    display(card)

def show_dots():
    card = Image.new("RGB", (320, 160), (245, 240, 228))
    pen = ImageDraw.Draw(card)
    for i in range(3):
        x = 35 + i * 90
        pen.ellipse((x, 50, x + 60, 110), fill=(111, 143, 126))
    display(card)

def show_rings():
    card = Image.new("RGB", (320, 160), (245, 240, 228))
    pen = ImageDraw.Draw(card)
    for i in range(3):
        x = 35 + i * 90
        pen.ellipse((x, 50, x + 60, 110), outline=(37, 62, 78), width=4)
    display(card)

A 막대 카드

style 값: bars

결과 해설 첫 분기의 show_bars가 실행되어 산호색 막대 카드 한 장이 나옵니다.

1-7A · 실습 코드
A · 조건과 호출
A · 조건과 호출
style = "bars"

if style == "bars":
    show_bars()
elif style == "dots":
    show_bars()  # TODO: 채운 원 함수 호출
else:
    show_bars()  # TODO: 윤곽 원 함수 호출

완성 코드
해설용 완성 코드
해설용 완성 코드
style = "bars"

if style == "bars":
    show_bars()
elif style == "dots":
    show_dots()
else:
    show_rings()

B 채운 원 카드

style 값: dots

결과 해설 두 번째 분기의 show_dots가 실행되어 세이지색 원 카드 한 장이 나옵니다.

1-7B · 실습 코드
B · 조건과 호출
B · 조건과 호출
style = "dots"

if style == "bars":
    show_bars()
elif style == "dots":
    show_bars()  # TODO: 채운 원 함수 호출
else:
    show_bars()  # TODO: 윤곽 원 함수 호출

완성 코드
해설용 완성 코드
해설용 완성 코드
style = "dots"

if style == "bars":
    show_bars()
elif style == "dots":
    show_dots()
else:
    show_rings()

C 윤곽 원 카드

style 값: rings

결과 해설 else의 show_rings가 실행되어 짙은 윤곽 원 카드 한 장이 나옵니다.

1-7C · 실습 코드
C · 조건과 호출
C · 조건과 호출
style = "rings"

if style == "bars":
    show_bars()
elif style == "dots":
    show_bars()  # TODO: 채운 원 함수 호출
else:
    show_bars()  # TODO: 윤곽 원 함수 호출

완성 코드
해설용 완성 코드
해설용 완성 코드
style = "rings"

if style == "bars":
    show_bars()
elif style == "dots":
    show_dots()
else:
    show_rings()

A·B·C 결과 비교

비교 기록

선택값
___
실행한 함수
___
실행하지 않은 함수
___
출력 장수
___

선택 경로의 검증

A에서 막대가 나왔다는 사실만으로 전체 조건문이 완성되었다고 판단하지 않습니다. A는 첫 분기만 시험합니다. B와 C도 실행해야 나머지 두 분기가 어떤 함수를 부르는지 확인할 수 있습니다. 이번 작성용 코드에는 세 분기가 모두 막대를 부르도록 되어 있으므로 A만 보면 오류가 드러나지 않습니다.

조건을 고칠지 호출을 고칠지도 구분합니다. 선택값이 dots인데 elif로 정상 진입했다면 그 분기 안의 호출이 show_dots인지 확인합니다. 조건이 맞는 상황에서 비교 문자열을 바꿔 결과를 억지로 맞추면 선택 규칙 자체가 달라집니다.

분기별 실행 추적
style첫 조건두 번째 조건실행할 함수출력 장수
bars참검사하지 않음show_bars1
dots거짓참show_dots1
rings거짓거짓show_rings1
unknown거짓거짓show_rings1

현재 else는 rings만을 뜻하지 않습니다. bars도 dots도 아닌 모든 값을 받아 윤곽 원을 만듭니다. 따라서 철자가 틀린 dot도 윤곽 원을 만들 수 있습니다. 이번 실습은 세 선택값을 정확히 쓰는 조건으로 진행하며, “나머지 모든 경우”라는 else의 범위를 함께 기록합니다.

호출의 포함 범위

조건문 다음에 show_rings()를 맨 왼쪽으로 추가하면 그 호출은 조건에 속하지 않습니다. 그때는 bars 선택에서도 막대 카드 뒤에 윤곽 원 카드가 추가됩니다. 문법이 맞고 그림도 나오지만 “하나만 선택한다”는 요구가 깨진 경우입니다.

완성 후에는 어느 함수가 실행되지 않았는지도 말해 봅니다. dots 선택에서는 show_bars와 show_rings의 정의가 준비되어 있어도 두 본문은 실행되지 않습니다. 코드를 위에서부터 전부 그리기 명령으로 읽지 않고, 정의·조건 판단·선택된 호출을 나누어 읽는 것이 핵심입니다.

들여쓰기 비교
앞의 카드 함수 준비 후 · 두 출력의 원인
앞의 카드 함수 준비 후 · 두 출력의 원인
style = "bars"

if style == "bars":
    show_bars()

show_rings()

show_rings()는 조건문 밖이므로 첫 조건과 무관하게 실행됩니다. 이 예제의 결과는 막대 한 장과 윤곽 원 한 장입니다. 호출문을 조건 안에 둘지 밖에 둘지는 실행할 시점을 설계하는 문제입니다.

그리기 규칙과 호출 규칙

막대의 색을 바꾸라는 요청은 show_bars의 본문을 수정하는 일입니다. 채운 원을 선택하게 하라는 요청은 style 또는 분기 안의 호출을 수정하는 일입니다. 둘 다 결과를 바꾸지만 수정하는 책임이 다릅니다. 먼저 제작 요청을 읽고 함수 안의 표현 문제인지, 함수 밖의 선택 문제인지 판단합니다.

이 구분은 다음 함수 조합에서도 이어집니다. 작은 이미지 함수는 정해진 결과를 만들고, 바깥 코드는 그 작업을 언제 어떤 순서로 사용할지 정합니다. 재사용은 같은 줄을 여러 번 호출하는 데서 끝나지 않고, 이미 준비한 작업을 새로운 흐름에 배치하는 것으로 확장됩니다.

9. 함수의 조합

핵심 질문

이미지 두 장의 제작 순서를 하나의 작업으로 묶으면 한 번의 호출에서 무엇이 실행될까요?

선택과 조합

실습 7에서는 막대·채운 원·윤곽 원 중 하나를 선택하여 카드 한 장을 표시했습니다. 이번에는 같은 함수들을 사용해 막대 카드 다음에 채운 원 카드를 표시합니다. 선택은 실행할 작업 하나를 고르고, 조합은 여러 작업의 실행 순서를 정합니다.

실행 방식
구분바깥 코드의 역할사용하는 함수표시 결과
조건별 호출조건에 맞는 호출 하나 실행show_bars 또는 show_dots 또는 show_rings카드 한 장
함수 조합필요한 호출을 순서대로 실행예: show_bars 다음 show_dots카드 두 장

작은 작업과 큰 작업

이번 요구는 서로 다른 안내 카드 두 장을 정해진 순서로 보여 주는 것입니다. 막대와 채운 원의 그리기 본문을 새로 복사할 필요는 없습니다. 이미 준비한 show_bars와 show_dots를 필요한 순서로 호출하면 됩니다. 이 두 호출의 순서 자체도 자주 사용한다면 show_pair_a라는 작업으로 묶을 수 있습니다.

함수의 본문에는 print·display처럼 이미 만들어진 도구의 호출이 들어 있었습니다. 자신이 정의한 함수도 같은 방식으로 본문에서 호출할 수 있습니다. 함수 안에 또 다른 def를 넣는 것이 아니라, 바깥에서 준비한 함수의 이름 뒤에 괄호를 붙여 실행을 요청합니다.

두 장의 세트개념 비유
막대 도장과 원 도장 아래, 막대 카드와 채운 원 카드가 각각 놓인 두 칸의 남색 포트폴리오
한 세트를 만드는 작업이 두 카드의 제작을 차례로 요청하는 모습입니다. 조합 함수 한 번의 호출 안에서 두 이미지 함수를 호출하며, 결과는 서로 독립적인 두 장입니다. 실습에서는 두 이미지가 호출 순서대로 표시됩니다.

호출과 복귀의 경로

큰 작업을 실행하다 작은 함수를 만나면 그 본문을 끝낸 뒤 원래 작업의 다음 문장으로 돌아옵니다. prepare_card를 불렀다고 publish_card의 나머지 부분을 잊는 것이 아닙니다. 어느 호출의 다음 줄로 돌아오는지 표시하면 여러 함수가 있어도 실행 순서를 따라갈 수 있습니다.

아래 코드는 세 정의를 준비한 뒤 맨 아래 publish_card()에서 제작을 시작합니다. 그 본문의 시작 안내가 먼저 출력되고, prepare_card와 show_title의 호출을 차례로 만납니다. 표에서 작은 작업을 마친 뒤 돌아오는 위치까지 함께 확인합니다.

큰 작업 안의 두 호출
큰 작업 안의 두 호출
def prepare_card():
    print("2. 카드 준비")

def show_title():
    print("3. 제목 표시")

def publish_card():
    print("1. 제작 시작")
    prepare_card()
    show_title()
    print("4. 제작 완료")

publish_card()
print("5. 바깥 작업")

호출 경로
순서현재 실행 위치출력 후 다음 위치
1publish_card의 시작 printprepare_card()
2prepare_card의 본문publish_card의 show_title()
3show_title의 본문publish_card의 완료 print
4publish_card의 완료 print맨 아래 바깥 print
5바깥 print전체 작업 끝
호출 경로 해설

출력 번호는 1 → 2 → 3 → 4 → 5입니다. publish_card의 본문 안에서 두 작은 작업을 차례로 사용하고, 큰 작업도 끝나면 바깥 호출 다음 줄로 돌아옵니다. prepare_card와 show_title의 정의를 서로 바꿔 놓아도 호출 순서가 같고 실행 전에 두 정의가 준비되었다면 출력 순서는 같습니다.

정의 순서와 사용 순서

아래 예제는 끝 안내를 먼저 정의하지만 시작 안내부터 호출합니다. 파일에서 정의가 나타난 순서와 작업이 수행되는 순서가 항상 같지는 않다는 것을 확인합니다. 단, 호출하는 순간에는 해당 이름의 정의가 준비되어 있어야 합니다.

이번 노트북에서는 읽기 쉽게 작은 함수를 먼저 정의하고 조합 함수를 그다음에 정의합니다. 이어서 조합 함수를 호출합니다. 런타임의 이전 상태에 기대지 않고 위에서 아래로 읽어도 준비와 사용의 순서를 확인할 수 있는 배치입니다.

정의 순서와 호출 순서의 비교
정의 순서와 호출 순서의 비교
def show_end():
    print("끝")

def show_start():
    print("시작")

show_start()
show_end()

실습 8 · 함수 조합

공통 원리
이미지 두 장의 호출 순서를 하나의 함수로 묶어 다시 사용합니다.
유지 요소
실습 7의 세 이미지 함수 · 각 이미지의 제작 규칙 · 조합당 출력 두 장

실습 7에서 정의한 show_bars·show_dots·show_rings를 그대로 사용합니다. A의 pass를 두 호출로 교체하고 바깥의 show_pair_a()를 실행합니다. B·C도 같은 방식으로 호출 흐름을 비교합니다. 아래 결과는 각 호출에서 나온 두 이미지를 출력 순서대로 위아래에 배치한 것입니다.

A 막대와 채움

show_bars → show_dots 순서로 호출합니다.

결과 해설 막대 카드 다음에 채운 원 카드가 표시됩니다. 위아래 두 칸은 서로 다른 출력입니다.

1-8A · 실습 코드
A · 조합 정의와 호출
A · 조합 정의와 호출
def show_pair_a():
    # TODO: 막대 카드와 채운 원 카드의 호출을 순서대로 작성하세요.
    pass

show_pair_a()

완성 코드
해설용 완성 코드
해설용 완성 코드
def show_pair_a():
    show_bars()
    show_dots()

show_pair_a()

B 채움과 윤곽

show_dots → show_rings 순서로 호출합니다.

결과 해설 채운 원 카드 다음에 윤곽 원 카드가 표시됩니다.

1-8B · 실습 코드
B · 조합 정의와 호출
B · 조합 정의와 호출
def show_pair_b():
    show_dots()
    show_rings()

show_pair_b()

C 윤곽과 막대

show_rings → show_bars 순서로 호출합니다.

결과 해설 윤곽 원 카드 다음에 막대 카드가 표시됩니다.

1-8C · 실습 코드
C · 조합 정의와 호출
C · 조합 정의와 호출
def show_pair_c():
    show_rings()
    show_bars()

show_pair_c()

A·B·C 결과 비교

비교 기록

바깥 호출
___
안쪽 호출 순서
___
출력 장수
___
공통 수정의 영향
___

출력 단위의 검증

show_bars의 약속은 카드 한 장이고 show_pair_a의 약속은 카드 두 장의 순서입니다. 따라서 “함수 한 번 호출은 항상 이미지 한 장”이라고 일반화하면 안 됩니다. 한 번의 호출이 무엇을 완성할지는 그 함수의 역할과 본문으로 결정합니다.

show_pair_a를 두 번 호출하면 카드 네 장이 나옵니다. 바깥 호출 두 번 × 안쪽 이미지 함수 두 번 × 각 이미지의 요소 세 개이므로 전체 도형 처리는 열두 번입니다. 화면 출력 수와 개별 이미지 안의 요소 수를 함께 기록하면 서로 다른 반복 단위를 구분할 수 있습니다.

작업 단위별 횟수
실행 요청작은 이미지 함수 실행캔버스·출력전체 도형 처리
show_bars() 한 번1회1장3회
show_pair_a() 한 번2회2장6회
show_pair_a() 두 번4회4장12회

조합의 공통 수정

실습 8의 A는 막대 → 채운 원, B는 채운 원 → 윤곽 원, C는 윤곽 원 → 막대 순서입니다. 막대의 색을 바꾸면 A의 첫 이미지와 C의 두 번째 이미지에 변화가 적용됩니다. B에는 막대 함수의 호출이 없으므로 그 수정의 영향을 받지 않습니다.

반대로 A의 두 호출 순서만 바꾸면 A의 이미지 표시 순서만 달라집니다. 각 이미지의 색·형태·좌표는 그대로입니다. 공통 본문의 수정은 그 본문을 쓰는 모든 호출에 영향을 주고, 조합 순서의 수정은 해당 조합의 흐름에 영향을 줍니다. 아래 표에서 수정 위치와 영향을 받는 결과를 연결합니다.

수정의 영향 범위
제작 요청수정 위치변화가 나타날 결과
막대 색상 변경show_bars의 fillA 첫 장·C 두 번째 장
A의 순서 뒤집기show_pair_a의 두 호출A의 표시 순서
모든 윤곽 원 두께 변경show_rings의 widthB 두 번째 장·C 첫 장

작업 단위의 선택

도형 한 개마다 함수를 만드는 방법도 가능하지만 이번 요구에 항상 필요한 것은 아닙니다. 너무 작은 단위로 나누면 작업 순서를 이해하려고 여러 정의를 오가야 합니다. 반대로 선택 조건과 모든 그리기 본문을 하나에 넣으면 수정할 역할을 찾기 어려워집니다.

이번 카드에서는 한 장의 제작과 두 장의 조합이 각각 다시 사용할 작업이므로 이름을 붙였습니다. 한 장의 표현은 작은 함수에서 관리하고, 두 장의 순서는 조합 함수에서 관리합니다. 이 역할을 기준으로 다음 오류 진단에서 준비·호출·들여쓰기 중 어디가 어긋났는지 살펴봅니다.

10. 오류 진단

핵심 질문

오류 메시지가 있는 실패와 오류 없이 잘못된 출력을 만드는 실패는 어떻게 다를까요?

오류 메시지의 판독

오류를 확인할 때는 마지막 줄의 오류 이름과 설명을 먼저 읽습니다. 그다음 표시된 줄로 돌아가 해당 이름이나 들여쓰기를 살펴봅니다. 위쪽에 여러 줄의 경로가 보여도 모두 새로 작성해야 한다는 뜻은 아닙니다. 오류가 발생한 위치와 그 코드가 필요로 하는 준비를 연결합니다.

다음 두 예제는 의도적으로 잘못 작성했습니다. 한 번 실행하여 메시지를 확인한 뒤 같은 셀을 수정합니다. 진단을 위해 만든 오류를 마지막 상태로 남기지 않아야 이후 전체 실행이 이어집니다. 메시지의 파일명과 줄 번호는 실행 환경에 따라 다를 수 있으므로 오류 종류와 해당 코드를 중심으로 읽습니다.

이름 오류

아래 정의는 show_notice인데 호출은 show_notcie입니다. 글자의 순서가 다른 이름을 호출하므로 준비된 함수를 찾지 못합니다. 본문이 틀린 것인지 호출에서 찾는 이름이 틀린 것인지 구분해 보세요.

핵심 질문

준비된 이름을 찾지 못하면 본문의 print는 실행될까요? 아래 오류 메시지와 수정 결과를 함께 확인합니다.

오류 진단 · 실행 후 같은 셀 수정
오류 진단 · 실행 후 같은 셀 수정
def show_notice():
    print("작업 완료")

show_notcie()

실제 오류와 수정
NameError: name 'show_notcie' is not defined

오류가 가리키는 show_notcie를 정의에 있는 show_notice와 대조합니다. 이번에는 철자를 맞추면 해결됩니다. 이름이 정확한데도 같은 종류의 오류가 나면 정의 셀을 실행했는지 확인합니다. 원인을 확인하지 않고 새로운 함수 이름을 추가하면 작업이 여러 이름으로 나뉠 수 있습니다.

수정 코드
수정 코드
def show_notice():
    print("작업 완료")

show_notice()

수정 후에는 “작업 완료” 한 줄이 출력됩니다. 함수의 본문은 그대로 두고 잘못된 호출만 고쳤습니다. 수정한 줄과 출력 결과를 함께 기록하면 무엇이 원인이었는지 다시 설명할 수 있습니다.

들여쓰기 오류

정의의 콜론 아래에는 본문이 있어야 합니다. 다음 코드는 print를 함수 밖 수준에 두어 정의에 속하는 본문이 없습니다. 메시지가 print 줄을 가리키더라도 print라는 도구가 잘못된 것이 아니라 그 줄의 포함 관계가 문제입니다.

오류 진단 · 본문의 위치
오류 진단 · 본문의 위치
def show_notice():
print("작업 완료")

show_notice()

실제 오류와 수정
IndentationError: expected an indented block after function definition on line 1 (lesson-example, line 2)

print 앞에 네 칸을 넣으면 함수 본문이 됩니다. 마지막 show_notice()는 함수 밖에서 실행할 요청이므로 맨 왼쪽에 둡니다. 두 줄을 모두 들여쓰면 호출까지 함수 안으로 들어가므로 이번 목적과 달라집니다.

수정 코드
수정 코드
def show_notice():
    print("작업 완료")

show_notice()

수정 후 “작업 완료”가 한 번 출력되는지 확인합니다. 오류 메시지가 사라졌다는 사실과 본문이 의도한 횟수만큼 실행되었다는 사실을 함께 확인해야 합니다.

문법 오류와 출력 오류

def 줄의 콜론을 빠뜨린 경우에는 정의문을 해석할 수 없어 SyntaxError가 발생합니다. 반면 함수를 정의하고 이름만 적거나, 본문에 pass만 남기면 오류 메시지 없이 원하는 출력이 없을 수 있습니다. 메시지의 유무만으로 완성 여부를 판단하지 않습니다.

노트북의 호출 누락 셀은 announce_rule이라는 이름만 마지막에 적혀 있습니다. 괄호가 없으므로 본문의 print를 실행하는 요청이 아닙니다. 환경에 따라 함수에 대한 짧은 표현이 표시되더라도 그것은 도형 규칙 문장의 출력과 다릅니다. 실제 본문이 실행되었는지 확인합니다.

호출 누락 · 괄호 수정
호출 누락 · 괄호 수정
def announce_rule():
    print("큰 크기는 사각형으로 그립니다.")

announce_rule

호출 누락 해설
괄호를 복구한 호출
괄호를 복구한 호출
def announce_rule():
    print("큰 크기는 사각형으로 그립니다.")

announce_rule()

announce_rule()로 고치면 규칙 문장이 출력됩니다. 여기서는 정의가 준비되어 있었지만 실행 요청이 없었습니다. 앞의 NameError처럼 이름 자체를 찾지 못한 상황과 구분합니다.

실습 9 · 반복 범위

도형 번호는 세 개, 완료 안내는 마지막에 한 번만 출력하려고 합니다. 아래 코드의 들여쓰기 한 곳을 고친 뒤, 이미지 코드의 display 위치와 연결해 설명하세요.

수정 전 코드 · 해설과 함께 완성
수정 전 코드 · 해설과 함께 완성
def show_progress():
    for i in range(3):
        print("도형", i)
        print("완료")

show_progress()

해설과 완성 코드
완성 코드
완성 코드
def show_progress():
    for i in range(3):
        print("도형", i)
    print("완료")

show_progress()

완료 print를 여덟 칸에서 네 칸으로 옮기면 도형 0·1·2 다음에 완료가 한 번 나옵니다. 반복 횟수나 문구는 바꾸지 않습니다. 한 장을 다 그린 뒤 display를 실행하는 구조와 같습니다.

최소 수정과 재검증

실습 9는 문법이 맞지만 완료 문장의 횟수가 요구와 다른 논리 오류입니다. 원하는 출력이 “도형 번호 세 개와 마지막 완료 한 번”이라는 기준이 있어야 현재 결과가 틀렸다고 판단할 수 있습니다. 기준이 없으면 실행된다는 이유만으로 넘어가기 쉽습니다.

오류를 고칠 때는 원인으로 판단한 한 곳을 먼저 수정합니다. 함수 이름, 반복 수, 색, 들여쓰기를 동시에 바꾸면 어떤 수정이 문제를 해결했는지 알기 어렵습니다. 수정 후 같은 입력으로 재실행하고 처음의 요구와 다시 비교합니다. 이 방식은 이후 생성기에서 입력을 연결하거나 반환 위치를 고칠 때에도 사용할 수 있습니다.

증상별 진단 경로
증상우선 점검완료 확인
NameError이름 철자·정의 셀 실행올바른 본문 출력
IndentationErrordef 아래 본문과 공백정의 후 호출 성공
SyntaxError콜론·괄호 등 문장 구조정의 셀 실행 성공
오류 없이 출력 없음호출 괄호·본문 내용·표시 명령예상한 결과 표시
중간 그림이 여러 장display의 반복 포함 여부요구한 표시 횟수
마지막 도형만 표시Image.new의 반복 포함 여부한 캔버스의 누적 결과

“함수 이름의 철자를 맞춘 뒤 문장이 한 번 출력되었다”처럼 수정 내용과 결과를 연결합니다. 앞의 선택·조합에서도 같은 기준으로 호출 경로를 확인할 수 있습니다. 마지막 배지 실습에서는 이 점검 방식을 새 이미지 코드의 함수화에 적용합니다.

11. 종합 실습과 정리

핵심 질문

원 세 개의 배지를 한 번의 작업으로 묶으면 호출과 공통 수정이 어떻게 연결될까요?

새 코드의 함수화

지금까지 살펴본 정의·호출·준비·공통 수정을 원 세 개의 배지 코드에 적용합니다. 한 번 호출하면 배지 한 장이 완성되도록 준비부터 표시까지 묶는 과정을 해설과 함께 따라갑니다.

아래 기준 코드는 320 × 160 캔버스에 같은 크기의 윤곽 원 세 개를 그립니다. 첫 왼쪽 좌표는 35이고 간격은 90입니다. 각 원의 가로·세로 범위는 60이며 위쪽 y는 50입니다. 먼저 한 장을 실행해 기준을 확인하고, 함수 변환 중에는 이 숫자를 유지합니다.

작업 단위의 기준

이번 배지 함수의 작업 단위는 “원 세 개가 있는 새 배지 한 장을 표시한다”입니다. 원 한 개의 제작이나 배지 두 장의 제작을 본문에 고정하지 않는 이유는, 한 장의 규칙을 유지하면서 바깥 호출 수로 필요한 장수를 정하기 위해서입니다.

원본 코드에서 새 문법을 더 찾을 필요는 없습니다. 준비·반복·표시의 포함 관계를 보존하면서 작업의 이름을 추가하는 문제입니다. 이미지는 원본과 같고 재사용 방식만 바뀌는 A를 먼저 만든 뒤, B의 두께와 C의 채움 변형을 각각 A와 대조합니다.

배지의 완성 조건
구분요구검증 방법
한 번의 작업새 배지 한 장한 번 호출한 출력 장수
한 장의 구성같은 위치의 원 세 개원본과 좌표·크기 대조
재사용두 번 호출해 두 장 제작캔버스 수·출력 수 확인
공통 수정한 줄의 두께가 모든 원에 적용두 출력의 원 여섯 개 대조
본문 범위준비부터 표시까지호출을 제외한 포함 관계 점검

장수와 본문의 구분

원 한 개만 묶으면 호출한 쪽에서 캔버스 준비와 세 원의 배치를 다시 관리해야 합니다. 배지 두 장을 본문에 고정하면 세 장이 필요해졌을 때 본문을 수정해야 합니다. 여기서는 한 장 제작을 함수로 묶고 장수를 바깥 호출로 정합니다.

앞의 조합 함수는 두 작업의 순서를 재사용하려고 두 호출을 묶었습니다. 이번 배지는 한 장을 필요한 횟수만큼 만들려는 작업입니다. 따라서 함수 한 번이 항상 한 장을 뜻하는 것이 아니라, 이번 제작 목적에서 한 장을 작업 단위로 선택한 것입니다.

배지 기준 코드
배지 기준 코드
from PIL import Image, ImageDraw
from IPython.display import display

badge = Image.new("RGB", (320, 160), (245, 240, 228))
badge_draw = ImageDraw.Draw(badge)
for i in range(3):
    x = 35 + i * 90
    badge_draw.ellipse((x, 50, x + 60, 110), outline=(37, 62, 78), width=2)
display(badge)

실행 결과 비교같은 코드와 입력으로 확인
배지 기준 · 같은 크기의 윤곽 원 세 개
배지 기준 · 같은 크기의 윤곽 원 세 개

예시 이름은 show_badge입니다. 이미지 준비부터 마지막 표시까지 묶고, import는 공통 준비에 남깁니다. 함수 밖에서 두 번 호출하면 세 원이 있는 배지 두 장이 나옵니다. 이미 배운 들여쓰기와 실행 순서를 같은 방식으로 적용합니다.

좌표와 본문 범위

원본에서 i가 0·1·2일 때 왼쪽 x는 35·125·215입니다. 오른쪽 끝은 각각 95·185·275이므로 세 원 모두 너비 320 안에 들어갑니다. 함수로 옮긴 뒤에도 이 계산을 유지해야 합니다. 출력 수만 같아도 마지막 원이 잘렸다면 좌표나 캔버스 준비가 달라졌는지 확인합니다.

본문은 badge의 준비에서 시작하여 마지막 display에서 끝납니다. for와 ellipse는 원래의 포함 관계를 유지한 채 한 단계 더 안으로 들어갑니다. import는 공통 도구를 준비하고, 바깥의 두 호출은 장수를 정하므로 본문과 구분합니다.

배지 함수의 구성
항목코드의 범위와 역할
함수 이름show_badge · 배지 한 장 표시
본문의 시작과 끝badge = Image.new(...)부터 display(badge)까지
반복에 속할 명령x 계산과 badge_draw.ellipse(...)
함수 밖의 명령import와 show_badge() 두 호출
두 호출의 결과캔버스 2개 / 도형 처리 6회 / 표시 2회

노트북의 “실습 10 · 배지 함수”를 완성 코드와 함께 읽고 수정합니다. A에서는 원본과 같은 결과를 유지하며 함수 구조를 확인합니다. 그다음 B의 선 두께와 C의 채움을 바꾸어, 본문 수정이 각 호출 결과에 적용되는 과정을 확인합니다.

배지의 공통 수정

두 번 호출한 기준 결과를 남긴 뒤 다음 셀에 자신의 함수 정의를 옮깁니다. width=2 한 곳을 width=6으로 바꾸고 새 정의 다음에 같은 함수를 두 번 호출합니다. 앞 셀의 수정 전 코드와 출력은 보존하여 전후 비교에 사용합니다.

for 안의 세 원이 모두 같은 width를 읽으므로 한 장의 세 원 전체가 두꺼워집니다. 두 호출이 같은 본문을 사용하므로 두 장 모두 같은 변화가 나타납니다. 원의 위치·크기·개수는 그대로입니다. 이 연결을 A와 B의 결과에서 확인합니다.

핵심 질문

width를 한 곳 수정하면 각 이미지의 원 몇 개가 바뀔까요? 두 장 중 한 장만 달라졌다면 어떤 정의와 호출을 먼저 확인해야 할까요?

실습 10 · 배지 함수

공통 원리
원 세 개를 만드는 작업을 함수로 묶고 같은 함수를 두 번 호출합니다.
유지 요소
320 × 160 캔버스 · 원의 수·크기·위치 · 두 번의 호출

A는 원본 코드를 함수로 묶고 밖에서 두 번 호출합니다. B는 width를 6으로 고치고, C는 outline·width 대신 fill을 사용합니다. 각 버전의 두 출력이 같은지 확인합니다. 아래 비교 이미지는 각 버전의 첫 번째 출력입니다.

A 얇은 윤곽

원본을 함수로 묶고 outline과 width=2를 유지합니다.

결과 해설 세 원의 얇은 윤곽선이 있는 배지입니다.

1-10A · 실습 코드
실습 코드
실습 코드
from PIL import Image, ImageDraw
from IPython.display import display

badge = Image.new("RGB", (320, 160), (245, 240, 228))
badge_draw = ImageDraw.Draw(badge)
for i in range(3):
    x = 35 + i * 90
    badge_draw.ellipse((x, 50, x + 60, 110), outline=(37, 62, 78), width=2)
display(badge)

완성 코드
해설용 완성 코드
해설용 완성 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_badge():
    badge = Image.new("RGB", (320, 160), (245, 240, 228))
    badge_draw = ImageDraw.Draw(badge)
    for i in range(3):
        x = 35 + i * 90
        badge_draw.ellipse((x, 50, x + 60, 110), outline=(37, 62, 78), width=2)
    display(badge)

show_badge()
show_badge()

B 굵은 윤곽

A의 width만 6으로 바꿉니다.

결과 해설 A와 같은 세 원의 윤곽선만 두꺼워집니다.

1-10B · 실습 코드
실습 코드
실습 코드
# TODO: 앞에서 만든 배지 함수의 정의를 이 셀에 옮기고 width=2 한 곳을 width=6으로 바꾸세요.
# 새 정의 뒤에 같은 함수를 두 번 호출하세요.

완성 코드
해설용 완성 코드
해설용 완성 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_badge():
    badge = Image.new("RGB", (320, 160), (245, 240, 228))
    badge_draw = ImageDraw.Draw(badge)
    for i in range(3):
        x = 35 + i * 90
        badge_draw.ellipse((x, 50, x + 60, 110), outline=(37, 62, 78), width=6)
    display(badge)

show_badge()
show_badge()

C 채운 원

A의 ellipse에서 outline·width 대신 fill을 사용합니다.

결과 해설 같은 위치의 원 세 개가 짙은 색으로 채워집니다.

1-10C · 실습 코드
실습 코드
실습 코드
from PIL import Image, ImageDraw
from IPython.display import display

def show_filled_badge():
    badge = Image.new("RGB", (320, 160), (245, 240, 228))
    badge_draw = ImageDraw.Draw(badge)
    for i in range(3):
        x = 35 + i * 90
        badge_draw.ellipse((x, 50, x + 60, 110), fill=(37, 62, 78))
    display(badge)

show_filled_badge()
show_filled_badge()

A·B·C 결과 비교

비교 기록

공통 원리
___
바꾼 줄
___
A와 B의 차이
___
A와 C의 차이
___

구성 점검

배지에 원이 하나만 보이면 단순히 range(3)을 더 큰 값으로 바꾸지 않습니다. 캔버스가 반복 안에서 새로 만들어지는지 먼저 확인합니다. 이미지가 세 장씩 나오면 display가 반복 안에 있는지 확인합니다. 기준 코드는 이미 작동하므로 함수로 옮기면서 달라진 범위에 집중합니다.

마지막 호출까지 함수 안으로 들여쓰지 않았는지도 확인합니다. 이번 작업은 바깥에서 필요할 때 한 장을 요청하는 구조입니다. 정의만 실행했을 때 출력이 없고 바깥에서 호출했을 때 한 장이 나오는지 나누어 확인하면, 어디에서 실행을 시작하는지 분명해집니다.

show_badge라는 이름이 가리키는 작업은 새 배지 한 장의 제작과 표시입니다. 본문의 범위, 두 호출의 실행 횟수, 한 곳의 수정이 적용된 결과를 연결하면 앞에서 배운 함수의 역할을 같은 구조로 설명할 수 있습니다.

학습 내용 점검

각 개념은 이번에 남긴 결과를 근거로 설명합니다. 코드 중복의 문제는 첫 복사본, 정의와 호출은 실습 1과 이미지 함수, 실행 위치는 A·B·C 비교, 재사용은 배지의 공통 수정에서 확인할 수 있습니다. 답을 외워 쓰기보다 자신이 실행한 셀과 출력의 연결을 찾습니다.

최종 점검
질문근거로 사용할 결과
함수가 필요한 이유는?복사본의 수정 누락과 공통 본문 수정
정의와 호출의 차이는?정의 직후와 호출 직후의 출력
for와 함수의 역할은?한 장의 도형 반복과 여러 장의 호출
들여쓰기는 무엇을 결정하는가?A·B·C의 생성·표시 위치
호출마다 필요한 준비는?함수 안의 고정값과 새 캔버스
선택한 함수만 실행되는 이유는?실습 7의 조건과 호출 경로
큰 작업의 출력 수는?실습 8의 작은 함수 호출 수와 순서
새 코드에도 적용할 수 있는가?배지의 함수 범위와 두 호출
수정이 반영되지 않으면 무엇을 확인하는가?편집한 정의 실행과 새 호출

기준 복구와 다음 학습

배지 실습의 코드와 출력·기록을 보존한 뒤 마지막 기준 복구 셀을 실행합니다. 산호색 사각형과 경계 60/40의 여섯 도형 함수가 다음 교시의 출발점입니다. 복구는 지금까지의 실습을 지우는 작업이 아니라, 다음 비교에서 공통으로 사용할 상태를 준비하는 작업입니다.

현재 show_shape_row는 입력값을 받지 않고 본문에 경계 60과 40을 직접 사용합니다. 이제 “같은 작업으로 큰 경계 60인 이미지와 63인 이미지를 각각 만들자”는 요구를 생각합니다. 본문을 두 벌로 복사하면 처음의 중복 문제가 다시 생기므로, 공통 작업에 다른 값을 전달할 자리가 필요합니다. 2교시에서 매개변수를 추가하며 이 요구를 해결합니다.

2교시 기준 함수
정상 기준 복구
정상 기준 복구
from PIL import Image, ImageDraw
from IPython.display import display

def show_shape_row():
    sizes = [30, 46, 64, 46, 60, 38]
    palette = [(37, 62, 78), (111, 143, 126), (201, 111, 93)]
    image = Image.new("RGB", (660, 220), (245, 240, 228))
    draw = ImageDraw.Draw(image)

    for i in range(6):
        x = 50 + i * 100
        y = 80
        size = sizes[i]
        if size >= 60:
            draw.rectangle((x, y, x + size, y + size), fill=palette[2])
        elif size >= 40:
            draw.ellipse((x, y, x + size, y + size), fill=palette[1])
        else:
            draw.ellipse((x, y, x + size, y + size), outline=palette[0], width=2)

    display(image)

show_shape_row()

마지막 결과가 산호색 사각형 두 개를 포함하는지 확인합니다. 공통 준비부터 순서대로 실행해 기준 이미지와 함수가 준비된 상태를 저장합니다. 다음 교시에는 같은 노트북의 2교시 구역에서 이 함수를 먼저 호출합니다.

참고 자료

이 페이지의 목차