본문으로 건너뛰기

단원 연습 문제

파이썬 목차파일과 모듈문제 12개

10단원 '파일과 모듈'의 다섯 레슨에서 여덟 문제를 내고, 앞 단원(반복과 패턴 · 함수 · 오류와 예외)에서 네 문제를 섞었습니다. 눈으로 코드를 따라가 무엇이 찍히는지 쓰거나, 빈칸을 채우고, 잘못된 줄을 고치거나, 짧은 프로그램을 직접 만듭니다.

한 번에 다 풀 필요는 없습니다. 문제 1~10은 폰으로도 풀 수 있고(약 15분), 끝의 만들어 보기 둘은 컴퓨터에서 직접 돌려 봅니다. 이 단원을 건너뛰어도 되는지 보려면 문제 2·4·7·9를 먼저 풀어 보세요(약 6분). 건너뛸지 볼 때는 힌트를 펴지 않고 풉니다. 모두 맞으면 11단원 첫 레슨 클래스와 객체로 넘어가고, 하나라도 틀리면 정답 끝에 적힌 '다시 볼 레슨'으로 돌아갑니다.

끝의 두 문제는 프로그램을 직접 만들어 봅니다. 아래 코드 칸에서 바로 고쳐 돌려 볼 수 있고, 내 컴퓨터에서 돌려 보는 길은 내 컴퓨터에서 돌려 보기에 있습니다. 실행 결과는 모두 파이썬 3.15로 돌린 것입니다.

문제 1

콜론(:)으로 이어진 문자열을 쪼갠 뒤 다시 하이픈(' - ')으로 이어 붙입니다. 무엇이 찍힐까요? 두 줄을 쓰세요.

s = '봄:여름:가을'
parts = s.split(':')
print(parts[0])
print(' - '.join(parts))
힌트

s.split(':')은 문자열을 ':' 자리마다 잘라 리스트 ['봄', '여름', '가을']을 돌려줍니다. parts[0]이 무엇인지, ' - '.join(parts)가 사이사이에 무엇을 끼워 넣는지 보세요.

정답
실행 결과
봄
봄 - 여름 - 가을
  • 2번 줄: '봄:여름:가을'.split(':')이 리스트 ['봄', '여름', '가을']을 만들어 parts에 붙입니다.
  • 3번 줄: 첫 번째 원소 parts[0]인 '봄'을 찍습니다.
  • 4번 줄: ' - '.join(parts)가 리스트 원소 사이마다 ' - '를 끼워 넣은 '봄 - 여름 - 가을'을 찍습니다.

흔한 오답은 마지막 '가을' 뒤에도 ' - '를 붙여 '봄 - 여름 - 가을 - '로 쓰는 것입니다. join()은 원소 사이에만 구분자를 넣습니다.

다시 볼 레슨: 글과 리스트 사이: split·join

문제 2

같은 파일 memo.txt를 'w' 모드와 'a' 모드로 차례로 열어 쓴 뒤 읽습니다. 무엇이 찍힐까요? 두 줄을 쓰세요.

with open('memo.txt', 'w') as f:
    f.write('사과\n')
with open('memo.txt', 'w') as f:
    f.write('배\n')
with open('memo.txt', 'a') as f:
    f.write('감\n')
with open('memo.txt', 'r') as f:
    print(f.read(), end='')
힌트

3번 줄에서 memo.txt를 다시 'w' 모드로 열 때, 1~2번 줄에서 써 두었던 '사과\n'가 어떻게 되는지 생각해 보세요.

정답
실행 결과
배
감
  • 1~2번 줄: memo.txt를 새로 만들어 '사과\n'를 씁니다.
  • 3~4번 줄: 다시 'w'(덮어쓰기) 모드로 여는 순간 기존 '사과\n'가 지워지고 '배\n'만 남습니다.
  • 5~6번 줄: 'a'(이어쓰기) 모드로 열어 기존 '배\n' 뒤에 '감\n'를 덧붙입니다.

흔한 오답은 '사과', '배', '감' 세 줄이 모두 찍힌다고 쓰는 것입니다. 'w' 모드로 파일을 다시 열면 이전 내용이 통째로 사라집니다.

다시 볼 레슨: 파일에 쓰기

문제 3

다음 코드를 실행하면 어떻게 될까요? 알맞은 것을 고르세요.

nums = [10, 20, 30]
print(', '.join(nums))
  • ① 10, 20, 30이 찍히고 끝난다
  • ② [10, 20, 30]이 찍히고 끝난다
  • ③ 2번 줄에서 TypeError로 멈춘다
  • ④ 2번 줄에서 AttributeError로 멈춘다
힌트

', '.join(...)에 넘기는 리스트의 원소들이 문자열(str)인지 정수(int)인지 확인해 보세요.

정답

③ — 실행 결과는 이렇습니다.

실행 결과
Traceback (most recent call last):  File "main.py", line 2, in <module>    print(', '.join(nums))          ~~~~~~~~~^^^^^^TypeError: sequence item 0: expected str instance, int found
  • ③ (정답): join()은 리스트의 모든 원소가 문자열(str)이어야 합칠 수 있습니다. 정수 10이 들어 있어 TypeError로 멈춥니다.
  • ① print()처럼 join()도 정수를 알아서 문자열로 바꿔 준다고 봤습니다. 먼저 [str(n) for n in nums]⁠로 바꾼 뒤 합쳐야 합니다.
  • ④ AttributeError는 nums.join(', ')처럼 리스트에 .join()을 불렀을 때 납니다.

다시 볼 레슨: 글과 리스트 사이: split·join

문제 4

파일에 적힌 세 줄을 한 줄씩 읽어 쉼표 뒤의 숫자를 모두 더합니다. 무엇이 찍힐까요?

with open('item.txt', 'w') as f:
    f.write('사과,1000\n')
    f.write('배,2000\n')
    f.write('감,1500\n')
total = 0
with open('item.txt', 'r') as f:
    for line in f:
        s = line.strip()
        row = s.split(',')
        total += int(row[1])
print(total)
힌트

8~9번 줄에서 line.strip()과 s.split(',')을 실행하면 첫 줄은 ['사과', '1000']이 됩니다. row[0]과 row[1] 가운데 어느 쪽이 가격인지 보세요.

정답
실행 결과
4500
  • 7~10번 줄: 바퀴마다 한 줄을 읽어 끝의 \n을 떼고(strip()), 쉼표로 쪼갠 뒤(split(',')) 두 번째 조각 row[1]을 정수로 바꿔 total에 더합니다.
  • 1000 + 2000 + 1500이 모여 4500이 찍힙니다.

다시 볼 레슨: 파일 읽어 모으기 · 글과 리스트 사이: split·join · 합 모으기와 세기

문제 5

math 모듈에서 올림(ceil)과 내림(floor) 함수를 가져다 씁니다. 무엇이 찍힐까요? 두 줄을 쓰세요.

import math
from math import floor

print(math.ceil(3.2))
print(floor(3.8))
힌트

math.ceil(3.2)는 3.2 이상인 가장 작은 정수를 돌려주고, floor(3.8)은 3.8 이하인 가장 큰 정수를 돌려줍니다.

정답
실행 결과
4
3
  • 1번 줄 import math로 모듈 전체를 가져왔으므로 math.ceil(3.2)는 4를 돌려줍니다.
  • 2번 줄 from math import floor⁠로 floor를 직접 꺼내 왔으므로 마침표 없이 floor(3.8)로 불러 3을 돌려줍니다.

흔한 오답은 반올림으로 생각하여 둘 다 3과 4로 거꾸로 쓰는 것입니다. ceil은 무조건 올림, floor는 무조건 내림입니다.

다시 볼 레슨: 모듈 불러 쓰기

문제 6

리스트의 세 과일을 파일 b에 한 줄에 하나씩 저장하려 했는데, 마지막 '감' 한 줄만 남았습니다. 왜 그런지 찾고, 세 줄이 모두 저장되도록 고쳐 쓰세요.

for x in ['사과', '배', '감']:
    with open('b', 'w') as f:
        f.write(x + '\n')
with open('b', 'r') as f:
    print(f.read(), end='')
실행 결과
감
힌트

2번 줄 with open('b', 'w') as f:⁠가 for 반복문 안에 있으면 바퀴가 돌 때마다 파일에 무슨 일이 일어나는지 보세요.

정답

with open('b', 'w') as f: 줄을 for 반복문 바깥(위)으로 옮겨 파일을 한 번만 열고 그 안에서 세 번 f.write()를 부릅니다.

items = ['사과', '배', '감']
with open('b', 'w') as f:
    for x in items:
        f.write(x + '\n')
with open('b', 'r') as f:
    print(f.read(), end='')
실행 결과
사과
배
감

고치기 전에는 바퀴마다 파일을 'w' 모드로 새로 열어 앞 바퀴에 쓴 줄을 지워 버렸기 때문에 마지막 바퀴의 '감'만 남았습니다.

다시 볼 레슨: 파일에 쓰기 · for와 range

문제 7

같은 폴더에 calc.py와 main.py 두 파일이 아래와 같이 있습니다. main.py를 실행하면 어떻게 될까요? 알맞은 것을 고르세요.

print('calc 로드')


def triple(n):
    return n * 3
from calc import triple

print(triple(4))
  • ① 12 한 줄만 찍힌다
  • ② calc 로드가 먼저 찍히고 다음 줄에 12가 찍힌다
  • ③ 12가 먼저 찍히고 다음 줄에 calc 로드가 찍힌다
  • ④ from calc.py import triple⁠로 적지 않아 ModuleNotFoundError가 난다
힌트

파이썬이 1번 줄 from calc import triple⁠을 만나는 순간 calc.py 파일을 어떻게 처리하는지, 그리고 import 문에 .py를 붙이는지 떠올려 보세요.

정답

② — 첫 줄에 calc 로드, 둘째 줄에 12가 찍힙니다.

  • ② (정답): 1번 줄 from calc import triple⁠을 실행할 때 파이썬이 calc.py를 위에서부터 끝까지 한 번 실행하므로 print('calc 로드')가 먼저 찍히고, 이어서 main.py의 print(triple(4))가 12를 찍습니다.
  • ① from calc import triple⁠이 triple 함수만 골라 오므로 calc.py의 다른 줄은 실행되지 않는다고 봤습니다.
  • ③ main.py가 끝난 뒤에야 calc.py가 실행된다고 봤습니다.
  • ④ import 문에는 파일 확장자 .py를 적지 않습니다. 오히려 .py를 붙이면 오류가 납니다.

다시 볼 레슨: 내 파일을 모듈로

문제 8

파일 a.txt를 만들어 두 번 연속 f.read()를 부른 뒤, 없는 파일 b.txt를 열려 합니다. 무엇이 찍힐까요? 세 줄을 쓰세요.

with open('a.txt', 'w') as f:
    f.write('안녕\n')
with open('a.txt', 'r') as f:
    print(len(f.read()))
    print(len(f.read()))
try:
    open('b.txt', 'r')
except FileNotFoundError:
    print('b 없음')
힌트

'안녕\n'은 세 글자입니다. 4번 줄에서 끝까지 다 읽은 뒤 5번 줄에서 다시 f.read()를 부르면 무엇이 돌아오는지, 7번 줄에서 없는 파일을 'r'로 열면 어디로 가는지 보세요.

정답
실행 결과
3
0
b 없음
  • 4번 줄: 첫 번째 f.read()가 '안녕\n'(3글자)을 끝까지 읽어 길이 3을 찍습니다.
  • 5번 줄: 읽기 위치가 이미 파일 끝에 가 있으므로 두 번째 f.read()는 빈 문자열 ''(길이 0)을 돌려줍니다.
  • 7~9번 줄: 없는 파일 'b.txt'를 'r'로 열 때 FileNotFoundError가 나 except 블록이 'b 없음'을 찍습니다.

흔한 오답은 5번 줄에서도 똑같이 3이 찍힌다고 쓰는 것입니다. 한 번 끝까지 읽은 파일 객체는 자동으로 맨 앞으로 되감기지 않습니다.

다시 볼 레슨: 파일 읽어 모으기 · try와 except

문제 9

1부터 6 사이의 주사위 눈을 두 번 뽑으려 한 코드인데 5번 줄에서 AttributeError가 났습니다. 왜 오류가 났는지 찾고, 4~5번 줄을 어떻게 고쳐야 할지 쓰세요.

import random

random.seed(0)
random = random.randint(1, 6)
print(random.randint(1, 6))
실행 결과
Traceback (most recent call last):  File "main.py", line 5, in <module>    print(random.randint(1, 6))          ^^^^^^^^^^^^^^AttributeError: 'int' object has no attribute 'randint'
힌트

4번 줄 random = random.randint(1, 6)⁠이 실행된 뒤 이름 random이 모듈을 가리키는지 정수를 가리키는지 보세요(규칙 3).

정답

4번 줄에서 결과를 담는 변수 이름을 random이 아닌 다른 이름(예: first)으로 바꿉니다.

import random

random.seed(0)
first = random.randint(1, 6)
print(first)
print(random.randint(1, 6))
실행 결과
4
4

4번 줄에서 변수 이름을 모듈 이름과 같은 random으로 지어, 이름 random이 모듈 대신 정수 4를 가리키게 되었습니다(규칙 3). 그래서 5번 줄이 정수 4에서 .randint를 찾다가 AttributeError로 멈췄습니다.

다시 볼 레슨: 모듈 불러 쓰기 · 변수: 값에 이름 붙이기

문제 10

같은 폴더에 있는 내 파일 price_util.py에서 sale 함수를 꺼내 오고, 표준 라이브러리 math 모듈에서 ceil 함수를 꺼내 와 둘 다 마침표 없이 부르려고 합니다. 빈칸 (가)와 (나)에 들어갈 말을 차례로 쓰세요.

from ___(가)___ import sale
___(나)___ math import ceil

print(sale(10000))
print(ceil(4.2))
힌트

(가)에는 파일 이름 price_util.py에서 확장자 .py를 뺀 모듈 이름을 쓰고, (나)에는 모듈 안에서 특정 이름만 꺼내 올 때 맨 앞에 쓰는 키워드를 씁니다.

정답
  • (가): price_util
  • (나): from

흔한 오답은 (가)에 확장자까지 붙여 price_util.py라고 쓰거나, (나)에 import를 쓰는 것입니다. 모듈 안에서 특정 함수만 꺼내 올 때는 from 모듈이름 import 함수이름 꼴로 적습니다.

다시 볼 레슨: 내 파일을 모듈로 · 모듈 불러 쓰기

문제 11 (만들어 보기)

쉼표(,)로 이어진 숫자 문자열 line(예: '10,20,30')을 받아, 각 숫자를 정수로 바꾸어 모두 더한 값을 돌려주는 함수 sum_csv(line)을 만들어 봅니다. 뼈대의 마지막 줄이 이렇게 찍어야 합니다.

60

끝에 print(sum_csv('5,15')) 한 줄을 더해 한 번 더 부르면 이렇게 나와야 합니다.

60
20

아래 뼈대에서 마지막 줄은 그대로 두고, 주석 셋 아래를 채우세요.

def sum_csv(line):
    # 1. 쉼표로 쪼개기
    # 2. 정수로 바꿔 더하기
    # 3. 합 돌려주기


print(sum_csv('10,20,30'))

처음 펼 때 약 6MB를 받습니다.

힌트

parts = line.split(',')⁠으로 쪼갠 뒤, total = 0을 만들고 for p in parts: 안에서 total += int(p)로 더합니다.

정답

모범 답입니다.

def sum_csv(line):
    # 1. 쉼표로 쪼개기
    parts = line.split(',')
    # 2. 정수로 바꿔 더하기
    total = 0
    for p in parts:
        total += int(p)
    # 3. 합 돌려주기
    return total


print(sum_csv('10,20,30'))
실행 결과
60
  • 다르게 써도 되는 곳: for p in line.split(','):⁠처럼 한 줄로 바로 순회해도 같습니다.
  • 흔한 오답:
    • int(p)로 바꾸지 않고 문자열끼리 더하면 '102030'이 됩니다.
    • line.split()처럼 인자를 비워 두면 쉼표 자리에서 쪼개지지 않고 ['10,20,30'] 한 덩어리가 되어 int('10,20,30')에서 ValueError가 납니다.

다시 볼 레슨: 글과 리스트 사이: split·join · 합 모으기와 세기 · 돌려받은 값 쓰기

문제 12 (만들어 보기)

이번에는 빈 데서 씁니다. 두 줄의 주문 입력('품목,가격')을 받아 o.txt 파일에 저장한 뒤, 다시 그 파일을 한 줄씩 읽어 품목 이름과 가격 합계를 찍는 프로그램을 만들어 봅니다.

  • input('첫째: ')과 input('둘째: ')으로 두 줄을 입력받습니다.
  • o.txt를 'w' 모드로 열어 두 줄을 각각 줄바꿈(\n)과 함께 저장합니다.
  • o.txt를 'r' 모드로 열어 한 줄씩 읽으면서 strip()과 split(',')으로 품목 이름과 가격을 나눕니다.
  • 줄마다 품목 이름을 찍고, 마지막에 '합계:', total을 찍습니다.

'사과,1000'과 '배,2500'을 넣으면 이렇게 나와야 합니다.

첫째: 사과,1000
둘째: 배,2500
사과
배
합계: 3500

입력만 '우유,2000'과 '식빵,3000'으로 바꾸면 이렇게 나와야 합니다.

첫째: 우유,2000
둘째: 식빵,3000
우유
식빵
합계: 5000
# 1. 두 줄 입력받아 파일에 쓰기

# 2. 파일 읽어 품목 찍고 가격 더하기

# 3. 합계 찍기넣는 값사과,1000배,2500

처음 펼 때 약 6MB를 받습니다.

힌트

a = input('첫째: ')와 b = input('둘째: ')를 받은 뒤 with open('o.txt', 'w') as f: 안에서 f.write(a + '\n')와 f.write(b + '\n')를 실행합니다. 이어서 with open('o.txt', 'r') as f:⁠로 한 줄씩 읽으며 s = line.strip()과 row = s.split(',')으로 나눕니다.

정답

모범 답입니다.

a = input('첫째: ')
b = input('둘째: ')
with open('o.txt', 'w') as f:
    f.write(a + '\n')
    f.write(b + '\n')
total = 0
with open('o.txt', 'r') as f:
    for line in f:
        s = line.strip()
        row = s.split(',')
        print(row[0])
        total += int(row[1])
print('합계:', total)넣는 값사과,1000배,2500
실행 결과
첫째: 사과,1000
둘째: 배,2500
사과
배
합계: 3500
  • 다르게 써도 되는 곳: s.split(',')의 결과를 name, price = s.split(',')⁠처럼 두 변수에 나누어 담아도 같습니다.
  • 흔한 오답:
    • f.write(a)처럼 끝에 '\n'을 붙이지 않으면 두 입력이 '사과,1000배,2500' 한 줄로 붙어 저장됩니다.
    • 읽을 때 두 번째 with open('o.txt', 'r') 대신 'w'로 열면 방금 저장한 파일 내용이 지워집니다.

다시 볼 레슨: 파일에 쓰기 · 파일 읽어 모으기 · 글과 리스트 사이: split·join

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

파이썬 목차로