A-2: 연산과 문자열 — 계산하고, 글자를 자르고 합치기
목차 48
안녕하세요! 여러분의 파이썬 가이드, 홍순구 튜터입니다.
지난 시간엔 변수에 값을 담고, 값의 종류(int·float·str·bool)를 type() 으로 확인하고, input() 과 print() 로 사용자와 묻고 답했죠.
빨간 에러 메시지를 읽는 법까지 익혔고요.
오늘은 그 값들을 가지고 본격적으로 계산을 하고, 글자(문자열)를 자르고 다듬고 합치는 도구들을 배웁니다.
지난 시간 REPL에서 2 + 2 를 살짝 맛봤는데, 오늘은 그 계산을 제대로 파헤쳐요.
그리고 인사말에 썼던 f-string의 정체도 드디어 밝혀집니다.
오늘 우리는 이런 길을 따라갑니다.
오늘의 여정 — A-2 (총 8 단계)
1. 산술 연산자 — 계산기가 된 파이썬
2. 비교 연산자 — 참이야 거짓이야
3. 논리 연산자 — and·or·not 으로 조건 묶기
4. 형 변환 — 글자를 숫자로, 숫자를 글자로
5. f-string — 정체를 제대로 파헤치기
6. 인덱싱·슬라이싱 — 글자 자르기
7. 문자열 메서드 — 글자 다듬기
8. split·join — 해시태그 분리하고 다시 합치기
앞쪽 1~4단계는 숫자 계산, 뒤쪽 5~8단계는 글자 다루기예요. 한꺼번에 외우려 하지 말고, 직접 실행해 보면서 하나씩 익혀가요.
💡 오늘 수업의 핵심 — "숫자로 계산하고, 글자(문자열)를 자르고 다듬고 합치기"
🎯 학습 목표
- 산술·비교·논리 연산자로 계산하고 참/거짓을 판단할 수 있다
int()·str()·float()로 값의 종류를 바꾸고, 왜input()값을 변환해야 하는지 안다- f-string으로 값을 끼워 넣고, 천단위·소수 자릿수 같은 형식을 지정할 수 있다
- 인덱싱·슬라이싱과 문자열 메서드(
upper/strip/replace/split/join)로 글자를 자유롭게 다룰 수 있다
Step 1: "계산기가 된 파이썬 — 산술 연산자"
지난 시간 REPL에서 2 + 2 를 입력했더니 파이썬이 곧장 4 라고 답했죠.
파이썬은 아주 훌륭한 계산기예요.
오늘은 더하기를 넘어 다양한 계산 기호를 만나봅니다.
이렇게 계산에 쓰는 기호들을 산술 연산자(arithmetic operator)라고 불러요.
REPL로 계산기 두드려보기
REPL을 다시 켜고(uv run python) 한 줄씩 입력해 봅시다.
>>> 7 + 3
10
>>> 7 - 3
4
>>> 7 * 3
21
>>> 7 / 3
2.3333333333333335
더하기(+)·빼기(-)·곱하기(*)는 익숙하죠.
나누기는 별표가 아니라 빗금(/)을 씁니다.
그런데 결과를 보면 2.333... 처럼 소수가 나왔어요.
파이썬에서 / 나눗셈은 항상 소수(float) 로 답한다는 걸 기억해 두세요.
여기까지는 사칙연산이고, 파이썬에는 자주 쓰는 계산 기호가 세 개 더 있어요.
>>> 7 // 3
2
>>> 7 % 3
1
>>> 2 ** 10
1024
//는 나눗셈의 몫만 가져와요. 7을 3으로 나누면 3이 두 번 들어가고 1이 남으니, 몫은2.%는 나눗셈의 나머지예요. 7을 3으로 나눈 나머지는1.**는 거듭제곱이에요.2 ** 10은 2를 열 번 곱한1024.
// 와 % 는 한 쌍으로 떠올리면 쉬워요.
7 // 3 → 2 (몫: 3이 두 번 들어감)
7 % 3 → 1 (나머지: 그러고 1이 남음)
%(나머지)는 "짝수냐 홀수냐"를 가릴 때 두고두고 쓰니 눈에 익혀두면 좋아요.
연산자 일곱 개를 한 표로 정리하면 이렇습니다.
| 연산자 | 뜻 | 예 | 결과 |
|---|---|---|---|
+ |
더하기 | 7 + 3 |
10 |
- |
빼기 | 7 - 3 |
4 |
* |
곱하기 | 7 * 3 |
21 |
/ |
나누기(소수) | 7 / 3 |
2.333... |
// |
몫 | 7 // 3 |
2 |
% |
나머지 | 7 % 3 |
1 |
** |
거듭제곱 | 2 ** 10 |
1024 |
파일로 정리해 인스타에 적용하기
REPL은 실험용이라 끄면 입력한 게 사라지죠. 이번엔 파일에 담아 게시물 좋아요 수를 계산해 봅시다.
# src/a_intro/a2_operators.py
# 게시물 두 개의 좋아요 수
likes_post1 = 120
likes_post2 = 86
total_likes = likes_post1 + likes_post2
print(f"좋아요 합계: {total_likes}")
# 나누기 두 가지 — / 는 항상 소수(float), // 는 몫만(정수)
post_count = 3
average_float = total_likes / post_count
average_floor = total_likes // post_count
print(f"게시물당 평균 좋아요(/): {average_float}")
print(f"게시물당 평균 좋아요(//, 몫만): {average_floor}")
여기 f"..." 는 지난 시간에 맛본 그 f-string이에요.
정체는 Step 5에서 제대로 파헤치니, 지금은 "중괄호에 값을 끼워 출력한다" 정도로만 보고 넘어가요.
▶ 버튼으로 실행하거나 터미널에서 uv run python src/a_intro/a2_operators.py 를 입력하면 이렇게 나옵니다.
좋아요 합계: 206
게시물당 평균 좋아요(/): 68.66666666666667
게시물당 평균 좋아요(//, 몫만): 68
같은 나눗셈인데 / 는 68.66... 이라는 소수를, // 는 68 이라는 깔끔한 정수를 돌려줬죠.
"몇 개씩 나눠 가질 수 있나" 처럼 정수가 필요할 땐 // 가 딱 맞아요.
💡 한 줄 정리
산술 연산자는 계산 기호예요. / 는 항상 소수, // 는 몫, % 는 나머지, ** 는 거듭제곱을 돌려줍니다.
🙋 학생 질문 — "그냥 나누기 / 하나면 충분할 것 같은데, 왜 // 까지 있어요?"
좋은 질문이에요!
/ 는 정확한 값을 알려주려고 늘 소수로 답해요. 7 / 2 는 3.5 처럼요.
그런데 "사탕 7개를 2명이 나눠 가지면 한 사람당 몇 개?" 같은 상황에서는 3.5개가 어색하죠.
이럴 때 // 가 "한 사람당 3개씩, 1개 남음" 의 그 3 을 깔끔하게 알려줘요.
그리고 남는 1 은 % 가 알려주고요.
정리하면, 소수까지 정확히 알고 싶으면 /, 딱 떨어지는 몫만 필요하면 // 라고 기억하면 됩니다.
Step 2: "참이야 거짓이야 — 비교 연산자"
지난 시간 '생각해볼 주제'에서 이런 질문을 던졌어요. "= 는 왜 '같다'가 아니라 '담는다'일까?"
오늘 그 답의 반쪽이 나옵니다.
값을 담을 땐 등호 하나(=), 두 값이 같은지 비교할 땐 등호 두 개(==)를 써요.
이렇게 두 값을 견주어 보는 기호를 비교 연산자(comparison operator)라고 부릅니다.
= 와 == 는 완전히 다른 기호
REPL에서 차이를 확인해 봅시다.
>>> likes = 120
>>> likes == 120
True
>>> likes == 200
False
likes = 120은 "likes 이름표에 120을 담아라" 라는 대입이에요 (지난 시간에 배웠죠).likes == 120은 "likes 가 120과 같니?" 라고 묻는 거예요. 같으니True.likes == 200은 200과 같냐고 묻는 거고, 다르니False.
비교의 답은 늘 True(참) 아니면 False(거짓), 둘 중 하나예요.
지난 시간에 배운 bool 자료형 기억나죠? 비교 연산자는 바로 그 bool 값을 만들어내는 도구랍니다.
여섯 가지 비교 연산자
파일로 두 게시물의 좋아요 수를 견주어 봅시다.
# src/a_intro/a2_operators.py
# = 는 "대입"(값을 담기), == 는 "비교"(같은지 묻기)
my_likes = 120
your_likes = 86
print(f"내 좋아요와 네 좋아요가 같은가(==): {my_likes == your_likes}")
print(f"내 좋아요와 네 좋아요가 다른가(!=): {my_likes != your_likes}")
print(f"내 좋아요가 더 큰가(>): {my_likes > your_likes}")
print(f"내 좋아요가 더 작은가(<): {my_likes < your_likes}")
print(f"내 좋아요가 120 이상인가(>=): {my_likes >= 120}")
print(f"네 좋아요가 100 이하인가(<=): {your_likes <= 100}")
실행하면 이렇게 나와요.
내 좋아요와 네 좋아요가 같은가(==): False
내 좋아요와 네 좋아요가 다른가(!=): True
내 좋아요가 더 큰가(>): True
내 좋아요가 더 작은가(<): False
내 좋아요가 120 이상인가(>=): True
네 좋아요가 100 이하인가(<=): True
여섯 개를 표로 정리하면 이렇습니다.
| 연산자 | 뜻 | 예 | 결과 |
|---|---|---|---|
== |
같다 | 120 == 86 |
False |
!= |
다르다 | 120 != 86 |
True |
> |
크다 | 120 > 86 |
True |
< |
작다 | 120 < 86 |
False |
>= |
크거나 같다(이상) | 120 >= 120 |
True |
<= |
작거나 같다(이하) | 86 <= 100 |
True |
💡 한 줄 정리
비교 연산자(== != > < >= <=)는 두 값을 견주어 True/False 를 돌려줘요. 값을 담을 땐 =, 같은지 물을 땐 == 입니다.
🙋 학생 질문 — "= 랑 == 를 자꾸 헷갈려요. 실수하면 어떻게 되나요?"
아주 흔한 실수라 걱정 마세요!
둘을 바꿔 쓰면 의미가 완전히 달라져요.
likes = 120 은 "120을 담아라" 라서 likes 의 값이 바뀌고, likes == 120 은 "같니?" 라고 묻기만 할 뿐 값은 그대로예요.
헷갈릴 땐 "담을 땐 하나, 물을 땐 둘" 이라고 소리 내어 외워보세요. 손가락으로 등호 개수를 세는 습관도 의외로 도움이 된답니다.
Step 3: "and·or·not 으로 조건 묶기 — 논리 연산자"
비교 하나로는 "좋아요가 100 이상인가?" 같은 단순한 질문만 할 수 있어요.
그런데 실제로는 "좋아요도 많고 댓글도 많은가?" 처럼 조건을 여러 개 묶고 싶을 때가 많죠.
이때 쓰는 게 논리 연산자(logical operator) and·or·not 입니다.
and·or·not 으로 인기 게시물 판단하기
# src/a_intro/a2_operators.py
# 인기 게시물 기준: 좋아요 100 이상 그리고(and) 댓글 10 이상
likes = 120
comments = 14
is_many_likes = likes >= 100
is_many_comments = comments >= 10
is_popular = is_many_likes and is_many_comments
print(f"좋아요 100 이상인가: {is_many_likes}")
print(f"댓글 10 이상인가: {is_many_comments}")
print(f"인기 게시물인가(and): {is_popular}")
# 둘 중 하나만 만족해도 추천: 좋아요 1000 이상 또는(or) 댓글 100 이상
is_viral = likes >= 1000 or comments >= 100
print(f"화제의 게시물인가(or): {is_viral}")
# not 으로 결과를 뒤집기 — 인기 게시물이 아닌가?
is_not_popular = not is_popular
print(f"인기 게시물이 아닌가(not): {is_not_popular}")
실행 결과예요.
좋아요 100 이상인가: True
댓글 10 이상인가: True
인기 게시물인가(and): True
화제의 게시물인가(or): False
인기 게시물이 아닌가(not): False
세 연산자의 성격을 한눈에 보면 이래요.
and는 양쪽이 둘 다 참이어야 참. 좋아요도 많고(True) 댓글도 많으니(True) 인기 게시물은True.or는 하나만 참이어도 참. 좋아요 1000 이상도, 댓글 100 이상도 아니니 화제글은False.not은 참·거짓을 뒤집어요. 인기 게시물이True니, "아닌가?"는False.
and 와 or 의 모든 경우를 표로 그리면 이렇습니다.
A B A and B A or B
───────────────────────────────────────
True True True True
True False False True
False True False True
False False False False
오늘 만든 이 True/False 는 그냥 출력하고 끝나는 게 아니에요.
다음 시간엔 이 참·거짓을 가지고 '갈림길'을 만듭니다.
"좋아요가 100 이상이면(if) 인기글로 표시하고, 아니면 일반글로 두라" 처럼요.
오늘 만든 판단(is_popular)이 다음 시간 if 문의 재료가 되는 거예요.
💡 한 줄 정리
논리 연산자는 and(둘 다 참), or(하나만 참), not(뒤집기). 비교로 만든 참·거짓을 묶어 더 똑똑한 판단을 만들어요.
🙋 학생 질문 — "비교 결과를 꼭 is_popular 같은 변수에 담아야 하나요?"
꼭은 아니에요!
likes >= 100 and comments >= 10 처럼 묶어서 바로 써도 잘 동작해요.
다만 변수에 담으면 이름(is_popular)이 그 판단의 뜻을 설명해줘서 코드가 훨씬 읽기 좋아져요.
참·거짓을 담는 변수는 보통 is_ 로 시작해서 "이건 참거짓 판단이구나" 를 알려주는 습관이 있답니다.
Step 4: "글자를 숫자로, 숫자를 글자로 — 형 변환"
지난 시간 input() 으로 사용자 입력을 받았죠.
그런데 input() 에는 한 가지 함정이 있어요.
숫자를 입력해도 항상 글자(str)로 준다는 거예요.
input 이 준 값은 늘 글자
# src/a_intro/a2_convert.py
age_text = "25" # input("나이: ") 로 받으면 이렇게 글자로 들어와요
print(f"age_text 의 종류: {type(age_text)}") # <class 'str'>
# 글자끼리 + 는 '이어붙이기' 라서 계산이 안 돼요
two_texts = "25" + "10"
print(f'"25" + "10" (글자 이어붙이기): {two_texts}') # 2510
age_text 의 종류: <class 'str'>
"25" + "10" (글자 이어붙이기): 2510
"25" + "10" 이 35가 아니라 2510 이 나왔어요!
글자끼리의 + 는 계산이 아니라 '이어붙이기' 라서 그래요.
int() 로 글자를 숫자로, str()·float() 로 바꾸기
age = int(age_text)
next_age = age + 1
print(f"int 로 바꾼 뒤 +1: {next_age}") # 26
likes = 206
likes_text = str(likes)
print("str 로 바꾼 좋아요: " + likes_text + "개") # 글자끼리 이어붙이기 OK
height_text = "175.5"
height = float(height_text)
print(f"float 로 바꾼 키: {height} (종류: {type(height)})")
int 로 바꾼 뒤 +1: 26
str 로 바꾼 좋아요: 206개
float 로 바꾼 키: 175.5 (종류: <class 'float'>)
int() 는 글자를 정수로, float() 는 소수로, str() 은 숫자를 글자로 바꿔줘요.
글자 "25" 를 int() 로 바꾸니 진짜 숫자가 되어 +1 계산이 됐죠.
에러는 친구 — 글자와 숫자를 그냥 더하면?
지난 시간에 "에러는 친구" 라고 했죠? 일부러 글자와 숫자를 그냥 더해보고, 파이썬이 뭐라고 하는지 들어봅시다.
# src/a_intro/a2_convert_error.py
likes_text = "10" # input() 으로 받으면 글자
bonus = 5 # 숫자
total = likes_text + bonus # 여기서 TypeError — 글자 + 숫자는 안 돼요
print(total)
실행하면 빨간 메시지가 떠요.
Traceback (most recent call last):
File ".../a2_convert_error.py", line 7, in <module>
total = likes_text + bonus # 여기서 TypeError — 글자 + 숫자는 안 돼요
~~~~~~~~~~~^~~~~~~
TypeError: can only concatenate str (not "int") to str
TypeError 는 "종류(Type)가 안 맞아요" 라는 뜻이에요.
메시지 "can only concatenate str (not "int") to str" 는 "글자(str)에는 글자만 이어붙일 수 있고, 숫자(int)는 안 돼" 라는 거고요.
화살표 ^ 가 문제가 난 자리를 정확히 가리켜 주죠.
해결책은 간단해요. bonus 를 str(bonus) 로 글자로 바꾸거나, likes_text 를 int(likes_text) 로 숫자로 바꾸면 됩니다.
💡 한 줄 정리
int()·str()·float() 로 값의 종류를 바꿔요. 특히 input() 이 준 글자는 계산하기 전에 int()/float() 로 바꿔야 합니다.
🙋 학생 질문 — "input() 은 왜 숫자도 글자로 주나요? 불편한데요."
사용자가 키보드로 뭘 칠지 파이썬은 미리 알 수 없어요.
25 를 칠 수도, 스물다섯 을 칠 수도, 안녕 을 칠 수도 있죠.
그래서 input() 은 가장 안전하게 "일단 글자로" 받아둬요.
숫자가 필요하면 우리가 직접 int() 로 바꾸겠다고 알려주는 거예요.
덕분에 "이 값을 숫자로 쓸 거야" 라는 의도가 코드에 분명히 드러나는 장점도 있답니다.
Step 5: "f-string 제대로 파헤치기"
지난 시간 인사말에 f"안녕하세요 @{name}" 같은 걸 썼죠.
그때 "맨 앞의 f 는 뭐지?" 싶었을 거예요.
드디어 그 정체를 밝힙니다.
f 와 {} 의 정체
f-string은 따옴표 앞에 f 를 붙인 글자예요.
그 안에서 중괄호 {} 로 감싼 부분은 '값으로 바뀌어' 글자에 끼워집니다.
# src/a_intro/a2_fstring.py
username = "minji"
likes = 1240
comments = 86
# 1) f 와 {} — 중괄호 안의 변수가 값으로 바뀐다
print(f"@{username} 님의 게시물")
@minji 님의 게시물
{username} 자리에 변수 값 minji 가 쏙 들어갔죠.
중괄호 안에는 '식'도 넣을 수 있다
# 2) 중괄호 안에는 '식'도 넣을 수 있다 (계산 결과가 들어감)
print(f"좋아요 + 댓글 = {likes + comments}")
print(f"2의 10제곱 = {2 ** 10}")
좋아요 + 댓글 = 1326
2의 10제곱 = 1024
Step 1에서 배운 연산자를 중괄호 안에서 바로 계산할 수 있어요.
{likes + comments} 가 1326 으로 바뀌어 들어갔죠.
보기 좋게 — 형식 지정
# 3) 포맷 지정 — 큰 수에 천단위 콤마(:,)
big_likes = 1234567
print(f"누적 좋아요: {big_likes:,}") # 1,234,567
# 4) 포맷 지정 — 소수점 자리수(:.2f)
rating = 4.5678
print(f"평점: {rating:.2f}") # 4.57
누적 좋아요: 1,234,567
평점: 4.57
중괄호 안에서 : 뒤에 형식을 적어요.
{값:,} 는 천 단위마다 콤마를 찍어주고, {값:.2f} 는 소수 둘째 자리까지 반올림해 보여줘요.
큰 숫자나 평점을 사람이 읽기 좋게 다듬을 때 아주 유용하답니다.
여러 줄도 한 번에 — 멀티라인 f-string
# 5) 멀티라인 f-string — 따옴표 세 개(""")로 여러 줄을 한 번에
card = f"""[게시물 카드]
작성자 : @{username}
좋아요 : {big_likes:,}
평점 : {rating:.2f}"""
print(card)
[게시물 카드]
작성자 : @minji
좋아요 : 1,234,567
평점 : 4.57
따옴표 세 개(""")로 감싸면 여러 줄짜리 글자를 한 번에 만들 수 있어요.
줄을 나눈 모양 그대로 출력되죠.
💡 한 줄 정리
f-string은 따옴표 앞에 f 를 붙이고 {} 안에 변수나 식을 넣어 글자에 끼우는 도구예요. :, :.2f 로 형식을 지정하고, """ 로 여러 줄도 만들어요.
🙋 학생 질문 — "f 를 안 붙이면 어떻게 돼요?"
f 를 빠뜨리면 중괄호가 값으로 안 바뀌고 글자 그대로 나와요.
print("@{username}") 는 화면에 @{username} 이라고 중괄호째 찍혀버려요.
"왜 변수 값이 안 나오지?" 싶을 때 십중팔구 f 를 안 붙인 경우랍니다.
중괄호로 값을 끼우려거든 꼭 f 를 먼저, 라고 기억해요.
Step 6: "글자 자르기 — 인덱싱과 슬라이싱"
지금까지 글자를 통째로 다뤘는데, 이번엔 글자 속 한 글자, 또는 일부분만 꺼내봅니다.
글자마다 번호가 있다
문자열의 각 글자에는 0 부터 시작하는 번호(인덱스)가 붙어 있어요.
"instagram"
i n s t a g r a m
0 1 2 3 4 5 6 7 8 ← 앞에서부터 (0)
-9 -8 -7 -6 -5 -4 -3 -2 -1 ← 뒤에서부터 (-1)
[0:4] → i n s t → "inst" (0,1,2,3 — 4번 직전까지!)
# src/a_intro/a2_strings.py
username = "instagram"
print(f"첫 글자 [0]: {username[0]}") # i
print(f"마지막 글자 [-1]: {username[-1]}") # m
print(f"앞 4글자 [0:4]: {username[0:4]}") # inst
print(f"글자 수 len(): {len(username)}") # 9
첫 글자 [0]: i
마지막 글자 [-1]: m
앞 4글자 [0:4]: inst
글자 수 len(): 9
[0]은 첫 글자예요. 번호가 1이 아니라 0부터 시작한다는 게 핵심이에요.[-1]처럼 음수를 쓰면 뒤에서부터 세요. 마지막 글자는[-1].[0:4]처럼 콜론으로 범위를 주면 일부분을 잘라내요. 이걸 슬라이싱이라고 해요.len()은 글자 수를 알려줘요. "instagram" 은 9글자.
💡 한 줄 정리
인덱싱 [n] 은 n번째 글자 하나(0부터, 음수는 뒤에서). 슬라이싱 [start:end] 는 start부터 end '직전'까지 잘라내요.
🙋 학생 질문 — "[0:4] 인데 왜 4번 글자(a)는 안 나오고 inst 까지만 나와요?"
슬라이싱의 끝 번호는 '거기 직전까지' 라는 약속이에요.
[0:4] 는 0, 1, 2, 3번 글자(i, n, s, t)까지만 가져오고 4번은 빼요.
처음엔 헷갈리지만, 이렇게 약속한 덕에 [0:4] 가 정확히 4글자라는 게 딱 떨어져요 (4 - 0 = 4).
"끝 번호는 직전까지" 라고 외워두면 편해요.
Step 7: "글자 다듬기 — 문자열 메서드"
문자열에는 스스로를 다듬는 편리한 기능들이 딸려 있어요.
이렇게 어떤 값에 점(.)을 찍어 부르는 기능을 메서드(method)라고 해요.
(지난 시간 type() 처럼 그냥 부르는 건 함수, 점을 찍어 부르는 건 메서드인데, 이름 구분은 나중에 자세히 다뤄요. 지금은 "점 뒤에 붙는 기능" 정도면 충분해요.)
upper·lower·strip·replace
# src/a_intro/a2_strings.py
caption = " Hello Instagram! "
print(f"대문자: {caption.upper()}")
print(f"소문자: {caption.lower()}")
print(f"공백 제거: '{caption.strip()}'")
print(f"바꾸기: {caption.replace('Instagram', '인스타그램')}")
print(f"원본은 그대로: '{caption}'")
대문자: HELLO INSTAGRAM!
소문자: hello instagram!
공백 제거: 'Hello Instagram!'
바꾸기: Hello 인스타그램!
원본은 그대로: ' Hello Instagram! '
upper()/lower()는 모두 대문자 / 모두 소문자로 바꿔요.strip()은 앞뒤 공백을 없애요. 작은따옴표로 감싸 출력하니 공백이 사라진 게 보이죠.replace(찾을글자, 바꿀글자)는 특정 글자를 다른 글자로 바꿔요.
중요 — 문자열은 '불변', 원본은 그대로
맨 아래 출력을 보세요.
upper·strip·replace 를 다 했는데도 caption 원본은 공백도 대소문자도 그대로예요!
문자열 메서드는 원본을 바꾸지 않고 '다듬은 새 글자' 를 만들어 돌려주거든요.
이걸 문자열은 불변(immutable)이라고 해요.
바뀐 결과를 쓰려면 clean = caption.strip() 처럼 새 변수에 담아야 합니다.
💡 한 줄 정리
문자열 메서드는 점(.)으로 부르는 기능이에요. upper/lower(대소문자), strip(공백 제거), replace(바꾸기). 단, 원본은 그대로 두고 새 글자를 돌려줘요(불변).
🙋 학생 질문 — "strip() 했는데 왜 원본 caption 은 공백이 그대로예요?"
문자열은 '불변' 이라 한 번 만들어지면 못 바꿔요.
그래서 메서드는 원본을 건드리는 대신 '다듬은 새 글자' 를 만들어 돌려줘요.
caption.strip() 의 결과를 쓰고 싶으면 clean = caption.strip() 처럼 새 이름표에 담으면 됩니다.
"메서드는 원본을 바꾸지 않는다" 는 처음엔 의외지만, 원본이 안 변해서 안전하다는 큰 장점이 있어요.
Step 8: "해시태그 분리하고 다시 합치기 — split·join"
지난 시간 마무리에서 "글자를 자르고(split) 다듬고 합쳐서 캡션과 해시태그를 가지고 놀자" 고 예고했죠.
이제 그 split 과 join 을 만나봅니다. 오늘의 마지막 도구예요.
split — 글자를 여러 조각으로 자르기
# src/a_intro/a2_hashtags.py
caption = "오늘 카페 다녀왔어요 #일상 #카페 #감성"
parts = caption.split()
print(f"split 결과: {parts}")
print(f"조각 개수: {len(parts)}")
print(f"첫 조각 [0]: {parts[0]}") # 오늘 (Step 6 의 [0] 그대로!)
split 결과: ['오늘', '카페', '다녀왔어요', '#일상', '#카페', '#감성']
조각 개수: 6
첫 조각 [0]: 오늘
split() 은 공백을 기준으로 글자를 토막내요.
결과가 대괄호 [] 와 따옴표로 묶여 나오는데, 이건 여러 조각을 담은 '리스트' 라는 그릇이에요.
리스트는 다음 카테고리에서 제대로 배우니, 지금은 "조각들이 순서대로 담긴다" 정도면 충분해요.
조각 하나를 꺼낼 땐 Step 6에서 배운 [0] 을 그대로 쓰면 되고요.
구분자를 직접 정하기
profile_line = "민지,서울,사진작가"
fields = profile_line.split(",")
print(f"콤마로 자른 결과: {fields}")
콤마로 자른 결과: ['민지', '서울', '사진작가']
split(",") 처럼 기준이 될 글자를 넣어주면 공백 대신 그 글자로 잘라요.
join — 조각을 다시 하나로 합치기
rejoined = " / ".join(parts)
print(f"join 으로 합치기: {rejoined}")
join 으로 합치기: 오늘 / 카페 / 다녀왔어요 / #일상 / #카페 / #감성
join 은 split 의 반대예요.
"사이에 끼울 글자".join(조각들) 형태로, 조각 사이에 글자를 끼워 하나로 잇습니다.
" / " 로 합치니 슬래시로 이어졌죠.
split + join 콤보 — 들쭉날쭉한 띄어쓰기 정리
messy = " #Seoul #Cafe #Daily "
clean = " ".join(messy.split())
print(f"정리 전: '{messy}'")
print(f"정리 후: '{clean}'") # #Seoul #Cafe #Daily
정리 전: ' #Seoul #Cafe #Daily '
정리 후: '#Seoul #Cafe #Daily'
split() 은 공백이 여러 칸이어도 알아서 깔끔하게 토막내요.
거기에 join 으로 한 칸씩 다시 붙이면, 지저분한 띄어쓰기가 한 번에 정리됩니다.
실무에서도 자주 쓰는 단골 기술이에요.
💡 한 줄 정리
split 은 글자를 조각으로 자르고(기본은 공백, 구분자 지정 가능), join 은 조각을 사이 글자로 이어 합쳐요. 둘을 합치면 들쭉날쭉한 글자도 깔끔하게 정리돼요.
🙋 학생 질문 — "split 결과에 붙은 대괄호 [] 랑 따옴표는 뭐예요?"
그건 여러 조각을 순서대로 담는 '리스트(list)' 라는 그릇이에요.
['오늘', '카페', ...] 처럼요.
글자 하나가 아니라 여러 개가 모여 있어서 대괄호로 묶여 나와요.
리스트는 다음 카테고리에서 본격적으로 배우니, 지금은 "split 이 조각들을 리스트에 담아 준다" 와 "[0] 으로 첫 조각을 꺼낸다" 두 가지만 알면 충분해요.
마무리
오늘 숫자 계산과 글자 다루기, 정말 많은 도구를 만났어요. 고생 많으셨습니다.
오늘 배운 핵심 세 가지
💡 하나: 산술(+ - * / // % **)·비교(== != > < >= <=)·논리(and or not) 연산자로 계산하고 참·거짓을 판단했어요.
💡 둘: input() 이 준 글자는 int()/float() 로 바꿔야 계산할 수 있고, f-string으로 값을 끼우고 형식까지 지정했어요.
💡 셋: 인덱싱·슬라이싱으로 글자를 잘라 꺼내고, 메서드(upper/strip/replace)와 split·join 으로 글자를 다듬고 합쳤어요.
다음 시간 예고
다음 시간에는 "흐름 제어" 를 배웁니다.
오늘 비교·논리 연산자로 만든 True/False 기억하죠?
다음 시간엔 이 참·거짓을 가지고 '갈림길' 을 만들어요.
"좋아요가 100 이상이면(if) 인기글, 아니면(else) 일반글" 처럼, 조건에 따라 프로그램이 다른 길로 가게 하는 거예요.
같은 일을 여러 번 되풀이하는 for·while 도 만나고요.
드디어 프로그램이 스스로 판단하고 반복하기 시작합니다. 기대해 주세요!
과제
오늘 배운 연산자·형 변환·f-string·문자열 도구를 직접 손으로 익혀봅시다. 눈으로 본 것과 직접 짜보는 것은 완전히 다르답니다.
[기초] 좋아요 통계 계산기
3일치 좋아요 수를 변수에 담고, 산술 연산자로 간단한 통계를 내보세요.
- 월·화·수 좋아요 수를 각각 변수에 담습니다
- 합계(
+), 하루 평균(몫//), 평균 내고 남는 수(%)를 계산합니다 - f-string으로 "3일 합계: N" 처럼 라벨을 붙여 출력합니다
[응용] 프로필 카드 만들기
input() 으로 정보를 받아 형식을 갖춘 카드를 출력하는 프로그램을 만들어 보세요.
- 사용자 이름, 총 좋아요 수, 평점을
input()으로 받습니다 (좋아요는 정수, 평점은 소수) - 받은 값을 알맞은 종류로 변환합니다 (
int/float) - 멀티라인 f-string으로 카드를 출력합니다. 좋아요엔 천단위 콤마(
:,), 평점은 소수 둘째 자리(:.2f)
실행 예시:
사용자 이름: 민지
총 좋아요 수: 12345
평점: 4.789
[프로필 카드]
이름 : @민지
좋아요 : 12,345
평점 : 4.79
[심화] 해시태그 정리기
지저분하게 입력된 캡션을 깔끔하게 다듬는 프로그램을 만들어 보세요.
- 앞뒤 공백과 느낌표(
!)가 섞인 캡션 문자열을 하나 준비합니다 strip()으로 앞뒤 공백을 없애고,replace()로 느낌표를 지웁니다split()과join()으로 여러 칸 띄어쓰기를 한 칸으로 정리합니다lower()로 영문을 소문자로 통일해 출력합니다
생각해볼 주제
정답이 하나로 정해진 질문이 아니에요. 혼자 곰곰이 생각해 보거나, 함께 공부하는 동료와 이야기 나눠 보면 좋습니다.
1. 나누기가 / 와 // 두 개로 나뉜 이유는?
다른 계산기에선 나누기 버튼이 하나뿐인데, 파이썬은 /(소수)와 //(몫)를 따로 뒀어요.
왜 굳이 둘로 나눴을까요?
프로그램에서 "딱 떨어지는 정수" 가 필요한 상황은 어떤 게 있을까요?
2. 문자열이 '불변'인 게 왜 좋을까?
문자열 메서드는 원본을 바꾸지 않고 새 글자를 돌려줬어요. "그냥 원본을 바로 바꾸면 편할 텐데" 싶을 수도 있는데, 원본이 안 바뀌면 어떤 점이 안전할까요? 반대로 불편한 점은 없을까요?
3. input() 이 모든 값을 글자로 주는 건 친절일까 불편함일까?
input() 은 숫자를 입력해도 글자로 줘서, 계산하려면 매번 int() 로 바꿔야 했어요.
만약 input() 이 "숫자처럼 생겼으면 알아서 숫자로" 줬다면 더 편했을까요?
아니면 지금처럼 "항상 글자, 필요하면 직접 변환" 이 더 안전할까요? 왜 그렇게 생각하나요?
✅ 예시 답안정답 보기
과제 예시답안
🎯 [기초] 좋아요 통계 계산기 — 예시답안
채점 포인트
| 항목 | 배점 | 기준 |
|---|---|---|
| 좋아요 수를 변수에 담고 합계 계산 | 30% | + 로 세 값을 더했는가 |
몫(//)과 나머지(%)를 구분해 사용 |
40% | 평균은 //, 남는 수는 % 인가 |
| f-string으로 라벨 붙여 출력 | 30% | {변수} 로 값을 끼워 넣었는가 |
풀이 예시
# src/a_intro/a2_likes_stats_solution.py
# [기초] 좋아요 통계 계산기
likes_mon = 120
likes_tue = 95
likes_wed = 131
total = likes_mon + likes_tue + likes_wed
day_count = 3
average = total // day_count # 하루 평균(몫만)
remainder = total % day_count # 평균 내고 남는 좋아요
print(f"3일 합계: {total}")
print(f"하루 평균(몫): {average}")
print(f"평균 내고 남는 좋아요: {remainder}")
print(f"최고 기록의 제곱: {likes_wed ** 2}")
실행하면 이렇게 나옵니다.
3일 합계: 346
하루 평균(몫): 115
평균 내고 남는 좋아요: 1
최고 기록의 제곱: 17161
💡 튜터의 한마디
평균을 낼 때 / 를 쓰면 115.33... 같은 소수가 나와요.
"하루 몇 개씩" 처럼 딱 떨어지는 수가 필요할 땐 몫 // 가 깔끔하죠.
그리고 나눠 떨어지지 않고 남는 양은 나머지 % 가 알려줘요. 이 둘은 한 쌍으로 기억해두면 좋아요.
자주 하는 실수
| 실수 | 무슨 일이 생기나 | 해결법 |
|---|---|---|
평균에 / 를 씀 |
115.33... 처럼 소수가 나옴 |
몫만 필요하면 // |
f-string 앞에 f 를 빠뜨림 |
{total} 이 글자 그대로 출력됨 |
따옴표 앞에 f |
🎯 [응용] 프로필 카드 만들기 — 예시답안
채점 포인트
| 항목 | 배점 | 기준 |
|---|---|---|
input() 으로 세 값을 받음 |
30% | 이름·좋아요·평점을 물어보는가 |
알맞은 형 변환 (int/float) |
40% | 좋아요는 int, 평점은 float 인가 |
| 멀티라인 f-string + 형식 지정 | 30% | :, 와 :.2f 를 적용했는가 |
풀이 예시
# src/a_intro/a2_profile_card_solution.py
# [응용] 프로필 카드 만들기
username = input("사용자 이름: ")
likes_text = input("총 좋아요 수: ")
rating_text = input("평점: ")
likes = int(likes_text)
rating = float(rating_text)
card = f"""[프로필 카드]
이름 : @{username}
좋아요 : {likes:,}
평점 : {rating:.2f}"""
print(card)
실행하면 질문이 하나씩 뜨고, 답을 입력하면 카드가 완성됩니다.
사용자 이름: 민지
총 좋아요 수: 12345
평점: 4.789
[프로필 카드]
이름 : @민지
좋아요 : 12,345
평점 : 4.79
💡 튜터의 한마디
input() 이 준 값은 늘 글자라서, 계산이나 형식 지정을 하려면 먼저 int()·float() 로 바꿔야 해요.
입력한 12345 가 int 로 바뀐 덕에 :, 가 천 단위 콤마를 찍어 12,345 가 됐고, 4.789 는 float 라서 :.2f 가 4.79 로 반올림했죠.
자주 하는 실수
| 실수 | 무슨 일이 생기나 | 해결법 |
|---|---|---|
변환 없이 글자에 :, 적용 |
콤마가 안 찍히거나 에러가 남 | 먼저 int() 로 변환 |
좋아요를 float 로 변환 |
12,345.0 처럼 소수점이 붙음 |
정수는 int() |
🎯 [심화] 해시태그 정리기 — 예시답안
채점 포인트
| 항목 | 배점 | 기준 |
|---|---|---|
strip() + replace() 로 다듬기 |
30% | 앞뒤 공백과 느낌표를 없앴는가 |
split() + join() 으로 띄어쓰기 정리 |
40% | 여러 칸을 한 칸으로 합쳤는가 |
lower() 로 소문자 통일 |
30% | 영문을 소문자로 바꿨는가 |
풀이 예시
# src/a_intro/a2_hashtag_cleaner_solution.py
# [심화] 해시태그 정리기
raw = " Coffee Time!! #Seoul #CAFE #Daily "
caption = raw.strip() # 1) 앞뒤 공백 제거
caption = caption.replace("!", "") # 2) 느낌표 제거
caption = " ".join(caption.split()) # 3) 여러 칸 띄어쓰기를 한 칸으로
caption = caption.lower() # 4) 영문을 소문자로 통일
print(f"정리 전: '{raw}'")
print(f"정리 후: '{caption}'")
실행하면 지저분하던 글자가 깔끔해져요.
정리 전: ' Coffee Time!! #Seoul #CAFE #Daily '
정리 후: 'coffee time #seoul #cafe #daily'
💡 튜터의 한마디
메서드를 한 줄씩 차례로 적용하면서, 매번 그 결과를 같은 변수에 다시 담는 게 핵심이에요.
문자열은 불변이라 원본 raw 는 그대로 남고, 다듬은 결과만 caption 에 쌓여가죠.
특히 들쭉날쭉한 띄어쓰기는 replace 로 잡기 어려운데, split() 이 공백을 알아서 토막내고 join 이 한 칸씩 다시 붙여줘서 한 방에 정리됩니다.
자주 하는 실수
| 실수 | 무슨 일이 생기나 | 해결법 |
|---|---|---|
replace(" ", " ") 로 띄어쓰기 정리 |
세 칸 이상은 깔끔히 안 줄어듦 | " ".join(글자.split()) |
| 메서드 결과를 변수에 안 담음 | 다음 줄에서 원본 그대로 다뤄 정리가 안 됨 | caption = caption.strip() 처럼 다시 담기 |
생각해볼 주제 예시답안
🤔 [생각해볼 주제 1] 나누기가 / 와 // 두 개로 나뉜 이유는?
문제 상황 요약
다른 계산기에선 나누기 버튼이 하나뿐인데, 파이썬은 /(소수)와 //(몫)를 따로 뒀어요.
왜 굳이 둘로 나눴을까요? 프로그램에서 "딱 떨어지는 정수" 가 필요한 상황은 어떤 게 있을까요?
튜터의 가이드 및 해설
"몇 개씩 나눠 갖는" 상황을 떠올리면 답이 보여요.
/ 는 정확한 값을 알려주려고 늘 소수로 답해요.
>>> 7 / 2
3.5
그런데 "사진 7장을 한 줄에 2장씩 놓으면 몇 줄?" 같은 질문에 3.5줄 은 어색하죠.
줄은 정수여야 하니까요.
>>> 7 // 2
3
>>> 7 % 2
1
// 는 "3줄을 채우고 1장 남는다" 의 그 3 을, % 는 남는 1 을 알려줘요.
게시물을 페이지로 나눌 때, 점수를 등급으로 끊을 때, 시간을 분·초로 쪼갤 때처럼 "딱 떨어지는 정수" 가 필요한 순간은 생각보다 많아요.
그래서 파이썬은 "정확한 소수가 필요하면 /, 딱 떨어지는 몫이 필요하면 //" 로 도구를 둘로 나눠 둔 거예요.
💡 핵심을 한마디로
"나누기를 둘로 나눈 건 불편이 아니라 배려예요.
/는 정확한 소수,//는 딱 떨어지는 몫 — '몇 개씩 나눠 갖나' 처럼 정수가 필요한 순간이 그만큼 자주 오거든요."
🤔 [생각해볼 주제 2] 문자열이 '불변'인 게 왜 좋을까?
문제 상황 요약
문자열 메서드는 원본을 바꾸지 않고 새 글자를 돌려줬어요. "그냥 원본을 바로 바꾸면 편할 텐데" 싶을 수도 있는데, 원본이 안 바뀌면 어떤 점이 안전할까요? 반대로 불편한 점은 없을까요?
튜터의 가이드 및 해설
불변의 가장 큰 장점은 "믿을 수 있다" 는 거예요.
내가 어떤 글자를 변수에 담아뒀는데, 그걸 여기저기서 다듬어 쓰는 사이에 원본이 몰래 바뀐다면 아주 골치 아프겠죠.
>>> name = "Instagram"
>>> name.upper()
'INSTAGRAM'
>>> name
'Instagram'
upper() 를 불러도 name 원본은 그대로 Instagram 이에요.
덕분에 "내가 담아둔 값은 내가 바꾸기 전까진 안 변한다" 를 믿을 수 있어요.
원본이 안 흔들리니, 같은 글자를 여러 곳에서 나눠 써도 안심이고요.
물론 불편한 면도 있어요.
다듬은 결과를 쓰려면 clean = name.strip() 처럼 매번 새 변수에 담아야 하죠.
하지만 이 작은 번거로움이, "어디선가 값이 슬쩍 바뀌어 버그가 나는" 큰 사고를 막아줍니다.
💡 핵심을 한마디로
"문자열이 불변인 덕에, 내가 담아둔 글자가 어디선가 몰래 바뀔 걱정이 없어요. 바꾸고 싶으면 새 글자를 만들면 되니, 원본은 늘 믿을 수 있는 기준점으로 남습니다."
🤔 [생각해볼 주제 3] input() 이 모든 값을 글자로 주는 건 친절일까 불편함일까?
문제 상황 요약
input() 은 숫자를 입력해도 글자로 줘서, 계산하려면 매번 int() 로 바꿔야 했어요.
만약 input() 이 "숫자처럼 생겼으면 알아서 숫자로" 줬다면 더 편했을까요?
아니면 지금처럼 "항상 글자, 필요하면 직접 변환" 이 더 안전할까요?
튜터의 가이드 및 해설
"알아서 해주는 것" 이 늘 좋은 건 아니에요.
input() 이 알아서 숫자로 바꿔준다고 상상해 봅시다.
사용자가 우편번호 01234 를 입력했는데 숫자로 바뀌면 앞의 0 이 사라져 1234 가 돼버려요.
전화번호, 주민번호, 카드번호처럼 "숫자로 생겼지만 숫자로 계산하면 안 되는" 값이 세상엔 아주 많죠.
그래서 파이썬은 "일단 전부 글자로 받아두고, 숫자로 쓸지 말지는 네가 정해" 라고 우리에게 맡겨요.
계산이 필요하면 int() 한 번 쓰면 되고, 그 한 줄 덕에 "이 값을 숫자로 쓰겠다" 는 의도가 코드에 분명히 드러나요.
조금 번거롭더라도, 애매하게 알아서 바꾸는 것보다 내가 분명히 정하는 쪽이 사고가 적습니다.
💡 핵심을 한마디로
"
input()의 '항상 글자' 규칙은 불편해 보여도, '이 값을 숫자로 쓸지' 를 내가 직접 정하게 해줘요. 자동 변환의 애매함보다, 분명한 한 번의int()가 더 안전합니다."