Programming/python

python raise, yield, return의 차이

moxie2ks 2026. 2. 20. 18:01
728x90
반응형

개요

Python에서 함수의 흐름을 제어하는 키워드는 크게 세 가지로 나뉜다. return은 함수의 실행을 종료하고 값을 반환하며, yield는 함수를 제너레이터로 변환하여 값을 순차적으로 생성하고, raise는 예외를 명시적으로 발생시킨다. 이 세 키워드는 각각 다른 목적과 동작 방식을 가지며, Python 프로그래밍의 핵심 제어 흐름을 구성한다.

설명

return

return은 함수 실행을 즉시 종료하고 호출자에게 값을 반환한다. return 이후의 코드는 실행되지 않으며, 반환값이 없을 경우 None을 반환한다. 함수는 return 실행 후 완전히 종료되어 내부 상태가 소멸된다.

yield

yield는 함수를 제너레이터(Generator) 함수로 변환한다. yield를 포함한 함수는 호출 시 제너레이터 객체를 반환하며, next()가 호출될 때마다 yield 지점까지 실행 후 값을 반환하고 일시 정지(suspend)된다. 함수의 내부 상태(로컬 변수, 실행 위치)는 유지되며, 다음 next() 호출 시 중단된 지점부터 재개된다.

raise

raise는 예외(Exception)를 명시적으로 발생시킨다. 내장 예외 클래스나 사용자 정의 예외를 발생시킬 수 있으며, 예외가 발생하면 현재 실행 흐름이 중단되고 가장 가까운 except 블록으로 제어가 이동한다. 예외가 처리되지 않으면 프로그램이 종료된다.


특징

구분 return yield raise
목적 값 반환 및 함수 종료 값 생성 (제너레이터) 예외 발생
함수 상태 소멸 유지 (일시 정지) 소멸 (예외 전파)
반환 타입 임의의 값 또는 None Generator 객체 없음 (제어 흐름 변경)
반복 사용 1회 다회 예외 처리 전까지
메모리 즉시 해제 Lazy evaluation (지연 평가) -
주요 사용처 일반 함수 대용량 데이터 순회, 무한 시퀀스 오류 처리, 유효성 검사

추가적으로 yield는 Python 3.3부터 도입된 yield from 문법을 통해 하위 제너레이터에 위임할 수 있으며, raise 구문은 raise ExceptionType from original_exception 형태로 예외 체이닝(exception chaining)을 지원한다.

예제

return 예제

def add(a: int, b: int) -> int:
    return a + b  # 함수 종료 및 값 반환

result = add(3, 5)
print(result)  # 출력: 8

yield 예제

def fibonacci():
    a, b = 0, 1
    while True:
        yield a          # 실행을 일시 정지하고 a를 반환
        a, b = b, a + b  # 다음 호출 시 이 지점부터 재개

gen = fibonacci()
for _ in range(7):
    print(next(gen), end=" ")
# 출력: 0 1 1 2 3 5 8
# yield from 예제
def flatten(nested):
    for sublist in nested:
        yield from sublist  # 하위 이터러블에 위임

data = [[1, 2], [3, 4], [5, 6]]
print(list(flatten(data)))  # 출력: [1, 2, 3, 4, 5, 6]

raise 예제

def divide(a: float, b: float) -> float:
    if b == 0:
        raise ValueError("0으로 나눌 수 없음")  # 명시적 예외 발생
    return a / b

try:
    print(divide(10, 0))
except ValueError as e:
    print(f"오류 발생: {e}")
# 출력: 오류 발생: 0으로 나눌 수 없음
# 예외 체이닝 예제
def load_config(path: str):
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError as e:
        raise RuntimeError("설정 파일 로드 실패") from e  # 원인 예외 보존

try:
    load_config("nonexistent.yaml")
except RuntimeError as e:
    print(e)
    print(f"원인: {e.__cause__}")

세 키워드 비교 예제

def return_example():
    return [i for i in range(5)]  # 리스트 전체를 메모리에 생성 후 반환

def yield_example():
    for i in range(5):
        yield i  # 필요할 때마다 하나씩 생성

def raise_example(value):
    if value < 0:
        raise ValueError(f"음수 입력 불가: {value}")
    return value

# return: 리스트 전체가 메모리에 올라감
print(return_example())  # [0, 1, 2, 3, 4]

# yield: 지연 평가, 메모리 효율적
for v in yield_example():
    print(v, end=" ")  # 0 1 2 3 4

# raise: 예외 상황 처리
try:
    raise_example(-1)
except ValueError as e:
    print(e)  # 음수 입력 불가: -1

결론

return, yield, raise는 모두 함수의 실행 흐름을 변경하지만 그 목적과 동작 방식이 명확히 구분된다. return은 함수를 완전히 종료하고 값을 반환하는 가장 기본적인 메커니즘이다. yield는 함수 상태를 유지하면서 값을 순차적으로 생성하는 제너레이터 패턴을 구현하며, 특히 대용량 데이터 처리나 무한 시퀀스 구현 시 메모리 효율성 측면에서 return에 비해 우위를 가진다. raise는 예외를 명시적으로 발생시켜 오류 상황을 제어 흐름으로 처리하는 Python의 예외 처리 메커니즘의 핵심이다. 세 키워드의 특성을 정확히 이해하고 상황에 맞게 활용하는 것이 Python 코드의 가독성과 안정성을 높이는 데 핵심적이다.

참고문헌

728x90
반응형