목차
- 06-2 3과 5의 배수를 모두 더하기
- 06-3 게시판 페이징하기
- 06-4 간단한 메모장 만들기
- 06-5 숫자 야구 게임 만들기
- 06-6 시저 암호 만들기
이번에는 1,000 미만의 자연수 중 3의 배수 또는 5의 배수를 모두 더하는 프로그램을 만들어 보자.
예를 들어 10 미만의 자연수 중 3 또는 5의 배수는 다음과 같다.
3, 5, 6, 9
모두 더하면
3 + 5 + 6 + 9 = 23
이 된다.
필요한 것은 크게 두 가지이다.
1~999까지 반복3의 배수 또는 5의 배수인지 확인배수인지 확인할 때는 나머지 연산자 % 를 사용할 수 있다.
6 % 3
결과
0
어떤 숫자를 3으로 나눈 나머지가 0이라면 3의 배수이다.
n % 3 == 0
5의 배수도 동일하다.
n % 5 == 0
range()와 for문을 이용한다.
for n in range(1, 1000):
print(n)
range(1, 1000)은
1 ~ 999
까지의 숫자를 의미한다.
range()의 마지막 숫자는 포함되지 않는다.
or을 사용하면 두 조건 중 하나라도 만족하는 숫자를 찾을 수 있다.
if n % 3 == 0 or n % 5 == 0:
뜻은 다음과 같다.
3으로 나누어떨어지거나
또는
5로 나누어떨어지는 경우
result = 0
for n in range(1, 1000):
if n % 3 == 0 or n % 5 == 0:
result += n
print(result)
실행 결과
233168
15를 생각해 보자.
15 % 3 == 0
15 % 5 == 0
두 조건을 모두 만족한다.
따라서 다음처럼 if를 두 번 사용하면 문제가 발생한다.
if n % 3 == 0:
result += n
if n % 5 == 0:
result += n
15는 두 조건을 모두 만족하므로 두 번 더해진다.
반면
if n % 3 == 0 or n % 5 == 0:
result += n
처럼 하나의 if문에서 or로 묶으면 조건식 전체가 한 번만 참이 되기 때문에 15도 한 번만 더해진다.
result = 0
for n in range(1, 1000):
if n % 3 == 0 or n % 5 == 0:
result += n
print(result)
% → 배수 판별range() → 특정 범위의 숫자 생성or → 여러 조건 중 하나 이상 만족+= → 기존 값에 새로운 값 누적이번에는 게시물의 총 개수와 한 페이지에 표시할 게시물 수를 이용해 총 페이지 수를 계산하는 프로그램을 만들어 보자.
이렇게 게시물을 여러 페이지로 나누는 것을 페이징(Paging) 이라고 한다.
함수 이름은 다음과 같이 정한다.
get_total_page()
입력값은 두 가지이다.
m = 게시물의 총 개수
n = 한 페이지에 보여 줄 게시물 수
출력값은
총 페이지 수
이다.
예를 들어 한 페이지에 게시물을 10개씩 보여 준다고 해보자.
| 게시물 수 | 한 페이지 게시물 수 | 총 페이지 |
|---|---|---|
| 5 | 10 | 1 |
| 15 | 10 | 2 |
| 25 | 10 | 3 |
| 30 | 10 | 3 |
게시물 수를 페이지당 게시물 수로 나누고 1을 더할 수 있다.
def get_total_page(m, n):
return m // n + 1
여기서 //는 몫을 구하는 연산자이다.
예를 들어
15 // 10
결과는
1
이다.
따라서
1 + 1 = 2페이지
가 된다.
다음 코드를 실행해 보자.
def get_total_page(m, n):
return m // n + 1
print(get_total_page(5, 10))
print(get_total_page(15, 10))
print(get_total_page(25, 10))
print(get_total_page(30, 10))
결과
1
2
3
4
마지막 결과가 잘못되었다.
게시물이 30개이고 페이지당 10개라면
30 / 10 = 3
이므로 총 페이지는 3페이지여야 한다.
30 % 10
결과
0
나머지가 0이라는 것은 게시물이 페이지에 딱 맞게 나누어진다는 의미이다.
따라서 이 경우에는 +1을 하면 안 된다.
def get_total_page(m, n):
if m % n == 0:
return m // n
else:
return m // n + 1
테스트해 보자.
print(get_total_page(5, 10))
print(get_total_page(15, 10))
print(get_total_page(25, 10))
print(get_total_page(30, 10))
결과
1
2
3
3
정상적으로 출력된다.
나머지가 0
→ 게시물이 페이지에 딱 맞음
→ 몫만 사용
나머지가 있음
→ 마지막 페이지가 하나 더 필요함
→ 몫 + 1
즉,
if m % n == 0:
return m // n
else:
return m // n + 1
이 핵심이다.
이번에는 명령행에서 메모를 입력하여 파일에 저장하고, 저장된 내용을 다시 확인할 수 있는 간단한 메모장 프로그램을 만들어 보자.
필요한 기능은 두 가지이다.
다음처럼 프로그램을 실행한다고 생각해 보자.
python memo.py -a "Life is too short"
각각의 의미는 다음과 같다.
memo.py → 실행할 파이썬 파일
-a → 실행 옵션
"Life is too short" → 저장할 메모
프로그램 실행 시 전달한 값을 읽기 위해 sys.argv를 사용한다.
import sys
option = sys.argv[1]
memo = sys.argv[2]
print(option)
print(memo)
실행
python memo.py -a "Life is too short"
결과
-a
Life is too short
실제로는 다음과 같은 형태라고 생각하면 된다.
sys.argv
['memo.py', '-a', 'Life is too short']
각각의 위치는
sys.argv[0] → memo.py
sys.argv[1] → -a
sys.argv[2] → Life is too short
이다.
즉 프로그램 파일 이름도 sys.argv에 포함된다.
-a 옵션이 들어오면 메모를 파일에 저장하도록 만들어 보자.
import sys
option = sys.argv[1]
if option == '-a':
memo = sys.argv[2]
f = open('memo.txt', 'a')
f.write(memo)
f.write('\n')
f.close()
여기서
open('memo.txt', 'a')
의 a는 append를 의미한다.
즉 기존 내용을 지우지 않고 파일의 마지막 부분에 내용을 추가한다.
다음 코드가 없다면
f.write('\n')
여러 메모가 같은 줄에 계속 붙을 수 있다.
따라서 메모 하나를 저장한 뒤 \n을 추가하여 다음 메모가 새로운 줄에 저장되도록 한다.
python memo.py -a "Life is too short"
python memo.py -a "You need python"
memo.txt에는 다음과 같이 저장된다.
Life is too short
You need python
이번에는 -v 옵션을 사용하자.
python memo.py -v
코드는 다음과 같다.
import sys
option = sys.argv[1]
if option == '-a':
memo = sys.argv[2]
f = open('memo.txt', 'a')
f.write(memo)
f.write('\n')
f.close()
elif option == '-v':
f = open('memo.txt')
memo = f.read()
f.close()
print(memo)
실행
python memo.py -v
결과
Life is too short
You need python
python memo.py -a "메모 내용"
python memo.py -v
프로그램의 흐름은 다음과 같다.
프로그램 실행
↓
sys.argv로 옵션 확인
↓
┌─────────────┐
│ │
-a -v
│ │
메모 저장 파일 읽기
│ │
memo.txt 화면 출력
이번 예제에서는 지금까지 배운
sys.argvif / elifopen()write()read()등을 하나의 실제 프로그램으로 연결했다.
이번에는 컴퓨터가 만든 서로 다른 숫자 3개를 맞히는 숫자 야구 게임을 만들어 보자.
숫자는 1~9를 사용하며 0은 제외한다.
| 판정 | 의미 |
|---|---|
| 스트라이크 | 숫자와 위치가 모두 같음 |
| 볼 | 숫자는 있지만 위치가 다름 |
예를 들어 정답이
3 2 4
라고 해보자.
사용자가
3 2 5
를 입력했다면
3 → 숫자 O / 위치 O → 스트라이크
2 → 숫자 O / 위치 O → 스트라이크
5 → 숫자 X
따라서
2스트라이크 0볼
이다.
random 모듈의 sample()을 사용한다.
import random
nums = random.sample(range(1, 10), 3)
print(nums)
예를 들어 다음과 같이 생성될 수 있다.
[3, 7, 1]
random.sample(range(1, 10), 3)
은
1~9 중에서
중복되지 않는 숫자를
3개 선택
한다.
question = input("3자리 숫자를 입력하세요: ")
주의할 점은 input()으로 입력받은 데이터는 문자열이라는 것이다.
예를 들어
123
을 입력하면 실제로는
"123"
이 저장된다.
컴퓨터가 만든 숫자는 다음처럼 리스트에 들어 있다.
[3, 7, 1]
따라서 사용자가 입력한 "123"도 숫자 리스트로 바꾸어 주면 비교하기 편하다.
guess = list(map(int, question))
예를 들어
question = "123"
이면
map(int, question)
은 각 문자에 int()를 적용한다.
"1" → 1
"2" → 2
"3" → 3
그리고 list()로 변환하면
[1, 2, 3]
이 된다.
strike = 0
ball = 0
for i in range(3):
if guess[i] == nums[i]:
strike += 1
elif guess[i] in nums:
ball += 1
guess[i] == nums[i]
사용자가 입력한 숫자와 컴퓨터 숫자의 값과 위치가 모두 같은지 확인한다.
같다면
strike += 1
guess[i] in nums
현재 숫자가 정답 리스트 안에는 있지만 현재 위치가 다르다면 볼이다.
ball += 1
elif를 사용했기 때문에 이미 스트라이크가 된 숫자가 다시 볼로 계산되지 않는다.
한 번 입력하고 끝나는 것이 아니라 정답을 맞힐 때까지 반복해야 한다.
따라서
while True:
를 사용한다.
import random
nums = random.sample(range(1, 10), 3)
count = 0
while True:
question = input("3자리 숫자를 입력하세요: ")
guess = list(map(int, question))
count += 1
strike = 0
ball = 0
for i in range(3):
if guess[i] == nums[i]:
strike += 1
elif guess[i] in nums:
ball += 1
if strike == 3:
print(f"정답! {count}번 만에 맞혔습니다.")
break
print(f"{strike}스트라이크 {ball}볼")
if strike == 3:
print(f"정답! {count}번 만에 맞혔습니다.")
break
3개의 숫자가 모두 스트라이크라는 것은
숫자 O
위치 O
이므로 정답을 맞힌 것이다.
이때 break를 실행하여
while True:
반복문을 종료한다.
프로그램 시작
↓
1~9 중 서로 다른 숫자 3개 생성
↓
사용자 숫자 입력
↓
문자열 → 숫자 리스트 변환
↓
각 자리 비교
↓
스트라이크 / 볼 계산
↓
3스트라이크인가?
↙ ↘
No Yes
↓ ↓
결과 출력 정답 출력
↓ ↓
다시 입력 break
이번 프로그램에서는 여러 문법이 함께 사용된다.
random.sample() → 중복 없는 난수 생성
input() → 사용자 입력
map() → 각 요소에 함수 적용
list() → 리스트 변환
in → 값이 포함되어 있는지 확인
while True → 무한 반복
break → 반복 종료
이번에는 문자열의 알파벳을 일정한 칸만큼 이동시키는 시저 암호(Caesar Cipher) 를 만들어 보자.
예를 들어 알파벳을 3칸씩 이동시키면
A → D
B → E
C → F
와 같이 변환된다.
따라서
PYTHON
을 3칸 이동시키면
SBWKRQ
이 된다.
문자를 숫자로 바꾸려면 ord()를 사용한다.
ord('A')
결과
65
반대로 숫자를 문자로 바꾸려면 chr()를 사용한다.
chr(65)
결과
'A'
따라서
chr(ord('A') + 3)
은
'D'
가 된다.
과정을 살펴보면
'A'
↓ ord()
65
↓ + 3
68
↓ chr()
'D'
이다.
word = "PYTHON"
key = 3
result = ""
for char in word:
result += chr(ord(char) + key)
print(result)
결과
SBWKRQ
문자열도 for문을 이용하면 한 글자씩 가져올 수 있다.
for char in word:
word가 "PYTHON"이면
P
Y
T
H
O
N
순서로 하나씩 char에 들어간다.
다음 코드를 생각해 보자.
chr(ord('X') + 3)
우리가 원하는 결과는
X → Y → Z → A
즉 A이다.
하지만 문자 코드에 단순히 3을 더하면 알파벳 범위를 벗어날 수 있다.
따라서 Z 다음에는 다시 A로 돌아가는 처리가 필요하다.
알파벳은 총 26개이다.
따라서 다음처럼 숫자로 표현할 수 있다.
A → 0
B → 1
C → 2
...
X → 23
Y → 24
Z → 25
이것은 다음 코드로 만들 수 있다.
ord(char) - ord('A')
예를 들어
ord('C') - ord('A')
은
67 - 65
= 2
이다.
따라서
C → 2
가 된다.
X를 3칸 이동해 보자.
X = 23
여기에 3을 더하면
23 + 3 = 26
이 된다.
그리고
26 % 26
은
0
이다.
0은 다시 A를 의미한다.
즉
X → A
가 정상적으로 만들어진다.
Y도 마찬가지이다.
Y = 24
24 + 3 = 27
27 % 26 = 1
1 = B
따라서
Y → B
가 된다.
핵심 코드는 다음과 같다.
num = ord(char) - ord('A')
shifted = (num + key) % 26
result += chr(shifted + ord('A'))
과정을 정리하면
문자
↓
ord()
↓
0~25 숫자로 변환
↓
key만큼 이동
↓
% 26
↓
0~25 범위 유지
↓
다시 문자로 변환
word = "PYTHON"
key = 3
result = ""
for char in word:
num = ord(char) - ord('A')
shifted = (num + key) % 26
result += chr(shifted + ord('A'))
print(result)
실행 결과
SBWKRQ
이번에는 암호화 부분을 함수로 만들어 보자.
def caesar(word, key):
result = ""
for char in word:
num = ord(char) - ord('A')
shifted = (num + key) % 26
result += chr(shifted + ord('A'))
return result
사용자가 직접 값을 입력하도록 만들 수 있다.
word = input("문자열을 입력하세요: ")
key = int(input("밀 칸 수를 입력하세요: "))
print(f"암호화: {caesar(word, key)}")
def caesar(word, key):
result = ""
for char in word:
num = ord(char) - ord('A')
shifted = (num + key) % 26
result += chr(shifted + ord('A'))
return result
word = input("문자열을 입력하세요: ")
key = int(input("밀 칸 수를 입력하세요: "))
print(f"암호화: {caesar(word, key)}")
실행
문자열을 입력하세요: HAPPY
밀 칸 수를 입력하세요: 3
결과
암호화: KDSSB
시저 암호는 반대 방향으로 다시 이동하면 원래 문자열로 돌아온다.
예를 들어
HAPPY
를 3칸 이동하면
KDSSB
이다.
반대로 KDSSB를 -3칸 이동하면
HAPPY
가 된다.
실행
문자열을 입력하세요: KDSSB
밀 칸 수를 입력하세요: -3
결과
암호화: HAPPY
즉 같은 함수에서 key에 음수를 넣으면 반대 방향으로 이동시킬 수도 있다.
num = ord(char) - ord('A')
shifted = (num + key) % 26
result += chr(shifted + ord('A'))
각 함수와 연산자의 역할은 다음과 같다.
| 코드 | 설명 |
|---|---|
ord() | 문자 → 숫자 |
chr() | 숫자 → 문자 |
% 26 | A~Z 범위를 벗어나지 않도록 처리 |
for | 문자열의 문자를 하나씩 반복 |
result += | 변환된 문자 이어 붙이기 |
return | 암호화 결과 반환 |
이번 장에서는 앞에서 배운 파이썬 문법을 이용해 실제 프로그램을 만들어 보았다.
| 실습 | 주요 개념 |
|---|---|
| 3과 5의 배수 더하기 | for, range, %, or |
| 게시판 페이징 | 함수, //, %, if |
| 간단한 메모장 | sys.argv, 파일 입출력 |
| 숫자 야구 | random, input, map, while, break |
| 시저 암호 | 문자열 반복, ord, chr, % |
처음에는 각각 다른 프로그램처럼 보이지만 기본 구조는 비슷하다.
문제 확인
↓
입력값 결정
↓
출력값 결정
↓
필요한 기능 나누기
↓
작은 기능부터 구현
↓
조건문 / 반복문 / 함수 적용
↓
테스트
↓
예외 상황 확인
↓
프로그램 완성
파이썬 문법을 외우는 것도 중요하지만, 실제 프로그램을 만들 때는 문제를 작은 기능으로 나누고 지금까지 배운 문법을 조합하는 과정이 더 중요하다.