아래 코드에서 1번과 2번의 결과값을 예상할 수 있나요?
import sys
n = int(sys.stdin.readline().rstrip())
print(*[f"Hello World, Judge {x}!\n" for x in range(n)]) # 1번
print(*[f"Hello World, Judge {x}!" for x in range(n)],sep="\n") # 2번
3
2
1
...❗❗
두 결과값이 같다고 예상하신다면 글을 끝까지 보는 걸 추천드립니다! 👀
정답은 서로 다른 결과값을 출력하는데요,
# 1번
Hello World, Judge 0!
Hello World, Judge 1!
Hello World, Judge 2!
Hello World, Judge 3!
Hello World, Judge 4!
# 2번
Hello World, Judge 0!
Hello World, Judge 1!
Hello World, Judge 2!
Hello World, Judge 3!
Hello World, Judge 4!
왜 1번 결과는 두 번째 줄부터 앞에 공백이 포함될까요? 코드에서 사용한 개념들부터 모두 찬찬히 살펴보도록 하겠습니다.
⭐️ 왜 다른지에 대한 이유만이 궁금하다면 개념 설명은 스킵하셔도 됩니다 ⭐️
[input() vs sys.stdin.readline().rstrip()]
input() 함수와 sys.stdin.readline().rstrip()의 공통점은 '사용자로부터 입력을 받는다'는 것인데... 차이점은 크게 2가지가 있습니다.
속도 측면
input() 함수는 내부적으로 sys.stdin.readline()을 호출하고 추가적으로 앞과 뒤 공백을 제거하는 strip() 함수를 호출합니다.
sys.stdin.readline().rstrip()은 입력받은 line의 맨 오른쪽 개행 문자(\n)만을 제거합니다.
그렇기 때문에 속도 측면에서 차이가 발생하고 sys.stdin.readline().rstrip()가 더 효율적입니다 👍
개행 문자 처리
input() 함수는 자동으로 개행 문자를 제거하지만, sys.stdin.readline()은 자동으로 개행 문자를 제거하지 않습니다. 그래서 rstrip() 메서드를 사용해서 수동으로 제거하는 차이가 있습니다.
[리스트 컴프리핸션(List Comprehension)]
리스트 컴프리핸션(List Comprehension)은 리스트를 간결하게 생성하는 방법입니다.
일반화한다면,
[expression for item in iterable]
으로 표현할 수 있습니다.
📌 expression(표현식): 리스트에 추가될 새로운 요소를 정의하는 부분입니다. item에 대해 재정의할 수 있습니다.
📌 for item in iterable(반복문): iterable에서 각각의 요소를 순회하면서 item에 현재 요소의 값을 할당합니다. 이때, iterable은 말 그대로 반복가능한 리스트, 튜플, 문자열에 해당합니다.
예시로 1부터 10까지 짝수를 저장하는 로직은 다음과 같습니다.
even_numbers = []
for x in range(1, 11):
if x % 2 == 0:
even_numbers.append(x)
print(even_numbers) # [2, 4, 6, 8, 10]
list comprehension 문법을 적용하면,
even_numbers = [x for x in range(1, 11) if x % 2 == 0]
print(even_numbers) # [2, 4, 6, 8, 10]
으로 간결하게 생성할 수 있습니다.
[print 함수에서 list comprehension 사용]
자 그럼... 설명하기 위한 기본 문법을 알았으니 1번 코드만 다시 코드를 보겠습니다..!
import sys
n = int(sys.stdin.readline().rstrip())
print(*[f"Hello World, Judge {x}!\n" for x in range(n)]) # 1번
print 함수는 기본적으로 전달된 각 인자 사이에 공백을 추가하여 출력합니다. 예를 들어, print("Hello", "World")는 "Hello World"출력하게 됩니다.
* 연산자는 iterable인 리스트를 unpacking 하여 print 함수에 개별적인 인자로 전달합니다.
print("Hello", "World")는 print(*["Hello", "World"])와 같은 의미임을 알 수 있죠.
그런데 1번에서는 f-string에서 개행 문자가 포함되어 있죠? 그럼 list comprehension은 n에 따라 여러 개의 문자열을 생성하고, 각 문자열 끝에는 개행 문자가 추가됩니다. 그 후 * 연산자는 이 리스트의 각 문자열을 print 함수에 인자로 전달하게 되는데 print 함수는 이 인자들 사이에 공백을 추가하여 출력합니다.
Hello World, Judge 0!(\n)
(공백)Hello World, Judge 1!(\n)
(공백)Hello World, Judge 2!(\n)
(공백)Hello World, Judge 3!(\n)
(공백)Hello World, Judge 4!(\n)
그래서 두 번째 출력부터는 앞에 공백이 포함되어 있는 것입니다! 개행 문자로 인해 새로운 줄로 이동했음에도 불구하고 print 함수는 자동으로 인자들 사이에 공백을 추가하려고 하기 때문에 앞에 불필요한 공백이 하나씩 생기게 된 것입니다.
결론적으로... print 함수는 전달된 인자들 사이에 공백을 추가하기 때문에, sep 매개변수가 디폴트로 공백이기 때문에,
sep에 "\n"을 지정하면 2번처럼 원하는 결과를 도출할 수 있습니다.
print(*[f"Hello World, Judge {x}!" for x in range(n)],sep="\n") # 2번
python 문법 공부할 겸 아래 문제를 풀면서 궁금했던 점을 해결하는 시간을 가질 수 있었습니다 😀
https://www.acmicpc.net/problem/9316