프로그래밍/Python

파이썬 고급 자료구조: 리스트 컴프리헨션, 제너레이터, 데코레이터 활용법

shimdh 2025. 2. 21. 10:31
728x90

파이썬은 간결하고 효율적인 코드 작성을 위해 다양한 고급 자료구조와 기능을 제공합니다. 이번 포스트에서는 리스트 컴프리헨션, 제너레이터, 데코레이터라는 세 가지 강력한 도구를 깊이 있게 살펴보고, 각각의 개념과 활용 방법을 예제와 함께 설명하겠습니다. 이 세 가지 기능은 파이썬 프로그래밍에서 매우 중요한 역할을 하며, 특히 대규모 데이터 처리, 코드 재사용성 향상, 메모리 효율성 증대 등에 큰 도움을 줍니다. 이 글에서는 각 주제를 더욱 상세히 다루고, 추가적인 예제와 활용 사례를 통해 이해를 돕겠습니다.


1. 리스트 컴프리헨션 (List Comprehension)

리스트 컴프리헨션은 파이썬에서 리스트를 간결하고 효율적으로 생성하는 방법입니다. 기존의 for 루프보다 코드가 짧고 가독성이 높아 자주 사용됩니다. 리스트 컴프리헨션은 단순한 리스트 생성부터 조건부 필터링, 중첩된 반복문 처리까지 다양한 상황에서 활용할 수 있습니다.

1.1 기본 구조

new_list = [expression for item in iterable if condition]
  • expression: 새 리스트의 각 요소를 정의하는 식입니다.
  • item: 반복 가능한 객체(iterable)에서 가져온 요소입니다.
  • condition (선택 사항): 특정 조건을 만족하는 경우에만 요소를 포함합니다.

1.2 예제: 기본 사용법

squares = [x**2 for x in range(10)]
print(squares)

출력:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  • range(10)에서 각 숫자 x의 제곱값을 계산하여 새로운 리스트를 생성합니다.

1.3 예제: 조건부 사용법

even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)

출력:

[0, 4, 16, 36, 64]
  • 짝수인 경우에만 제곱값을 리스트에 포함합니다.

1.4 예제: 중첩된 리스트 컴프리헨션

colors = ['red', 'green', 'blue']
sizes = ['S', 'M', 'L']

combinations = [(color + size) for color in colors for size in sizes]
print(combinations)

출력:

['redS', 'redM', 'redL', 'greenS', 'greenM', 'greenL', 'blueS', 'blueM', 'blueL']
  • 두 리스트의 모든 조합을 생성합니다.

1.5 예제: 딕셔너리 컴프리헨션

리스트 컴프리헨션과 유사하게 딕셔너리도 컴프리헨션을 사용하여 생성할 수 있습니다.

numbers = [1, 2, 3, 4, 5]
squared_dict = {x: x**2 for x in numbers}
print(squared_dict)

출력:

{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
  • 숫자를 키로, 그 제곱값을 값으로 하는 딕셔너리를 생성합니다.

1.6 예제: 중첩된 조건문 사용

리스트 컴프리헨션에서 여러 조건을 적용할 수도 있습니다.

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filtered_numbers = [x for x in numbers if x % 2 == 0 if x > 5]
print(filtered_numbers)

출력:

[6, 8, 10]
  • 짝수이면서 5보다 큰 숫자만 필터링합니다.

1.7 예제: 리스트 컴프리헨션과 함수 조합

리스트 컴프리헨션에서 함수를 사용하여 더 복잡한 연산을 수행할 수 있습니다.

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            return False
    return True

primes = [x for x in range(20) if is_prime(x)]
print(primes)

출력:

[2, 3, 5, 7, 11, 13, 17, 19]
  • 20 이하의 소수만 리스트에 포함합니다.

1.8 예제: 중첩된 리스트 컴프리헨션을 활용한 행렬 생성

matrix = [[i + j for j in range(3)] for i in range(3)]
print(matrix)

출력:

[[0, 1, 2], [1, 2, 3], [2, 3, 4]]
  • 3x3 행렬을 생성합니다.

2. 제너레이터 (Generator)

제너레이터는 메모리 효율적인 방법으로 데이터를 생성하고 처리할 수 있는 도구입니다. 값을 한 번에 하나씩 반환하며, 대량의 데이터나 무한 시퀀스를 다룰 때 유용합니다. 제너레이터는 yield 키워드를 사용하여 값을 반환하고, 함수의 실행 상태를 유지합니다.

2.1 제너레이터의 개념

  • 지연 실행 (Lazy Evaluation): 필요한 값만 즉시 생성하여 메모리를 절약합니다.
  • 상태 유지: 함수 호출 후 중단된 지점에서 다시 시작할 수 있습니다.

2.2 예제: 간단한 제너레이터

def count_up_to(max):
    count = 1
    while count <= max:
        yield count
        count += 1

# 사용 예
counter = count_up_to(5)
for number in counter:
    print(number)

출력:

1
2
3
4
5
  • yield를 사용하여 값을 하나씩 반환합니다.

2.3 예제: 제너레이터 표현식

squares = (x * x for x in range(10))

for square in squares:
    print(square)

출력:

0
1
4
9
16
25
36
49
64
81
  • 리스트 컴프리헨션과 유사하지만 괄호(())를 사용하여 제너레이터를 생성합니다.

2.4 예제: 피보나치 수열 생성기

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

# 첫 번째 열 개수 출력하기 
fib_gen = fibonacci()
for _ in range(10):
    print(next(fib_gen))

출력:

0
1
1
2
3
5
8
13
21
34
  • 무한 피보나치 수열을 생성합니다.

2.5 예제: 파일 처리에 제너레이터 활용

대용량 파일을 처리할 때 제너레이터를 사용하면 메모리 사용량을 크게 줄일 수 있습니다.

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

# 파일에서 한 줄씩 읽어 처리
for line in read_large_file('large_file.txt'):
    print(line)
  • 파일을 한 줄씩 읽어 처리하므로 메모리 효율성이 높습니다.

2.6 예제: 제너레이터와 send() 메서드

제너레이터는 send() 메서드를 사용하여 외부에서 값을 전달받을 수 있습니다.

def generator_with_send():
    value = yield
    while True:
        value = yield value * 2

gen = generator_with_send()
next(gen)  # 제너레이터 시작
print(gen.send(10))  # 10을 전달하고 결과 출력
print(gen.send(20))  # 20을 전달하고 결과 출력

출력:

20
40
  • 외부에서 값을 전달받아 처리합니다.

2.7 예제: 제너레이터를 활용한 무한 시퀀스 생성

def infinite_sequence():
    num = 0
    while True:
        yield num
        num += 1

gen = infinite_sequence()
for _ in range(5):
    print(next(gen))

출력:

0
1
2
3
4
  • 무한 시퀀스를 생성합니다.

3. 데코레이터 (Decorator)

데코레이터는 함수나 메서드의 동작을 수정하거나 확장하는 데 사용됩니다. 주로 로깅, 접근 제어, 성능 측정 등에 활용됩니다. 데코레이터는 함수를 인자로 받아 새로운 함수를 반환하는 함수입니다.

3.1 기본 구조

def decorator_function(original_function):
    def wrapper_function():
        # 추가할 기능
        print("함수가 호출되기 전")
        original_function()
        print("함수가 호출된 후")
    return wrapper_function

3.2 예제: 간단한 데코레이터

def my_decorator(func):
    def wrapper():
        print("함수 실행 전 메시지")
        func()
        print("함수 실행 후 메시지")
    return wrapper

@my_decorator
def say_hello():
    print("안녕하세요!")

say_hello()

출력:

함수 실행 전 메시지
안녕하세요!
함수 실행 후 메시지
  • @my_decorator를 사용하여 데코레이터를 적용합니다.

3.3 예제: 매개변수가 있는 데코레이터

def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper 
    return decorator_repeat 

@repeat(3)
def greet(name):
    print(f"안녕하세요 {name}님!")

greet("철수")

출력:

안녕하세요 철수님!
안녕하세요 철수님!
안녕하세요 철수님!
  • 반복 횟수를 지정하여 함수를 여러 번 실행합니다.

3.4 예제: 성능 측정 데코레이터

import time 

def timer(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:.4f}초") 
        return result 
    return wrapper 

@timer 
def long_running_task(): 
    time.sleep(2)   # 시간이 걸리는 작업 시뮬레이션

long_running_task()

출력:

long_running_task 수행 시간: 2.0000초  
  • 함수의 실행 시간을 측정합니다.

3.5 예제: 로깅 데코레이터

함수의 호출을 로깅하는 데코레이터를 만들어보겠습니다.

def logger(func):
    def wrapper(*args, **kwargs):
        print(f"{func.__name__} 함수가 호출되었습니다.")
        result = func(*args, **kwargs)
        print(f"{func.__name__} 함수가 종료되었습니다.")
        return result
    return wrapper

@logger
def add(a, b):
    return a + b

print(add(3, 5))

출력:

add 함수가 호출되었습니다.
add 함수가 종료되었습니다.
8
  • 함수의 호출과 종료를 로깅합니다.

3.6 예제: 데코레이터 체이닝

여러 데코레이터를 동시에 적용할 수 있습니다.

def decorator1(func):
    def wrapper():
        print("Decorator 1 시작")
        func()
        print("Decorator 1 종료")
    return wrapper

def decorator2(func):
    def wrapper():
        print("Decorator 2 시작")
        func()
        print("Decorator 2 종료")
    return wrapper

@decorator1
@decorator2
def say_hello():
    print("안녕하세요!")

say_hello()

출력:

Decorator 1 시작
Decorator 2 시작
안녕하세요!
Decorator 2 종료
Decorator 1 종료
  • 데코레이터를 체이닝하여 순차적으로 적용합니다.

3.7 예제: 데코레이터를 활용한 접근 제어

def admin_required(func):
    def wrapper(user, *args, **kwargs):
        if user == "admin":
            return func(*args, **kwargs)
        else:
            raise PermissionError("관리자만 접근할 수 있습니다.")
    return wrapper

@admin_required
def delete_user(user_id):
    print(f"사용자 {user_id}가 삭제되었습니다.")

delete_user("admin", 1)  # 정상 실행
delete_user("user", 2)   # PermissionError 발생

출력:

사용자 1가 삭제되었습니다.
PermissionError: 관리자만 접근할 수 있습니다.
  • 관리자만 접근할 수 있는 기능을 구현합니다.

결론

리스트 컴프리헨션, 제너레이터, 데코레이터는 파이썬에서 코드의 효율성과 가독성을 높이는 강력한 도구입니다. 각각의 특징을 이해하고 적절히 활용하면 더 나은 코드를 작성할 수 있습니다. 리스트 컴프리헨션은 간결한 리스트 생성에, 제너레이터는 메모리 효율적인 데이터 처리에, 데코레이터는 함수의 동작 확장에 유용합니다.

728x90