문서 읽는 데 47분 · A2

A-2: 연산과 문자열 — 계산하고, 글자를 자르고 합치기

목차 48
전체 22강 중 3강 · 파이썬 기초
난이도 · 입문

ℹ️프로그래밍 경험이 없어도 시작할 수 있는 파이썬 입문 — ‘개발 입문’을 먼저 들으면 더 수월해요. AI·데이터 분석으로 이어지는 출발점이에요.

안녕하세요! 여러분의 파이썬 가이드, 홍순구 튜터입니다.

지난 시간엔 변수에 값을 담고, 값의 종류(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은 실험용이라 끄면 입력한 게 사라지죠. 이번엔 파일에 담아 게시물 좋아요 수를 계산해 봅시다.

Python
# 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 / 23.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 값을 만들어내는 도구랍니다.

여섯 가지 비교 연산자

파일로 두 게시물의 좋아요 수를 견주어 봅시다.

Python
# 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 으로 인기 게시물 판단하기

Python
# 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.

andor 의 모든 경우를 표로 그리면 이렇습니다.

텍스트
   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 이 준 값은 늘 글자

Python
# 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() 로 바꾸기

Python
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 계산이 됐죠.

에러는 친구 — 글자와 숫자를 그냥 더하면?

지난 시간에 "에러는 친구" 라고 했죠? 일부러 글자와 숫자를 그냥 더해보고, 파이썬이 뭐라고 하는지 들어봅시다.

Python
# 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)는 안 돼" 라는 거고요. 화살표 ^ 가 문제가 난 자리를 정확히 가리켜 주죠. 해결책은 간단해요. bonusstr(bonus) 로 글자로 바꾸거나, likes_textint(likes_text) 로 숫자로 바꾸면 됩니다.

💡 한 줄 정리

int()·str()·float() 로 값의 종류를 바꿔요. 특히 input() 이 준 글자는 계산하기 전에 int()/float() 로 바꿔야 합니다.

🙋 학생 질문 — "input() 은 왜 숫자도 글자로 주나요? 불편한데요."

사용자가 키보드로 뭘 칠지 파이썬은 미리 알 수 없어요.

25 를 칠 수도, 스물다섯 을 칠 수도, 안녕 을 칠 수도 있죠. 그래서 input() 은 가장 안전하게 "일단 글자로" 받아둬요. 숫자가 필요하면 우리가 직접 int() 로 바꾸겠다고 알려주는 거예요.

덕분에 "이 값을 숫자로 쓸 거야" 라는 의도가 코드에 분명히 드러나는 장점도 있답니다.


Step 5: "f-string 제대로 파헤치기"

지난 시간 인사말에 f"안녕하세요 @{name}" 같은 걸 썼죠. 그때 "맨 앞의 f 는 뭐지?" 싶었을 거예요. 드디어 그 정체를 밝힙니다.

f 와 {} 의 정체

f-string은 따옴표 앞에 f 를 붙인 글자예요. 그 안에서 중괄호 {} 로 감싼 부분은 '값으로 바뀌어' 글자에 끼워집니다.

Python
# src/a_intro/a2_fstring.py
username = "minji"
likes = 1240
comments = 86

# 1) f 와 {} — 중괄호 안의 변수가 값으로 바뀐다
print(f"@{username} 님의 게시물")
텍스트
@minji 님의 게시물

{username} 자리에 변수 값 minji 가 쏙 들어갔죠.

중괄호 안에는 '식'도 넣을 수 있다

Python
# 2) 중괄호 안에는 '식'도 넣을 수 있다 (계산 결과가 들어감)
print(f"좋아요 + 댓글 = {likes + comments}")
print(f"2의 10제곱 = {2 ** 10}")
텍스트
좋아요 + 댓글 = 1326
2의 10제곱 = 1024

Step 1에서 배운 연산자를 중괄호 안에서 바로 계산할 수 있어요. {likes + comments}1326 으로 바뀌어 들어갔죠.

보기 좋게 — 형식 지정

Python
# 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

Python
# 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번 직전까지!)
Python
# 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

Python
# 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) 다듬고 합쳐서 캡션과 해시태그를 가지고 놀자" 고 예고했죠. 이제 그 splitjoin 을 만나봅니다. 오늘의 마지막 도구예요.

split — 글자를 여러 조각으로 자르기

Python
# 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] 을 그대로 쓰면 되고요.

구분자를 직접 정하기

Python
profile_line = "민지,서울,사진작가"
fields = profile_line.split(",")
print(f"콤마로 자른 결과: {fields}")
텍스트
콤마로 자른 결과: ['민지', '서울', '사진작가']

split(",") 처럼 기준이 될 글자를 넣어주면 공백 대신 그 글자로 잘라요.

join — 조각을 다시 하나로 합치기

Python
rejoined = " / ".join(parts)
print(f"join 으로 합치기: {rejoined}")
텍스트
join 으로 합치기: 오늘 / 카페 / 다녀왔어요 / #일상 / #카페 / #감성

joinsplit 의 반대예요. "사이에 끼울 글자".join(조각들) 형태로, 조각 사이에 글자를 끼워 하나로 잇습니다. " / " 로 합치니 슬래시로 이어졌죠.

split + join 콤보 — 들쭉날쭉한 띄어쓰기 정리

Python
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% {변수} 로 값을 끼워 넣었는가

풀이 예시

Python
# 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 를 적용했는가

풀이 예시

Python
# 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() 로 바꿔야 해요. 입력한 12345int 로 바뀐 덕에 :, 가 천 단위 콤마를 찍어 12,345 가 됐고, 4.789float 라서 :.2f4.79 로 반올림했죠.

자주 하는 실수

실수 무슨 일이 생기나 해결법
변환 없이 글자에 :, 적용 콤마가 안 찍히거나 에러가 남 먼저 int() 로 변환
좋아요를 float 로 변환 12,345.0 처럼 소수점이 붙음 정수는 int()

🎯 [심화] 해시태그 정리기 — 예시답안

채점 포인트

항목 배점 기준
strip() + replace() 로 다듬기 30% 앞뒤 공백과 느낌표를 없앴는가
split() + join() 으로 띄어쓰기 정리 40% 여러 칸을 한 칸으로 합쳤는가
lower() 로 소문자 통일 30% 영문을 소문자로 바꿨는가

풀이 예시

Python
# 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() 가 더 안전합니다."

전체 목록 파이썬 기초