Python 강좌 : 제 16강 - 데이터 입력
데이터 입력(input)
입력(input) 함수를 이용해 사용자에게서 데이터를 입력받을 수 있습니다.
지금까지의 예제는 변수에 값을 직접 작성했기 때문에 실행할 때마다 같은 결과가 나왔습니다. 프로그램이 실행 중에 사용자로부터 값을 받을 수 있다면, 같은 코드로 입력에 따라 다른 결과를 만들 수 있습니다. 계산기, 간단한 게임, 알고리즘 문제 풀이 등 대부분의 콘솔 프로그램이 이 방식으로 동작합니다.
input(안내 문구)를 실행하면 안내 문구를 출력한 뒤, 사용자가 값을 입력하고 Enter를 누를 때까지 대기합니다.
input()은 표준 입력(stdin)에서 한 줄을 읽어옵니다. 사용자가 Enter를 누르는 순간까지 입력한 내용이 한 줄이 되며, 마지막의 줄바꿈 문자(\n)는 제거된 채로 반환됩니다.
입력 받은 초기 데이터 형식은 숫자(Number)를 입력하더라도 문자열(str)로 간주합니다.
이번 강좌에서는 input()이 항상 문자열을 반환하는 이유, 문자열을 숫자로 바꾸는 방법과 변환에 실패했을 때의 처리, 그리고 split()과 map()을 이용해 한 줄에서 여러 값을 입력받는 원리를 다룹니다.
-
Tip : 아래 예제들의 결과에서 안내 문구 뒤에 있는 값은 사용자가 직접 입력한 값입니다.
-
Tip : 안내 문구는 생략할 수 있으며,
input()은 안내 문구 뒤에 공백을 자동으로 넣지 않습니다. 입력값과 붙어 보이지 않도록"입력 : "처럼 끝에 공백을 포함해 작성합니다.
입력값은 항상 문자열
input()으로 받은 값을 바로 계산에 사용하면 의도와 다른 결과가 나오는 경우가 많습니다. 먼저 입력값이 어떤 자료형인지 직접 확인해 봅니다.
datum = input("입력 : ")
print(type(datum), repr(datum))
print(datum + datum)
print(datum * 3)- 결과
- 입력 : 5
<class ‘str’> ‘5’
55
555
숫자 5를 입력했지만 type()으로 확인하면 str이며, repr()로 출력하면 따옴표가 붙은 '5'입니다.
그러므로 datum + datum은 덧셈 10이 아니라 문자열 연결인 55가 되고, datum * 3은 곱셈 15가 아니라 문자열 반복인 555가 됩니다. 오류 없이 실행되기 때문에 오히려 실수를 알아채기 어렵습니다.
input()이 항상 문자열을 반환하는 이유는 다음과 같습니다.
- 키보드나 파일에서 들어오는 표준 입력은 본래 글자(텍스트)의 흐름입니다.
input()은 그 한 줄을 있는 그대로 전달할 뿐입니다. - 같은 입력도 어떤 자료형으로 해석할지는 프로그램마다 다릅니다.
07은 숫자7일 수도 있고 우편번호처럼0을 보존해야 하는 문자열일 수도 있으며,1e3은 실수1000.0일 수도 있고 제품 코드일 수도 있습니다. Python이 추측해서 변환하면 오히려 데이터가 망가질 수 있습니다. - 입력을 코드로 해석하는 것은 위험합니다. Python 2의
input()은 입력값을 Python 코드로 실행해 자료형을 결정했기 때문에, 사용자가 임의의 코드를 실행시킬 수 있었습니다. Python 3에서는 이 동작을 없애고 항상 문자열만 반환하도록 바뀌었습니다.
즉, input()은 읽기만 담당하고, 해석(형 변환)은 프로그램을 작성하는 사람이 명시적으로 결정하도록 설계되어 있습니다.
- Tip :
input()은 마지막 줄바꿈 문자만 제거하며, 앞뒤의 공백은 그대로 남깁니다." hello "를 입력하면 공백까지 포함된' hello '가 반환되므로, 공백을 제거하려면문자열.strip()을 사용합니다.
단일 데이터 입력
입력값을 숫자로 사용하려면 int()나 float() 같은 형 변환 함수로 문자열을 원하는 자료형으로 바꿔야 합니다.
datum = input("입력 : ")
answer = int(datum) + 5
print(answer)- 결과
- 입력 : 3
8
int(데이터)를 통하여 문자열을 정수형으로 변환하여 계산합니다.
실행 순서는 다음과 같습니다. input()이 문자열 '3'을 반환하여 datum에 할당되고, int(datum)이 정수 3을 새로 만들어 5와 더합니다. datum 자체는 여전히 문자열 '3'이며, 형 변환 함수는 원본을 바꾸지 않고 새 값을 반환합니다.
변환하지 않고 datum + 5를 실행하면 문자열과 정수를 더할 수 없으므로 TypeError가 발생합니다.
-
Tip : 실수를 입력받으려면
float(데이터)로 변환합니다. -
Tip :
abc처럼 정수로 변환할 수 없는 값을int()로 변환하면ValueError가 발생합니다.
datum = input("입력 : ")
print(datum + 5)- 결과
- 입력 : 3
TypeError: can only concatenate str (not “int”) to str
오류 메시지는 “문자열에는 (정수가 아닌) 문자열만 연결할 수 있다”는 뜻입니다. + 연산자는 양쪽이 모두 숫자이면 덧셈, 모두 문자열이면 연결을 수행하며, 자료형이 섞이면 어느 쪽으로 해석할지 정할 수 없으므로 오류를 발생시킵니다.
input()을 사용하는 코드에서 이 오류가 발생했다면 대부분 형 변환을 빠뜨린 것입니다.
print(int("42"), int(" -7 "), float("3.5"), float("1e3"))
print(int(float("3.9")))
print(int("3.5"))- 결과
- 42 -7 3.5 1000.0
3
ValueError: invalid literal for int() with base 10: ‘3.5’
형 변환 함수가 받아들이는 문자열의 형식을 확인하는 예제입니다.
int()는 앞뒤 공백과 부호(-, +)를 허용하므로 " -7 "도 -7로 변환됩니다. float()는 소수점이나 1e3 같은 지수 표기도 해석합니다.
하지만 int("3.5")처럼 소수점이 있는 문자열은 정수로 직접 변환할 수 없어 ValueError가 발생합니다. 실수 형태의 입력을 정수로 바꾸려면 int(float("3.9"))처럼 먼저 실수로 변환한 뒤 정수로 바꿉니다. 이때 int()는 소수점 이하를 버리므로 3.9는 4가 아닌 3이 됩니다.
오류 메시지의 invalid literal for int() with base 10은 “10진수 정수로 해석할 수 없는 문자열”이라는 뜻이며, 뒤에 실제로 입력된 값이 표시되어 원인을 쉽게 찾을 수 있습니다.
형 변환 실패 처리
사용자는 언제든 잘못된 값을 입력할 수 있습니다. 숫자를 기대했는데 abc가 입력되면 int()에서 ValueError가 발생하고, 처리하지 않으면 프로그램이 그대로 중지됩니다. 실제 프로그램에서는 오류를 알려주고 다시 입력받도록 만들어야 합니다.
while True:
text = input("정수 입력 : ")
try:
number = int(text)
except ValueError:
print(repr(text), "은(는) 정수가 아닙니다. 다시 입력하세요.")
else:
break
print("입력한 정수 :", number)- 결과
- 정수 입력 : abc
‘abc’ 은(는) 정수가 아닙니다. 다시 입력하세요.
정수 입력 : 3.5
‘3.5’ 은(는) 정수가 아닙니다. 다시 입력하세요.
정수 입력 : 7
입력한 정수 : 7
제 15강의 while True: 반복과 제 18강에서 자세히 다룰 try-except 예외 처리를 조합한 입력 검증의 기본 형태입니다.
실행 흐름을 따라가면 다음과 같습니다.
abc를 입력하면int(text)에서ValueError가 발생합니다. 실행은 즉시except ValueError:로 이동해 안내 문구를 출력하고, 반복문의 처음으로 돌아가 다시 입력받습니다.3.5도int()로 변환할 수 없으므로 같은 과정을 반복합니다.7을 입력하면 변환에 성공합니다. 예외가 발생하지 않았으므로else:블록이 실행되어break로 반복을 빠져나가고, 마지막print()가 실행됩니다.
except 뒤에 ValueError를 명시하여 변환 실패만 처리하도록 작성했습니다. 예외 종류를 생략하면 Ctrl + C로 프로그램을 끄려는 동작까지 잡아버려 반복을 빠져나갈 수 없게 될 수 있습니다.
- Tip :
try블록에는 오류가 날 수 있는int(text)만 두고, 성공했을 때의 처리(break)는else에 둡니다. 예외 처리의 실행 흐름은 제 18강에서 자세히 다룹니다.
print("123".isdigit(), "-3".isdigit(), "3.5".isdigit())- 결과
- True False False
변환하기 전에 문자열.isdigit()으로 숫자로만 이루어져 있는지 미리 검사하는 방법도 있습니다.
하지만 isdigit()은 숫자 문자만 검사하므로, int()로는 정상 변환되는 "-3"도 - 기호 때문에 False가 됩니다. 음수나 공백, 실수까지 고려한 검사 조건을 직접 작성하기는 어렵습니다.
그러므로 Python에서는 일반적으로 먼저 변환을 시도하고 실패하면 처리하는 try-except 방식을 사용합니다. 변환 규칙을 가장 정확히 아는 것은 int() 자신이기 때문입니다.
x = input()- 결과
- EOFError: EOF when reading a line
input()이 읽을 내용이 더 이상 없으면 EOFError가 발생합니다. 예제는 아무 입력도 연결하지 않은 상태로 실행한 결과입니다.
콘솔에서 Ctrl + Z 후 Enter(Windows) 또는 Ctrl + D(macOS, Linux)를 누르거나, python 파일.py < 입력.txt처럼 파일을 입력으로 연결했는데 파일의 줄이 모자라는 경우에 발생합니다.
입력의 끝까지 읽어야 하는 프로그램이라면 except EOFError:로 이 예외를 처리해 반복을 종료합니다.
다중 데이터 입력
input()은 한 번에 한 줄만 읽습니다. 한 줄에 여러 값을 입력받으려면, 읽어온 한 줄짜리 문자열을 구분자를 기준으로 나누는 과정이 필요합니다.
data = input("입력 (x,y,z) : ")
L = data.split(',')
x, y, z = L[0], L[1], L[2]
print(x)
print(y)
print(z)- 결과
- 입력 (x,y,z) : 1,2,3
1
2
3
문자열.split(구분자)를 이용하여 data에서 쉼표(,)마다 분리하여 리스트로 저장합니다.
이 후, 각각의 변수에 할당합니다.
처리 과정은 세 단계입니다. input()이 '1,2,3'이라는 문자열 하나를 반환하고, split(',')이 쉼표를 경계로 잘라 ['1', '2', '3'] 목록을 만들며, 인덱싱으로 각 요소를 변수에 할당합니다. 나누어진 각 요소도 여전히 문자열입니다.
- Tip :
x, y, z = L처럼 목록을 바로 언패킹하여 할당할 수도 있습니다.
print("1,,2".split(","))
print(" 1 2 ".split())
print(" 1 2 ".split(" "))
print("a,b,c".split(",", 1))- 결과
- [‘1’, ‘’, ‘2’]
[‘1’, ‘2’]
[’’, ‘1’, ‘’, ‘2’, ‘’]
[‘a’, ‘b,c’]
split()은 구분자를 지정했는지에 따라 동작이 다릅니다.
split(",")처럼 구분자를 지정하면 구분자가 나올 때마다 정확히 자르므로, 쉼표가 연속되면 그 사이의 빈 문자열도 요소가 됩니다. split(" ")도 마찬가지로 공백 하나하나를 경계로 삼아 빈 문자열이 많이 생깁니다.
split()처럼 구분자를 생략하면 연속된 공백을 하나로 보고 앞뒤 공백도 무시하므로, 사람이 띄어쓰기를 불규칙하게 입력해도 ['1', '2']처럼 깔끔하게 나눠집니다.
split(",", 1)처럼 두 번째 인수를 주면 최대 분리 횟수를 제한하여, 나머지는 하나의 문자열로 남깁니다.
- Tip : 공백으로 구분된 입력은
split(" ")가 아닌split()을 사용합니다.
다중 데이터 입력 간소화
입력, 분리, 할당 과정을 한 줄로 연결하면 코드를 짧게 작성할 수 있습니다.
x, y, z = input("데이터 입력 (a,b,c) : ").split(',')
print(x)
print(y)
print(z)- 결과
- 데이터 입력 (a,b,c) : 4,qqq,07
4
qqq
07
input()에서 데이터 할당과 동시에 문자열.split(구분자) 구문을 이용하여 쉼표(,)마다 분리하여 저장합니다.
이 한 줄은 안쪽에서 바깥쪽으로 실행됩니다. input()이 먼저 실행되어 문자열을 반환하고, 그 문자열의 split(',')이 호출되어 목록이 만들어진 뒤, 마지막으로 = 왼쪽의 세 변수에 언패킹됩니다.
언패킹은 오른쪽의 요소를 왼쪽의 변수 개수와 정확히 맞춰 하나씩 나누어 담습니다.
입력한 값의 개수와 변수의 개수가 다르면 ValueError가 발생합니다. 예를 들어 1,2만 입력하면 not enough values to unpack (expected 3, got 2) 오류가 발생합니다.
- Tip :
input()함수의 할당 된 데이터는str형식으로 취급합니다. 그러므로07도 숫자7이 아닌 문자열'07'로 저장됩니다.
x, y, z = map(str.strip, input("데이터 입력 (a,b,c) : ").split(','))
print(x)
print(y)
print(z)- 결과
- 데이터 입력 (a,b,c) : 4 , qqq , 07
4
qqq
07
쉼표(,) 앞뒤에 공백( )이 포함된 채로 입력하면, split(',')의 결과에도 공백이 그대로 남습니다.
map(함수, 반복값)은 반복값의 각 요소에 함수를 적용한 결과를 차례대로 반환합니다.
str.strip을 적용하면 분리된 각 문자열의 양측 공백을 제거한 뒤 할당할 수 있습니다.
split(',')의 결과는 ['4 ', ' qqq ', ' 07']이며, map()이 각 요소에 str.strip을 차례대로 적용하여 '4', 'qqq', '07'을 만들어 변수에 할당합니다. 이때 str.strip 뒤에 소괄호를 쓰지 않는다는 점에 주의합니다. 함수를 호출한 결과가 아니라 함수 자체를 map()에 전달하고, 호출은 map()이 요소마다 대신 수행합니다.
숫자 여러 개 입력
여러 값을 숫자로 사용하려면 분리한 각 요소마다 형 변환을 해야 합니다. 이때 map()에 int나 float를 전달하면 반복문 없이 한 번에 변환할 수 있습니다.
a, b = map(int, input("두 정수 입력 : ").split())
print(a + b)
print(type(a))- 결과
- 두 정수 입력 : 3 5
8
<class ‘int’>
split()처럼 구분자를 지정하지 않으면 공백(띄어쓰기, 탭 등)을 기준으로 분리하며, 연속된 공백도 하나로 취급합니다.
map(int, ...)으로 분리된 각 문자열을 정수로 변환한 뒤 변수에 할당합니다.
공백으로 구분된 여러 숫자를 입력받을 때 가장 많이 사용하는 형태입니다.
이 한 줄은 '3 5' → ['3', '5'] → int('3'), int('5') → a = 3, b = 5의 순서로 처리됩니다. 결과적으로 a와 b는 정수이므로 a + b는 8이 됩니다.
a, b, c = map(int, input("세 정수 입력 : ").split())- 결과
- 세 정수 입력 : 1 2 x
ValueError: invalid literal for int() with base 10: ‘x’
여러 값을 한 번에 변환할 때는 오류가 두 종류 발생할 수 있습니다.
예제처럼 값 중 하나라도 정수로 바꿀 수 없으면 int()의 ValueError가 발생합니다. 값의 개수가 맞지 않으면 언패킹의 ValueError가 발생하며, 1 2 3 4처럼 많이 입력하면 too many values to unpack (expected 3) 메시지가 표시됩니다.
두 오류는 같은 ValueError이므로, 앞의 입력 검증 예제처럼 try-except ValueError:로 함께 처리하여 다시 입력받을 수 있습니다.
numbers = list(map(int, input("숫자 입력 : ").split(',')))
print(numbers)
print(sum(numbers))- 결과
- 숫자 입력 : 1, 2, 3
[1, 2, 3]
6
입력할 값의 개수가 정해져 있지 않다면, list(map(...))로 목록으로 변환해 사용합니다.
map()의 결과는 목록이 아닌 map 객체이므로, 여러 번 사용하거나 출력하려면 list()로 변환해야 합니다.
- Tip :
int()는 문자열 양쪽의 공백을 무시하므로" 2"도 정수2로 변환됩니다.
m = map(int, ["1", "2", "3"])
print(m)
print(list(m))
print(list(m))- 결과
- <map object at 0x000001767FBBBF80>
[1, 2, 3]
[]
map 객체를 직접 출력하면 값이 아닌 <map object at ...> 형태가 표시됩니다. (메모리 주소는 실행할 때마다 달라집니다.)
map()은 변환 결과를 미리 만들어 두지 않고, 값이 요청될 때마다 하나씩 계산하는 반복자(Iterator)입니다. 첫 번째 list(m)이 모든 값을 꺼내 사용했으므로, 두 번째 list(m)은 꺼낼 값이 남아 있지 않아 빈 목록이 됩니다.
언패킹(a, b = map(...))이나 list(map(...))처럼 한 번만 사용하는 경우에는 문제가 없지만, 여러 번 사용해야 한다면 반드시 list()로 변환해 변수에 저장합니다.
first, *rest = map(int, input("숫자 입력 : ").split())
print(first)
print(rest)- 결과
- 숫자 입력 : 10 20 30 40
10
[20, 30, 40]
변수 앞에 *를 붙이면 나머지 요소를 모두 목록으로 받는 언패킹이 됩니다.
첫 번째 값 10은 first에, 남은 20, 30, 40은 목록 rest에 할당됩니다. 개수가 정해지지 않은 입력에서도 ValueError 없이 앞의 몇 개만 따로 꺼낼 수 있습니다.
여러 줄 입력
input()은 한 줄씩 읽으므로, 여러 줄에 걸친 입력은 input()을 필요한 줄 수만큼 반복 호출하여 받습니다. 입력 데이터의 첫 줄에 개수를 주고, 이어서 값을 한 줄씩 주는 형식이 자주 사용됩니다.
n = int(input("개수 : "))
numbers = [int(input(f"{i + 1}번째 값 : ")) for i in range(n)]
print(numbers)- 결과
- 개수 : 3
1번째 값 : 10
2번째 값 : 20
3번째 값 : 30
[10, 20, 30]
첫 번째 input()으로 개수 3을 읽어 정수로 변환한 뒤, range(3)만큼 input()을 다시 호출하여 값을 하나씩 읽습니다.
input()은 호출될 때마다 다음 줄을 읽으므로, 같은 코드가 반복되어도 매번 새로운 값이 반환됩니다. 각 값은 int()로 변환된 뒤 목록에 차례대로 저장됩니다.
목록을 만드는 [... for i in range(n)] 형태는 제 19강의 간소화(Comprehension)에서 자세히 다룹니다.
- Tip : 입력 줄 수가 수십만 줄 이상으로 매우 많다면
sys.stdin.readline()을 사용하면 더 빠르게 읽을 수 있습니다. 단, 이 함수는 줄 끝의\n을 제거하지 않으므로strip()이나int()변환으로 처리해야 합니다.
정리
| 이름 | 설명 | 반환값/특징 |
|---|---|---|
input(안내 문구) |
표준 입력에서 한 줄을 읽음 | 항상 str, 끝의 줄바꿈만 제거, 입력이 없으면 EOFError |
int(문자열) |
정수로 변환 | 앞뒤 공백·부호 허용, 실패 시 ValueError |
float(문자열) |
실수로 변환 | 소수점·지수 표기 허용, 실패 시 ValueError |
문자열.split() |
공백 기준으로 분리 | 연속 공백을 하나로 취급, list 반환 |
문자열.split(구분자) |
구분자 기준으로 분리 | 연속 구분자 사이의 빈 문자열도 요소가 됨 |
map(함수, 반복값) |
각 요소에 함수 적용 | 한 번만 사용할 수 있는 반복자 |
a, b = 반복값 |
언패킹 | 개수가 다르면 ValueError, *변수로 나머지 수집 |
try-except ValueError |
변환 실패 처리 | 잘못된 입력 시 다시 입력받기 |
input()으로 받은 값은 문자열 → 분리 → 형 변환 → 할당의 순서로 처리한다는 흐름을 기억하면, 한 줄짜리 입력 코드도 쉽게 읽을 수 있습니다. 그리고 사용자의 입력은 언제든 잘못될 수 있으므로, 형 변환에는 예외 처리를 함께 고려해야 합니다.
댓글 남기기