파이썬 기초

파이썬 - 데코레이터, 이터레이터

데이터_박과장 2023. 10. 31. 21:48

time.time() 함수를 사용하여 프로그램 실행 시간을 측정하는 데코레이터를 만들 수 있습니다. 데코레이터는 어떤 함수가 호출될 때 그 함수를 감싸서 추가적인 기능을 제공합니다. 이 경우, 데코레이터는 특정 함수의 실행 전후 시간을 측정하여 실행 시간을 계산합니다.

실행 시간 측정 데코레이터 예제

 

import time

def elapsed(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} 함수 실행 시간: {end_time - start_time:.6f}초")
        return result
    return wrapper

# 예제 함수에 데코레이터 적용
@elapsed
def example_function():
    # 임시로 시간 지연을 위한 코드
    for _ in range(1000000):
        pass

example_function()  # 함수 실행 및 시간 측정



이 코드에서 elapsed 데코레이터는 어떤 함수든 감쌀 수 있습니다. example_function 함수에 @elapsed를 적용하면, 해당 함수가 호출될 때마다 실행 시간이 측정되고 출력됩니다. wrapper 함수는 원래 함수를 호출하고, 호출 전후의 시간을 time.time()을 사용하여 측정합니다.

이러한 데코레이터는 함수의 성능을 측정하거나 디버깅할 때 유용하게 사용됩니다.


여기서  *args와 **kwargs를 사용하는 주된 이유는 함수가 받을 수 있는 매개변수의 수와 종류가 불특정할 때 유연성을 제공하기 위함입니다. 이들은 데코레이터와 같이 여러 종류의 함수에 적용되는 함수에서 특히 유용합니다. 각각의 역할을 간단히 설명하겠습니다:

*args (* 뒤에 다른 이름을 사용할 수도 있습니다):

이는 위치에 기반한 가변 인자 목록을 처리하는 데 사용됩니다.
함수에 전달되는 위치 인자들을 튜플로 묶어서 함수 내부에서 사용할 수 있게 해줍니다.


**kwargs (** 뒤에 다른 이름을 사용할 수도 있습니다):

이는 키워드에 기반한 가변 인자 목록을 처리하는 데 사용됩니다.
함수에 전달되는 키워드 인자들을 사전으로 묶어서 함수 내부에서 사용할 수 있게 해줍니다.


데코레이터의 경우, 다양한 종류의 함수에 적용될 수 있어야 하기 때문에 *args와 **kwargs를 사용하는 것이 일반적입니다. 이렇게 함으로써 어떤 함수가 주어지든, 그 함수의 인자 수나 타입에 관계없이 데코레이터가 제대로 작동하도록 할 수 있습니다. 예를 들어, 인자가 없는 함수, 위치 인자를 사용하는 함수, 키워드 인자를 사용하는 함수 등 어떤 함수에도 데코레이터를 적용할 수 있습니다.

 

 

 

이터레이터(Iterator)란?


이터레이터는 파이썬에서 반복 가능한 객체(예: 리스트, 튜플, 집합, 사전 등)의 요소를 순회하는 객체입니다. 이터레이터는 __next__() 메소드를 사용하여 컬렉션의 다음 요소를 반환합니다. 모든 요소가 소진되면, StopIteration 예외를 발생시켜 순회의 끝을 알립니다.

이터레이터의 필요성
for 루프는 파이썬의 반복 가능한 객체를 순회할 때 매우 편리합니다. 그러나 경우에 따라서는 좀 더 세밀한 제어가 필요할 수 있습니다. 예를 들어, 반복 중에 다음 요소로 건너뛰거나 특정 조건에서 반복을 중단하고 싶을 수 있습니다. 이때 이터레이터를 사용하면 루프의 내부 메커니즘을 더 잘 제어할 수 있습니다.

이터레이터 예제 코드
아래 코드는 기본 리스트를 이터레이터로 변환하고, 수동으로 순회하는 예제입니다.

my_list = [1, 2, 3, 4, 5]

# 리스트를 이터레이터로 변환
my_iter = iter(my_list)

# 이터레이터 순회
try:
    while True:
        item = next(my_iter)
        print(item)
except StopIteration:
    print("반복이 끝났습니다.")


이 예제에서 iter() 함수는 리스트를 이터레이터로 변환하고, next() 함수는 이터레이터의 다음 요소를 반환합니다. 모든 요소가 순회되고 나면 StopIteration 예외가 발생하여 반복이 종료됩니다.

for 루프와의 비교
for 루프는 내부적으로 이터레이터를 사용하여 컬렉션을 순회합니다. 즉, for 루프는 이터레이터의 개념을 숨긴 채, 보다 간결하고 읽기 쉬운 방식으로 같은 작업을 수행합니다. 직접 이터레이터를 사용하는 것은 보다 낮은 수준에서 반복 제어가 필요한 경우에 유용할 수 있습니다.

 

 

이터레이터를 사용하는 몇 가지 일반적인 시나리오

  • 세밀한 반복 제어가 필요한 경우: 이터레이터를 사용하면 순회 과정에서 다양한 조건에 따라 제어 로직을 적용할 수 있습니다. 예를 들어, 특정 조건에서 반복을 일시 중지하거나 건너뛰는 등의 동작을 구현할 수 있습니다.
  • 대용량 데이터 처리: 대용량 데이터를 처리할 때 모든 데이터를 한 번에 메모리에 로드하는 것은 비효율적일 수 있습니다. 이터레이터를 사용하면 데이터를 조금씩 읽어 처리할 수 있어 메모리 사용을 최적화할 수 있습니다.
  • 사용자 정의 반복 로직 구현: 복잡한 자료구조나 사용자 정의 객체에 대한 반복 로직을 구현할 때 이터레이터를 사용합니다. 클래스에 __iter__와 __next__ 메소드를 구현하여 이터레이터 프로토콜을 지원하도록 만들 수 있습니다.
  • 함수형 프로그래밍 스타일: 이터레이터는 함수형 프로그래밍 스타일과 잘 어울립니다. 예를 들어, map, filter, reduce와 같은 고차 함수와 함께 사용하여 효율적인 데이터 처리 파이프라인을 구축할 수 있습니다.


예시 코드: 대용량 데이터 처리
아래 예제에서는 파일을 한 줄씩 읽으면서 처리하는 경우를 보여줍니다. 이 방식은 파일의 크기가 매우 클 때 유용합니다.

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

# 파일 이터레이터 생성
log_file_iterator = file_reader('example_log_file.txt')

# 첫 5줄만 읽어 출력
for _ in range(5):
    try:
        print(next(log_file_iterator))
    except StopIteration:
        print("파일 끝에 도달했습니다.")
        break



이 코드에서 file_reader 함수는 제너레이터를 사용하여 파일의 각 줄을 하나씩 읽어 반환합니다. 이렇게 하면 파일 전체를 한 번에 메모리에 로드할 필요가 없으므로 메모리 효율성이 높아집니다.

참고
이터레이터가 한 번 소진되면 재사용할 수 없다는 점을 유의해야 합니다. 따라서 같은 데이터를 다시 반복하고 싶을 때는 이터레이터를 새로 생성하거나 다른 반복 가능한 객체를 사용해야 합니다.

'파이썬 기초' 카테고리의 다른 글

파이썬 기초 - 타입 어노테이션  (0) 2023.10.31
파이썬 기초 - 정규표현식  (0) 2023.10.31
파이썬 기초 - 클로저  (0) 2023.10.31
파이썬 주의사항  (0) 2023.10.16
파이썬 내장함수 모음  (0) 2023.10.11