return과 yield는 Python에서 함수의 동작 방식에 큰 차이를 만드는 두 키워드입니다.
이 두 키워드의 차이점과 언제 사용하는지에 대한 이해를 하기 위하여 아래와 같은 구조로 설명할 수 있습니다.
Python에서 return과 yield는 함수의 실행을 제어하는데 사용되지만, 그 방식과 결과는 크게 다릅니다.
return과 yield의 차이점을 살펴보겠습니다.
1. return의 동작 방식
• 정의: return은 함수에서 값을 반환하고, 함수의 실행을 종료하는 키워드입니다.
함수가 return을 만나면, 그 즉시 실행이 종료되고 값이 호출자에게 반환됩니다.
• 용도: 보통 함수가 어떤 결과값을 계산하고 그 값을 반환할 때 사용됩니다. 그 값은 단순히 반환된 결과로만 활용됩니다.
• 예시:
def add(a, b):
return a + b
result = add(3, 5)
print(result) # 출력: 8
이처럼 return을 만나면 즉시 결과값이 반환되고 종료됩니다.
2. yield의 동작 방식
• 정의: yield는 함수에서 값을 반환하는 것처럼 보이지만, 함수의 실행을 잠시 멈추고 해당 지점까지의 상태를 기억합니다.
함수는 yield를 만나면 중간에 멈추고 그 시점까지의 값을 호출자에게 반환하지만, 함수의 실행이 종료되지 않고, 다시 호출되었을 때 이전 상태에서 계속 실행을 이어갑니다. 즉, 호출자에게 제어권을 넘겨준다고 생각하시면 됩니다.
• 용도: yield는 데이터를 한 번에 하나씩 생성하거나 반복할 때 유용합니다. 주로 제너레이터(generator)를 만들 때 사용됩니다.
예시:
def yield_test():
for i in range(5):
yield i
# 함수 내에서 요소에 해당하는 부분을 yield 에 넣으면 됨
print(i,'번째 호출!')
print(type(yield_test())) # <class 'generator'>
# __next__() 함수를 통해 출력
t = yield_test()
print(t.__next__()) # 0
print(t.__next__()) # 0 번째 호출! 1
print(t.__next__()) # 1 번째 호출! 2
print(t.__next__()) # 2 번째 호출! 3
print(t.__next__()) # 3 번째 호출! 4
# print(t.__next__()) #Error
# 함수를 완전 실행시키는 것이 아니라 일부를 실행시키고 일시 정지 -> 호출한 쪽으로 제어권 전달
range(5)를 주었기 때문에 __next__()를 호출하면 yield에서 0을 반환하고 함수가 멈춘 상태로 대기합니다.
두 번째 호출 시, yield 다음 줄부터 실행이 재개되며 print() 문에서 "0 번째 호출!"을 출력한 뒤, yield에서 1을 반환하고 다시 대기합니다.
이 과정을 반복하며, 마지막(i=4)에서 "3 번째 호출!"을 출력한 뒤 yield에서 4를 반환하고 대기합니다.
즉, 제어권을 yield에게 주어 print가 다음으로 찍히지 않습니다. 이 후 한번 더 호출하면 for 루프가 끝나면서 함수가 종료되고
StopIteration 예외가 발생합니다.
출력:
0
0 번째 호출!
1
1 번째 호출!
2
2 번째 호출!
3
3 번째 호출!
4
그러나, 다음은 for문에서 제너레이터를 사용할때의 동작방식입니다.
#for문을 이용하여 출력
print("<-->")
for k in yield_test():
print(k)
여기서 4번째 출력까지 출력되는 이유는 k에 값을 할당하고 print문으로 출력하기 때문입니다. yield가 실행된 후 대기하지 않고 k에 값을 할당하며 바로 print(k)가 출력된다는 내용입니다.
출력:
<-->
0
0 번째 호출!
1
1 번째 호출!
2
2 번째 호출!
3
3 번째 호출!
4
4 번째 호출!
3. return과 yield의 주요 차이점 정리
return | yield | |
동작 방식 | 값을 반환하고 함수 종료 | 값을 반환하지만 함수는 종료되지 않음 |
용도 | 하나의 값 또는 결과를 반환 | 반복적으로 값을 생성하는 데 사용 |
반환 값 | 반환된 값을 단순히 받음 | 제너레이터 객체를 반환 |
기억되는 상태 | 함수 실행이 종료되므로 상태 저장 안됨 | 함수의 상태(변수 값 등)가 기억됨 |
성능 | 한 번에 전체 값 반환 | 메모리 효율적 (필요한 값만 반환) |
4. 결론
• 화자는 주로 return을 사용하지만 yield는 주로 큰 데이터를 다룰때 사용하면 메모리 절약에 좋습니다.
예를 들어서 100만개의 데이터를 사용한다고 할때 한꺼번에 큰 데이터를 메모리에 올리면 속도와 컴퓨팅(자원)비용 이슈가 있을 수 있기 때문에 필요한 숫자를 한번에 하나씩 생성해서 메모리 관리에 쓸때 사용하면 좋은 yield를 활용합니다.
파일 read나 API 응답 같은 데이터 스트리밍을 처리할때도 yield를 사용하면 좋습니다.
# readlines() 사용 예시 (비효율적)
with open("big_file.txt", "r") as f:
lines = f.readlines() # 파일 전체를 메모리에 읽어들임
# 모든 줄을 한꺼번에 메모리에 올리니까 큰 파일에는 메모리 문제 발생 가능
def read_large_file(file_path):
with open(file_path, "r") as f:
for line in f:
yield line.strip() # 한 줄씩 반환
for line in read_large_file("big_file.txt"):
print(line) # 한 줄씩 출력 (메모리 부담 없음)
Ref.