[Python 기초]

[Python] Iterator 와 반복 가능한 객체, Generator 정리

dyk98 2024. 11. 27. 19:33

1. 반복 가능한 객체 (Iterable)

  • 반복 가능한 객체(Iterable): for 루프에서 반복할 수 있는 객체.
  • 예: 리스트, 튜플, 문자열, 딕셔너리, 세트.
  • 내부적으로 __iter__() 메서드를 제공하여 이터레이터를 반환.

코드 예시

numbers = [1, 2, 3, 4]
for num in numbers:
    print(num)

 

2. 이터레이터 (Iterator)

  • 이터레이터(Iterator): 반복 가능한 객체의 요소를 하나씩 꺼내오는 객체.
  • __iter__(): 이터레이터 객체 자신을 반환.
  • __next__(): 다음 요소를 반환하며, 더 이상 요소가 없으면 StopIteration 예외 발생.

코드 예시

numbers = [1, 2, 3]
iterator = iter(numbers)  # 리스트에서 이터레이터 생성

print(next(iterator))  # 1
print(next(iterator))  # 2
print(next(iterator))  # 3

자연수 생성 이터레이터

class NaturalNumbers:
    def __init__(self, limit):
        self.current = 1
        self.limit = limit

    def __iter__(self):
        return self

    def __next__(self):
        if self.current <= self.limit:
            result = self.current
            self.current += 1
            return result
        raise StopIteration

# 1부터 10까지의 자연수를 출력
for num in NaturalNumbers(10):
    print(num)

 

3. 제너레이터 (Generator)

  • Generator: 이터레이터를 생성하는 함수.
  • yield 키워드를 사용하여 값을 반환하며, 함수의 실행 상태를 유지.
  • 메모리를 효율적으로 사용하며, 필요할 때 값을 생성(lazy evaluation).

특징

  • Generator 함수는 호출될 때 객체를 반환.
  • yield가 호출될 때까지 실행되고, 다음 호출 시 이전 상태에서 다시 시작.

코드 예시

def even_numbers(limit):
    num = 2
    while num <= limit:
        yield num
        num += 2

# 2부터 20까지의 짝수를 출력
for even in even_numbers(20):
    print(even)

무한 피보나치 수열 Generator

def infinite_fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# 피보나치 수열의 처음 10개 항 출력
fib_gen = infinite_fibonacci()
for _ in range(10):
    print(next(fib_gen))

4. Generator표현식

  • :List comprehension과 유사하지만 [] 대신 ()를 사용하여 생성.
  • 메모리를 효율적으로 사용하며, 값을 필요할 때 계산.

코드 예시

gen_exp = (x ** 2 for x in range(5))
for num in gen_exp:
    print(num)  # 0, 1, 4, 9, 16

새로운 제너레이터 표현식 예시

제곱수 생성

squared_numbers = (x ** 2 for x in range(1, 11))
for square in squared_numbers:
    print(square)

문자열 길이 구하기

strings = ["apple", "banana", "cherry", "date"]
string_lengths = (len(s) for s in strings)

for length in string_lengths:
    print(length)

Generator 와 Iterator 비교 예시

List 대신 Iterator와 Generator를 활용한 계산

# 리스트로 구현 (메모리 사용 높음)
squares_list = [x ** 2 for x in range(1, 1001)]
print(squares_list[:10])  # 처음 10개 출력

# 제너레이터로 구현 (메모리 사용 적음)
squares_gen = (x ** 2 for x in range(1, 1001))
for _ in range(10):
    print(next(squares_gen))

실용적인 Generator 예시

대용량 로그 파일 처리

def read_large_file(file_path):
    with open(file_path, "r") as file:
        for line in file:
            yield line.strip()

# 사용 예시: 대용량 로그 파일에서 'ERROR'가 포함된 줄 출력
for log in read_large_file("large_log_file.txt"):
    if "ERROR" in log:
        print(log)

시간 단위로 데이터 생성

import time

def live_data_stream():
    while True:
        yield time.ctime()  # 현재 시간을 반환
        time.sleep(1)  # 1초 대기

# 5초 동안 실시간 시간 데이터 출력
for _ in range(5):
    print(next(live_data_stream()))

5. Generator 와 Iterator 의 장점

  1. 메모리 효율성
  • 데이터를 미리 생성하지 않고 필요할 때만 생성.
  • 큰 데이터를 처리하거나 무한 반복에 유용.
  1. 느린 계산(계산 지연)
  • 값을 즉시 계산하지 않고 호출 시 계산.
  1. 코드 간결화
  • 복잡한 반복 작업을 간단하게 작성 가능.
Iterable 반복 가능한 객체. 예: 리스트, 문자열, 딕셔너리. 내부적으로 iter() 메서드를 가짐.
Iterator 반복 가능한 객체에서 요소를 하나씩 꺼내는 객체. iter(), next() 메서드를 가짐.
Generator yield 키워드를 사용하여 값을 하나씩 반환하는 함수. 메모리 효율이 높음.
Generator Expression 리스트 컴프리헨션과 유사한 구문으로 제너레이터를 생성. () 사용.

Decorator 와 with 구문

데코레이터 (Decorator)

  1. 정의: 함수나 메서드에 추가 기능을 부여하는 함수.
  2. 사용 방법: @decorator_name으로 적용.
  3. 장점:
    • 재사용성: 여러 함수에 동일한 기능 적용.
    • 가독성: 코드가 더 간결하고 읽기 쉬움.
    • 중복 제거: 반복되는 코드 패턴을 제거.
  4. 활용 예시:
    • 함수 실행 시간 측정
    • 사용 권한 확인
    • 로그 기록 추가

함수 실행 시간을 측정하는 Decorator

import time

def time_logger(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function '{func.__name__}' executed in {end_time - start_time:.4f} seconds")
        return result
    return wrapper

@time_logger
def calculate_sum(n):
    return sum(range(1, n+1))

# 함수 호출
print(calculate_sum(1000000))

사용 권한을 확인하는 Decorator

def authorize(func):
    def wrapper(user, *args, **kwargs):
        if user != "admin":
            print("Access denied. Only 'admin' can execute this function.")
            return
        return func(user, *args, **kwargs)
    return wrapper

@authorize
def secure_function(user):
    print(f"Welcome, {user}. You have access!")

# 함수 호출
secure_function("admin")  # Access granted
secure_function("guest")  # Access denied

반환값을 변경하는 Decorator

def double_return(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result * 2
    return wrapper

@double_return
def get_number():
    return 5

print(get_number())  # 10