PYTHON

파이썬 문자열은 바뀌지 않습니다 — strip 결과를 받아야 하는 이유

PANCO_IT
#파이썬#python#문자열#strip#lower#replace#split#슬라이싱#파이썬기초#파이썬독학

잉크 도장으로 종이에 I, D, E 세 글자가 찍혀 있고 네 번째 A 도장은 아직 종이 위에 놓여 있다

이미지 출처: Pexels · Eva Bronzini · Pexels License

회원 가입 폼에서 넘어온 이름과 메일 주소를 다듬는 코드입니다. 8줄인데 그중 3줄이 틀렸고, 에러는 한 줄도 나지 않습니다.

name = "  Hong Gildong  "
email = "HONG@Example.COM"

name.strip()
email = email.lower
first = name[0:4]

print("이름:[" + name + "]")
print("메일:", email)
print("성:[" + first + "]")

돌려 보면 이렇게 나옵니다.

이름:[  Hong Gildong  ]
메일: <built-in method lower of str object at 0x7f2a1c0b5230>
성:[  Ho]

세 줄 모두 틀렸습니다. 이름의 공백은 그대로고, 메일 자리에는 주소 대신 알 수 없는 문장이 찍혔고, 성은 Hong이 아니라 공백 두 칸에 Ho입니다.

그런데 에러는 한 줄도 나지 않았습니다. 빨간 글씨가 떴다면 그 자리를 봤을 텐데, 프로그램은 조용히 끝까지 돌았습니다. 이런 실수는 출력을 눈으로 확인하기 전까지 모릅니다.

세 개를 하나씩 잡아 보겠습니다.

실수 하나 — 메서드를 불렀는데 원본이 그대로입니다

name.strip()을 분명히 썼는데 공백이 남아 있습니다. 오타도 아니고 철자도 맞습니다.

strip을 불러도 name은 그대로입니다
name = "  Hong Gildong  "

name.strip()
print("[" + name + "]")

print("[" + name.strip() + "]")

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

세 번째 줄에서 strip()을 불렀는데 네 번째 줄 출력에는 공백이 그대로입니다. 그런데 여섯 번째 줄처럼 name.strip()을 출력 자리에 바로 넣으면 공백이 사라진 값이 나옵니다.

파이썬 문자열은 한 번 만들어지면 고칠 수 없습니다. 그래서 strip()은 원본을 다듬는 것이 아니라, 공백을 뗀 새 문자열을 만들어 돌려줍니다. 돌려준 값을 아무도 받지 않으면 그 새 문자열은 만들어지자마자 버려집니다. 원본 name은 처음부터 끝까지 손대지 않은 채로 남습니다.

문자열 메서드가 원본을 고치지 않고 새 값을 돌려준다는 것을 두 갈래로 그린 흐름도. 위쪽 갈래는 name.strip() 호출로 공백을 뗀 새 문자열이 만들어지지만 받는 이름표가 없어 버려지고 name은 공백이 있는 원래 값 그대로 남는다. 아래쪽 갈래는 name = name.strip()으로 이름표를 새 문자열 쪽으로 옮겨 붙여 name이 다듬은 값을 가리키게 된다

위 갈래가 지금 코드입니다. 새 값은 만들어졌지만 받는 곳이 없어 사라집니다. 아래 갈래처럼 이름표를 옮겨 붙여야 남습니다.

고치는 방법은 한 글자도 아니고 이름표를 다시 붙이는 것입니다.

name = name.strip()

upper(), lower(), replace(), title() 전부 같습니다. 문자열 메서드는 하나도 빠짐없이 새 값을 돌려주고 원본은 그대로 둡니다. 그래서 결과를 쓰려면 변수에 받거나, 받은 자리에서 바로 써야 합니다.

리스트를 배웠다면 헷갈릴 수 있습니다. list.append(3)은 리스트 자체를 바꿉니다. 리스트는 고칠 수 있고 문자열은 고칠 수 없습니다. 같은 점 표기법이라 같아 보이지만 동작이 반대입니다.

실수 둘 — 괄호를 빠뜨리면 기능 자체가 담깁니다

메일 자리에 찍힌 문장을 다시 보겠습니다.

<built-in method lower of str object at 0x7f2a1c0b5230>

email = email.lower에서 괄호를 빠뜨렸습니다. 괄호가 없으면 파이썬은 lower라는 기능 자체를 가리키는 것으로 읽습니다. 실행하라는 말이 아니라 "그 기능이 여기 있다"는 뜻입니다. 그 기능 덩어리가 email에 담겼고, 출력하니 위 문장이 나왔습니다.

괄호 하나 차이
email = "HONG@Example.COM"

print(email.lower)
print(email.lower())

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

첫 줄은 기능을, 둘째 줄은 그 기능을 실행한 결과를 찍습니다. 괄호는 "지금 실행해"라는 신호입니다.

이 실수도 에러가 나지 않습니다. email에 뭔가 담기긴 했으니까요. 다만 그다음에 문자열인 줄 알고 쓰면 그때 가서 터집니다.

email = email.lower
print(email + "@")
TypeError: unsupported operand type(s) for +: 'builtin_function_or_method' and 'str'

에러가 실수한 줄이 아니라 한참 뒤에서 납니다. 이런 에러를 만나면 그 줄이 아니라 그 변수에 값이 담긴 자리를 거슬러 올라가야 합니다.

실수 셋 — 슬라이싱은 다듬기 전 문자열을 자릅니다

first = name[0:4]Hong이 아니라 공백 두 칸에 Ho를 가져온 이유는 둘입니다.

첫째, 자른 대상이 다듬기 전 문자열입니다. 실수 하나 때문에 name은 여전히 " Hong Gildong "이고, 그 0번 칸은 H가 아니라 공백입니다.

둘째, 끝 번호는 포함되지 않습니다. [0:4]는 0번부터 4번까지가 아니라 0번부터 3번까지 넉 자입니다.

문자열 슬라이싱을 종이 테이프처럼 그린 그림. 공백 두 칸으로 시작하는 Hong Gildong 문자열의 각 칸 위에는 0부터 시작하는 번호가, 아래에는 마이너스 1부터 거꾸로 세는 번호가 적혀 있다. 0부터 3까지 네 칸이 색으로 묶여 있고 그 결과가 공백 공백 H o 라고 표시되어 있으며, 4번 칸에는 끝 번호는 포함되지 않는다는 표시가 붙어 있다

칸마다 번호가 붙어 있고 번호는 0부터입니다. [0:4]는 색칠된 네 칸이고, 4번 칸은 들어가지 않습니다.

번호를 직접 세어 보기
name = "  Hong Gildong  "
clean = name.strip()

print("[" + name[0:4] + "]")
print("[" + clean[0:4] + "]")

print(clean[-1])
print(clean[5:])
print(clean[::-1])

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

다듬은 뒤에 자르면 Hong이 제대로 나옵니다. 나머지 셋은 자주 쓰는 모양입니다.

  • clean[-1] — 음수는 뒤에서 셉니다. -1이 마지막 글자입니다.
  • clean[5:] — 끝 번호를 비우면 끝까지입니다. [:5]는 처음부터입니다.
  • clean[::-1] — 간격을 -1로 주면 거꾸로 읽습니다. 뒤집기의 관용 표현입니다.

여기서 순서 하나가 남습니다. 다듬고 나서 자르는 것과 자르고 나서 다듬는 것은 결과가 다릅니다. 지금 코드는 자르기가 먼저였고, 그래서 공백이 딸려 왔습니다.

고친 코드 여덟 줄

세 곳을 고칩니다. 줄 수는 그대로입니다.

세 줄만 고친 완성 코드
name = "  Hong Gildong  "
email = "HONG@Example.COM"

name = name.strip()
email = email.lower()
first = name[0:4]

print("이름:[" + name + "]")
print("메일:", email)
print("성:[" + first + "]")

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

이름:[Hong Gildong]
메일: hong@example.com
성:[Hong]

틀린 코드와 고친 코드의 같은 세 줄을 위아래로 놓고 바뀐 곳만 표시한 그림. 첫 줄은 name.strip()에서 name = name.strip()으로 이름표를 붙였고, 둘째 줄은 email.lower에서 email.lower()로 괄호가 생겼으며, 셋째 줄은 코드가 같지만 앞 줄에서 name이 다듬어진 덕분에 결과가 공백 공백 H o에서 Hong으로 달라진다. 오른쪽에 두 출력이 나란히 놓여 있다

바뀐 것은 이름표 하나와 괄호 한 쌍입니다. 셋째 줄은 코드가 그대로인데 결과만 달라졌습니다 — 앞 줄이 고쳐졌기 때문입니다.

셋째 줄이 이 글에서 가장 중요한 자리입니다. 그 줄은 한 글자도 건드리지 않았는데 출력이 달라졌습니다. 문자열을 다루는 코드는 이렇게 앞 줄의 결과를 물려받습니다. 한 줄이 어긋나면 그 아래가 전부 조용히 어긋납니다.

자주 쓰는 메서드 여덟 개

오늘 쓴 것과 바로 옆에 붙어 다니는 것만 모았습니다. 전부 새 값을 돌려주고 원본은 그대로 둡니다.

메서드하는 일결과
strip()양 끝 공백을 뗀다" hi ".strip()"hi"
lower() / upper()소문자 / 대문자로"Hi".lower()"hi"
replace(a, b)a를 b로 전부 바꾼다"a-b-c".replace("-", "")"abc"
split(구분자)잘라서 리스트로"a,b,c".split(",")["a", "b", "c"]
"구분자".join(리스트)리스트를 붙여 문자열로"-".join(["a", "b"])"a-b"
find(글자)몇 번 칸인지. 없으면 -1"hello".find("l")2
startswith(글자)그 글자로 시작하는지"hi.py".startswith("hi")True
count(글자)몇 번 나오는지"hello".count("l")2

두 가지만 더 짚겠습니다.

strip()은 양 끝만 뗍니다. 가운데 공백은 건드리지 않습니다. " Hong Gildong ".strip()"Hong Gildong"이고 가운데 두 칸은 남습니다. 가운데까지 없애려면 replace(" ", "")를 쓰는데, 이러면 이름이 HongGildong으로 붙어 버립니다. 하나로 줄이고 싶으면 " ".join(name.split())이 관용 표현입니다.

join은 방향이 헷갈립니다. 붙이는 쪽이 아니라 사이에 끼울 글자가 앞에 옵니다. ["a", "b"].join("-")이 아니라 "-".join(["a", "b"])입니다. 반대로 쓰면 AttributeError가 납니다.

strip과 replace와 join의 차이
raw = "  Hong   Gildong  "

print("[" + raw.strip() + "]")
print("[" + raw.replace(" ", "") + "]")
print("[" + " ".join(raw.split()) + "]")

📱 코드 실행(실습)은 태블릿·PC에서 이용할 수 있어요. 모바일에서는 위 코드를 눈으로 따라가며 확인하세요.

한 글자만 바꾸면 결과는 어떻게 될까요

미니 퀴즈

OX 3문제 + 4지선다 5문제

참고 자료


포켓코딩(PYTHON2) 1편 · PYTHON1 총정리에서 이어집니다. 예제 코드는 강사 개인 GitHub 저장소의 강의 자료를 바탕으로 다시 썼습니다. 다음 편은 딕셔너리입니다. 순서가 아니라 이름표로 값을 꺼내는 {}와, 없는 열쇠를 물었을 때 나는 KeyError를 다룹니다.

PANCO_IT

IT 지식을 정리하는 개발자

관련 글