Skip to content
This page has been auto-translated and may contain errors.View in English

출력과 입력

프로그래밍을 시작할 때부터 사용하게 될 두 가지 도구가 있습니다. print()는 터미널에 값을 표시하고, input()은 사용자로부터 텍스트를 받습니다. 간단하지만, 이들이 어떻게 동작하는지 알면 처음에 겪을 수 있는 많은 혼란을 피할 수 있습니다.

print()input()은 Python의 표준 터미널 입출력 함수입니다. 둘 다 처음 보기보다 훨씬 더 많은 옵션을 제공합니다. print()는 값을 어떻게 연결할지, 출력이 어디서 끝날지를 제어하는 인자를 받습니다. input()은 항상 문자열을 반환하며, 이는 사용자로부터 받은 모든 값을 어떻게 다룰지에 영향을 줍니다.

내부적으로 print()sys.stdout에 쓰고 input()sys.stdin에서 읽습니다(터미널이 연결된 표준 출력과 입력 스트림). 둘 다 텍스트만 다루며, 출력은 기본적으로 버퍼링됩니다. Python은 한 문자씩이 아니라 묶음으로 작성하기 위해 출력을 잠깐 보관합니다. 실제 프로그램에서 중요한 두 가지 결과가 있습니다. 출력이 에러와 다른 스트림으로 가고(stdout과 stderr), 필요할 때 버퍼를 강제로 비워서 출력이 즉시 나타나도록 할 수 있습니다.

Python이 코드를 실행하는 방식

Python은 코드를 위에서 아래로, 한 줄씩, 정확히 작성한 순서대로 실행합니다. 건너뛰는 일은 없습니다. 작성한 순서가 실행 순서입니다. 항상 그렇습니다.

python
city = "Seoul"
print(city)
print("Population: 10 million")

먼저 city에 값이 할당됩니다. 그다음 첫 번째 print가 실행됩니다. 그다음 두 번째 print가 실행됩니다. 매번 같은 순서입니다.

이것이 중요한 이유는 변수를 할당하기 전에 사용할 수 없기 때문입니다. Python이 아직 그 변수를 보지 못했으므로 에러가 발생합니다.

python
print(country)   # NameError: country가 아직 정의되지 않았습니다
country = "Korea"

프로그램이 커질수록 이것을 명심하세요. 사용하는 모든 것은 사용하기 전에 정의되어야 합니다.

Python은 **순차 실행**을 사용합니다. 파일을 위에서 아래로 한 번 통과하면서 각 문장이 만나는 순서대로 평가됩니다. 먼저 전체 파일을 스캔하지 않습니다. 할당되기 전에 이름을 참조하면 정확히 그 줄에서 NameError가 발생합니다.

python
city = "Seoul"
print(city)             # 작동합니다: city가 이미 바인딩됨
print(country)          # NameError: 아직 할당되지 않음
country = "Korea"

규칙은 간단합니다. 필요한 것들은 그것을 사용하는 줄보다 먼저 정의되어야 합니다.

Python은 파일을 위에서 아래로 한 번에 실행하며, 파일을 처음 읽을 때가 아니라 그 줄이 실제로 실행될 때만 이름을 조회합니다. 그래서 이름이 없는 경우 저장하는 순간이 아니라 런타임에(줄이 실행될 때) NameError가 발생합니다. 호이스팅(JavaScript가 var 선언을 스코프 상단으로 조용히 옮기는 방식)도 없고, 사전 선언도 없습니다(C처럼 지금 이름을 약속하고 나중에 정의하는 방식). 이름은 할당되는 순간 존재하며, 그 전에 사용하면 에러가 발생합니다.

python
print(country)   # 런타임에 NameError 발생, 문법 에러 아님
country = "Korea"
JunoPython이 코드를 실행하는 방식 Python은 파일을 위에서 아래로, 한 줄씩, 작성한 순서대로 읽습니다. 모두를 헷갈리게 하는 한 가지는 이름이 그 이름을 사용하는 줄 전에 존재해야 하며, 그렇지 않으면 NameError가 발생한다는 것입니다. 건너뛰기도 없고, 파일 전체를 먼저 읽지도 않습니다.
JunoPython이 코드를 실행하는 방식 위에서 아래로, 한 번에, 순서대로. 이름을 사용하는 줄 전에 할당되어야 하며, 그렇지 않으면 정확히 그 줄에서 NameError가 발생합니다. 파일을 미리 스캔하지 않습니다.
JunoPython이 코드를 실행하는 방식 위에서 아래로 한 번, 각 줄이 실행될 때 이름 조회. 이름을 할당하기 전에 사용하면 문법 에러가 아니라 런타임에 NameError가 발생합니다. Python은 호이스팅하지 않으며 사전 선언이 없으므로, 순서가 정말 중요합니다.

출력하기

print()는 Python이 당신과 소통하는 방식입니다. 어떤 값을 전달하면 그 값을 표시합니다. 자동으로 무엇이든 텍스트로 변환합니다.

print()는 각 인자를 str()을 통해 문자열로 변환하고, 구분자(기본값은 공백)로 연결한 후, 그 뒤에 줄바꿈을 붙여서 표준 출력에 씁니다. 기본값을 이해하면 선택 인자의 동작이 더 예측 가능해집니다.

print()의 전체 시그니처는 print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)입니다. *objects 수집자는 원하는 만큼 많은 위치 인자를 받고 각각에 str()을 호출합니다. sepend는 형식을 제어하고, file은 출력을 다른 스트림으로 리다이렉트하며, flush는 버퍼를 즉시 비웁니다.

python
print("Hello")    # Hello
print(42)         # 42
print(3.14)       # 3.14
print(True)       # True

여러 값 출력하기

쉼표로 구분하여 여러 값을 한 번에 출력할 수 있습니다. Python은 기본적으로 그들 사이에 공백을 넣습니다. sep으로 구분자를 바꿀 수 있습니다.

여러 위치 인자는 각각 str()으로 변환되고 sep으로 연결됩니다. 기본 sep은 한 개의 공백입니다. 이를 바꾸면 문자열 연결 없이 형식화된 출력을 만들 수 있습니다.

print()는 한 문자열을 만들고 쓰는 게 아니라 각 인자를 차례로 스트림에 씁니다(str() 호출하면서), 그 사이에 sep을 넣고 마지막에 end를 추가합니다. 이 쓰기들이 원자적인 단일 잠금 작업이 아니라 하나씩 일어나므로, 같은 시간에 실행되는 두 print() 호출(다른 스레드에서)이 출력을 섞을 수 있습니다. 이것이 중요할 때는 한 문자열을 만들고 한 번에 출력하세요. 일상적인 사용에서는 sep을 바꾸는 것이 여전히 손으로 문자열을 붙이는 것보다 낫습니다.

python
name = "Alice"
age = 28
print(name, age)                        # Alice 28
print("Name:", name)                    # Name: Alice
print("2024", "01", "15", sep="-")     # 2024-01-15
print("a", "b", "c", sep=", ")         # a, b, c

줄 끝 제어하기

print() 호출은 기본적으로 줄바꿈으로 끝나므로 다음 출력은 새 줄에서 시작됩니다. end로 이를 바꿀 수 있습니다. end=""로 설정하면 다음 print가 같은 줄을 계속 사용합니다.

end 매개변수는 기본 줄바꿈을 대체합니다. 줄 바꿈을 없애려면 ""로 설정하고, 공백과 함께 같은 줄에 머물려면 " "로 설정하거나, 다른 문자열로 설정할 수 있습니다. sep과 함께 사용하면 문자열을 수동으로 만들지 않고 대부분의 출력 형식을 만들 수 있습니다.

sepend는 키워드 전용입니다(위치 인자가 아니라 이름으로 전달하며, end="" 같은 식). 기본값은 각각 공백과 줄바꿈입니다. end=""는 부분 줄을 인쇄하고 다음 호출이 그것을 계속하도록 하는 표준 방식입니다. 진행 표시기나 라이브 로그처럼 출력이 일어나는 대로 나타나야 할 때는 flush=True를 추가해서 Python이 줄바꿈을 기다리거나 버퍼가 찰 때까지 기다리지 않고 즉시 쓰도록 합니다.

python
print("Loading", end="")
print("...")
# Loading...

print("one", end=" | ")
print("two", end=" | ")
print("three")
# one | two | three
Juno출력하기print()는 주는 것을 무엇이든 텍스트로 바꾸고 표시합니다. 쉼표로 구분된 여러 것을 전달하면 그들 사이에 공백을 얻습니다. sep으로 바꾸세요. 모든 print는 끝에 새 줄로 떨어집니다. end=""는 다음 것을 같은 줄에 유지합니다.
Juno출력하기print()는 각 인자에 str()을 실행하고, 그들을 sep(기본값 공백)으로 연결한 후, end(기본값 줄바꿈)로 끝냅니다. 문자열을 붙이지 않고도 출력을 형식화하려면 둘 중 하나를 무시하세요.
Juno출력하기print()는 각 인자를 str()을 통해 씁니다. 그 사이에 sep, 마지막에 end, 둘 다 키워드 전용. 줄을 계속하려면 end=""를 사용하고, 출력이 즉시 나타나야 할 때는 flush=True를 사용합니다.

f-string으로 출력 형식화하기

메시지를 만드는 가장 깔끔한 방식은 f-string입니다. 열린 따옴표 앞에 f를 붙이고, 모든 변수나 식을 중괄호로 감싸세요. Python이 런타임에 그것을 채워줍니다. {}의 안에는 어떤 값, 계산, 메서드 호출이든 넣을 수 있습니다.

f-string은 {}의 안의 모든 식을 런타임에 평가하고 그 결과를 문자열로 포함합니다. 값 뒤의 콜론은 형식 사양을 시작합니다. 소수 자릿수, 정렬, 숫자 형식을 제어하는 간결한 문법입니다. 이들은 연결보다 빠르고 읽기 쉬우며, 명시적 str() 호출이 필요하지 않습니다.

f-string의 각 {}는 결국 format(value, spec)을 호출하며, 이는 값의 __format__ 메서드(Python이 값을 형식화하기 위해 호출하는 더블 언더스코어 메서드)에 넘깁니다. 따라서 모든 클래스는 __format__을 정의해서 f-string 안에 어떻게 보일지 결정할 수 있습니다. 변환 플래그 !r, !s, !a는 먼저 값에 repr(), str(), ascii()를 실행합니다. !r은 디버깅할 때 기억할 것입니다. 코드처럼 값을 보여주기 때문입니다(따옴표, 이스케이프 등 모두). 아래 실제 예제를 보세요.

python
name = "Alice"
score = 980

# 연결: 투박함, 숫자에 str() 필요
print("Player: " + name + ", Score: " + str(score))

# f-string: 읽기 쉬움, 수동 변환 불필요
print(f"Player: {name}, Score: {score}")

{}의 안에는 어떤 식이든 넣을 수 있습니다. 산술, 메서드 호출(.upper() 같은, 문자열 장에서 자세히 다룸), 형식 사양:

python
price = 49.99
tax = 0.2
total = price * (1 + tax)

print(f"Total: {total:.2f}")          # Total: 59.99
print(f"Name: {name.upper()}")        # Name: ALICE
print(f"2 + 2 = {2 + 2}")             # 2 + 2 = 4

:뒤의 형식 사양은 값이 표시되는 방식을 제어합니다.

python
ratio = 0.8765
count = 1234567
label = "revenue"

print(f"{ratio:.1%}")       # 87.6%
print(f"{count:,}")         # 1,234,567
print(f"{label:>12}")       # "     revenue"

:.2f는 "소수 두 자리"를 의미합니다. 가격과 측정에 계속 사용할 겁니다. 나머지는 필요할 때 있습니다. 주요 점은 변수 이름만이 아니라 어떤 것이든 {}의 안에 들어갈 수 있다는 것입니다.

:.2f:.0%는 대부분의 형식화 필요를 충족합니다. 정렬 지정자(>, <, ^)와 너비는 깔끔한 표 형태 출력을 만듭니다. 일반적인 패턴은 {value:[align][width][.precision][type]}입니다. 부분을 알아보면, 모든 조합을 암기하지 않아도 모든 사양은 읽을 수 있습니다.

같은 사양이 다른 타입에는 다른 의미를 가질 수 있습니다. 각 타입이 자신을 형식화하기 때문입니다. f"{'hi':5}"는 텍스트를 오른쪽에 채웁니다(텍스트는 기본적으로 왼쪽 정렬) 반면 f"{42:5}"는 왼쪽에 채웁니다(숫자는 오른쪽 정렬). 같은 :5, 반대 결과. 각 내장 타입은 이 형식 미니 언어에 대해 자신의 규칙을 가지고 있으며, 커스텀 클래스는 __format__을 정의해서 자신의 규칙을 정합니다.

Junof-string으로 출력 형식화하기 열린 따옴표 앞에 f를 붙이고 뭐든 {}로 감싸세요. 변수, 합, 메서드 호출까지도요. 콜론은 형식을 추가합니다. :.2f 같은 것으로 소수 두 자리를 만들 수 있습니다. +로 문자열을 붙이는 것보다 훨씬 깔끔합니다.
Junof-string으로 출력 형식화하기f"..."는 어떤 식의 결과든 바로 문자열에 떨어뜨립니다. str() 필요 없습니다. 콜론 뒤에는 형식 사양이 옵니다. :.2f, :,, :>12 같은 것들. 연결보다 깔끔하고 빠릅니다.
Junof-string으로 출력 형식화하기{}는 값의 고유한 __format__을 호출하므로, 콜론 뒤의 사양은 그 타입이 결정하는 무엇이든 의미합니다. !r은 기억할 것입니다. 코드처럼 값을 보여주는데, 디버깅할 때 예기치 않은 여백과 다른 보이지 않는 것들을 드러냅니다.

사용자로부터 입력받기

input()은 프로그램을 일시 중지하고 사용자가 뭔가 입력할 때까지 기다립니다. 그들이 입력하고 Enter를 누른 것이 반환 값이 됩니다. 괄호 안의 문자열은 사용자가 보는 프롬프트입니다.

python
name = input("What's your name? ")
print(f"Hello, {name}!")

input()사용자가 뭘 입력하든 항상 문자열을 반환합니다. 42를 입력하면 숫자 42가 아니라 "42"를 얻습니다. 그것으로 산술을 하려면 명시적으로 변환하세요.

python
age = int(input("How old are you? "))
print(f"In ten years you'll be {age + 10}.")

사용자가 변환할 수 없는 걸 입력하면? Python은 ValueError를 발생시킵니다. 이를 제대로 처리하는 것은 파일과 예외 장에서 다룹니다.

input()은 프롬프트를 표준 출력에 쓰고, 표준 입력에서 한 줄을 읽고, 맨 뒤의 줄바꿈을 제거하고, 결과를 문자열로 반환합니다. 타입 추론은 없습니다. 터미널의 모든 것은 텍스트로 도착합니다. 필요한 타입은 경계에서 명시적으로 변환해서 선언합니다.

python
name = input("What's your name? ")
age = int(input("How old are you? "))

이 패턴(텍스트 받기, 필요한 타입으로 변환하기)은 외부 데이터가 도착하는 모든 곳에 적용됩니다. int(), float(), str()이 변환 도구입니다. 문자열을 변환할 수 없으면 Python은 ValueError를 발생시키며, 이는 파일과 예외 장에서 다룹니다.

input()은 얇은 래퍼입니다. sys.stdout(표준 출력)에 프롬프트를 쓰고, 플러시해서 프로그램이 블록되기 전에 프롬프트가 나타나도록 하고, sys.stdin(표준 입력)에서 한 줄을 읽고, 맨 뒤의 줄바꿈을 제거하고, str을 반환합니다. 설계상 항상 str입니다. Python은 "42"가 정수, 부동소수점, 아니면 리터럴 텍스트를 의도했는지 추측할 수 없습니다. 그래서 경계에서 변환합니다. Python I/O의 기본 형태가 이겁니다. 외부 데이터는 텍스트로 도착하고, 당신의 코드가 진입점에서 그 타입을 결정합니다.

python
name = input("Enter your name: ")
score = float(input("Enter your score (0.0 to 1.0): "))

print(f"Name:  {name!r}")      # !r는 보이지 않는 여백을 드러냅니다
print(f"Score: {score:.1%}")
Juno사용자로부터 입력받기input()은 프롬프트를 보여주고, 줄을 기다리고, 항상 숫자를 입력했어도 string으로 반환합니다. 숫자가 필요하세요? int()float()으로 들어올 때 변환해서 올바른 타입으로 계속하세요.
Juno사용자로부터 입력받기input()은 항상 str을 반환합니다. 타입 추론은 없습니다. 경계에서 int()float()으로 변환하고, 텍스트를 파싱할 수 없으면 ValueError를 예상하세요. 그 받기-그-다음-변환하기 패턴은 외부 데이터가 나타나는 모든 곳에 나타납니다.
Juno사용자로부터 입력받기input()에서 나오는 모든 것은 설계상 str입니다. Python이 뭘 의도했는지 추측할 수 없습니다. 진입점에서 변환하고 파싱할 수 없을 때 ValueError를 처리하세요. 외부 데이터가 들어오는 다른 모든 곳과 같은 형태입니다.

stderr에 쓰기

기본적으로 print()표준 출력에 씁니다. 터미널에 나타나는 스트림이며 파이프로 흐릅니다. Python은 또한 표준 에러가 있는데, 진단과 경고용 별개의 스트림입니다. 터미널에서는 동일하게 보이지만 구분됩니다. 스크립트의 출력을 다른 명령으로 파이핑하면 stdout만 통과합니다. stderr는 항상 터미널에 도달합니다.

stderr에 쓰려면 print()file 인자를 사용합니다. 이는 모듈 장에서 다루는 sys 가져오기가 필요합니다. 지금은 두 스트림이 존재하고 왜 구분되는지 아는 것으로 충분합니다.

실제로

사용자 입력으로 자신을 개인화하는 퀴즈:

python
name = input("What's your name? ")
subject = input("Which subject? ")

print(f"Okay, {name}. Starting your {subject} quiz.")
print("Good luck!")

두 입력 모두 문자열로 돌아와 f-string에 바로 들어갑니다. 숫자가 아니라 텍스트로 사용하고 있으므로 변환이 필요 없습니다.

정렬된 표 형태 출력을 하는 온도 변환기:

python
celsius = float(input("Temperature in Celsius: "))
fahrenheit = celsius * 9 / 5 + 32

print(f"{'Celsius':>12} {'Fahrenheit':>12}")
print(f"{celsius:>12.1f} {fahrenheit:>12.1f}")

float()는 사용자로부터 정수와 소수를 모두 처리합니다. :>12 사양은 숫자가 몇 자리든 열이 정렬되게 유지합니다. 100-40을 입력해보세요. 어느 쪽이든 출력은 정갈합니다.

Python이 정확히 받은 것을 표시하려면 !r을 사용하세요. 출력이 이상할 때 유용합니다.

python
name = input("Enter your name: ")
score = float(input("Enter your score (0.0 to 1.0): "))

print(f"Name:   {name!r}")
print(f"Score:  {score!r}")
print()
print(f"Result: {name}: {score:.1%}")

입력이 깨끗할 때 {name}{name!r}은 동일하게 표시됩니다. 맨 뒤 공백이나 다른 보이지 않는 문자가 있을 때 차이가 나타납니다. 디버깅할 때 !r을 습관화하면 예기치 않은 값이 즉시 보입니다.