프로그래밍/Python

파이썬 고급 자료 구조: 리스트 컴프리헨션, 제너레이터, 데코레이터, 컨텍스트 매니저

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

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

리스트 컴프리헨션은 파이썬에서 리스트를 간결하고 효율적으로 생성하는 방법입니다. 반복문과 조건문을 사용하여 새로운 리스트를 만들 수 있으며, 코드의 가독성을 높이고 실행 속도를 개선할 수 있습니다. 리스트 컴프리헨션은 파이썬의 강력한 기능 중 하나로, 데이터 처리와 변환에 널리 사용됩니다.

1.1 기본 개념

리스트 컴프리헨션의 기본 형식은 다음과 같습니다:

[표현식 for 항목 in iterable if 조건]
  • 표현식: 새로 만들 요소의 값을 정의합니다.
  • 항목: iterable(반복 가능한 객체)의 각 요소를 나타냅니다.
  • iterable: 리스트, 튜플, 문자열 등 반복 가능한 객체입니다.
  • 조건 (선택적): 특정 조건을 만족하는 경우에만 표현식을 적용합니다.

1.2 예제

1.2.1 기본 사용법

# 0부터 9까지의 숫자로 이루어진 리스트 생성
numbers = [x for x in range(10)]
print(numbers)  # 출력: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

1.2.2 제곱값 계산하기

squares = [x**2 for x in range(10)]
print(squares)  # 출력: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

1.2.3 짝수 필터링

even_numbers = [x for x in range(20) if x % 2 == 0]
print(even_numbers)  # 출력: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

1.2.4 문자열 처리하기

words = ["apple", "banana", "cherry"]
word_lengths = [len(word) for word in words]
print(word_lengths)  # 출력: [5, 6, 6]

1.2.5 중첩된 루프 사용하기

combinations = [(x, y) for x in range(3) for y in range(3)]
print(combinations)  # 출력: [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

1.2.6 조건문을 활용한 복잡한 예제

# 1부터 20까지의 숫자 중 3의 배수는 "Fizz", 5의 배수는 "Buzz", 15의 배수는 "FizzBuzz"로 변환
fizzbuzz = ["FizzBuzz" if x % 15 == 0 else "Fizz" if x % 3 == 0 else "Buzz" if x % 5 == 0 else x for x in range(1, 21)]
print(fizzbuzz)
# 출력: [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz', 16, 17, 'Fizz', 19, 'Buzz']

1.2.7 딕셔너리 컴프리헨션

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

# 키는 숫자, 값은 제곱값으로 이루어진 딕셔너리 생성
squares_dict = {x: x**2 for x in range(10)}
print(squares_dict)  # 출력: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

1.2.8 집합 컴프리헨션

집합도 컴프리헨션을 사용하여 생성할 수 있습니다.

# 0부터 9까지의 숫자 중 짝수만 포함하는 집합 생성
even_set = {x for x in range(10) if x % 2 == 0}
print(even_set)  # 출력: {0, 2, 4, 6, 8}

2. 제너레이터 (Generator)

제너레이터는 메모리 효율적인 데이터 처리를 가능하게 해주는 특별한 종류의 이터러블입니다. 대량의 데이터를 처리해야 할 때 특히 유용하며, 필요할 때마다 데이터를 생성하여 메모리 사용량을 줄일 수 있습니다. 제너레이터는 데이터를 한 번에 메모리에 로드하지 않고, 순차적으로 생성하기 때문에 대용량 데이터 처리에 적합합니다.

2.1 기본 개념

  • 이터레이터: 제너레이터는 이터레이터 프로토콜(Iterator Protocol)을 따릅니다. 즉, __iter__()__next__() 메소드를 구현하여 반복 가능한 객체를 만듭니다.
  • yield 키워드: 일반 함수에서는 return 키워드를 사용해 값을 반환하지만, 제너레이터에서는 yield 키워드를 사용합니다. yield를 만나면 함수의 상태가 저장되고 다음 호출 시 그 지점부터 실행됩니다.

2.2 예제

2.2.1 제너레이터 함수 만들기

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

# 사용 예시
counter = count_up_to(5)
for number in counter:
    print(number)  # 출력: 1 2 3 4 5

2.2.2 제너레이트 표현식 (Generator Expressions)

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.2.3 파일 읽기

def read_large_file(file_name):
    with open(file_name) as f:
        for line in f:
            yield line.strip()

# 사용 예시
for line in read_large_file('large_file.txt'):
    print(line)

2.2.4 무한 시퀀스 생성

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

# 처음 몇 개 숫자 출력하기 
gen = infinite_numbers()
for _ in range(5):
    print(next(gen))  # 출력: 0 1 2 3 4

2.2.5 제너레이터를 사용한 피보나치 수열

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

# 처음 10개의 피보나치 수 출력
fib = fibonacci()
for _ in range(10):
    print(next(fib))  # 출력: 0 1 1 2 3 5 8 13 21 34

3. 데코레이터 (Decorator)

데코레이터는 함수나 메서드의 동작을 수정하거나 확장하는 방법입니다. 이를 통해 코드의 재사용성을 높이고, 가독성을 향상시키며, 반복적인 코드를 줄일 수 있습니다. 데코레이터는 로깅, 권한 확인, 성능 측정 등 다양한 상황에서 유용하게 사용됩니다.

3.1 기본 개념

  • 함수: 파이썬에서는 모든 것이 객체이며, 함수도 객체입니다.
  • 고차 함수: 다른 함수를 인자로 받거나 반환하는 함수를 말합니다. 데코레이터는 이러한 고차 함수의 일종입니다.
  • 사용 목적: 로깅(logging), 권한 확인(authentication), 성능 측정(timing) 등.

3.2 예제

3.2.1 간단한 데코레이터 예제

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

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

print(add(3, 4))  # 출력: 49

3.2.2 여러 개의 데코레이터 사용하기

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Calling function {func.__name__} with arguments {args} and {kwargs}")
        return func(*args, **kwargs)
    return wrapper

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

@log_decorator
@double_result
def multiply(x, y):
    return x * y

print(multiply(2, 5))  # 출력: 20

3.2.3 클래스 메서드에 데코레이터 적용

class MyClass:

    @staticmethod
    @log_decorator
    def static_method():
        print("This is a static method.")

MyClass.static_method()

3.2.4 데코레이터를 사용한 실행 시간 측정

import time

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

@timer_decorator
def slow_function():
    time.sleep(2)

slow_function()  # 출력: slow_function executed in 2.0002 seconds

4. 컨텍스트 매니저 (Context Manager)

컨텍스트 매니저는 자원 관리를 효율적으로 수행하기 위한 도구입니다. 주로 파일 입출력, 데이터베이스 연결, 네트워크 소켓 등과 같이 사용 후 반드시 정리해야 하는 자원을 관리할 때 사용됩니다. with 문을 사용하여 컨텍스트 매니저를 활용하면 코드의 가독성과 안정성을 높일 수 있습니다.

4.1 기본 개념

  • __enter__: 컨텍스트가 시작될 때 호출되며, 리소스를 초기화하거나 반환합니다.
  • __exit__: 컨텍스트가 종료될 때 호출되며, 리소스를 해제하거나 정리 작업을 수행합니다.

4.2 예제

4.2.1 파일 읽기

# with 문을 이용한 안전한 파일 처리
with open('example.txt', 'r') as file:
    content = file.read()
    print(content)  # with 블록 종료 시 자동으로 close() 호출됨

4.2.2 사용자 정의 컨텍스트 매니저

from contextlib import contextmanager

@contextmanager
def database_connection():
    connection = create_connection()  # 가상의 함수로 DB 연결 생성
    try:
        yield connection  # 연결 객체 제공
    finally:
        connection.close()  # 항상 연결 종료

# 사용 예시 
with database_connection() as conn:
    result = conn.execute("SELECT * FROM users")  # 쿼리 실행

4.2.3 컨텍스트 매니저를 활용한 타이머

import time
from contextlib import contextmanager

@contextmanager
def timer():
    start_time = time.time()
    try:
        yield
    finally:
        end_time = time.time()
        print(f"Elapsed time: {end_time - start_time:.2f} seconds")

# 사용 예시
with timer():
    time.sleep(2)  # 2초 대기
# 출력: Elapsed time: 2.00 seconds

4.2.4 컨텍스트 매니저를 사용한 임시 디렉토리 생성

import tempfile
import shutil
from contextlib import contextmanager

@contextmanager
def temp_directory():
    temp_dir = tempfile.mkdtemp()
    try:
        yield temp_dir
    finally:
        shutil.rmtree(temp_dir)  # 임시 디렉토리 삭제

# 사용 예시
with temp_directory() as temp_dir:
    print(f"Temporary directory created at {temp_dir}")
    # 임시 디렉토리에서 작업 수행
# 임시 디렉토리가 자동으로 삭제됨

결론

리스트 컴프리헨션, 제너레이터, 데코레이터, 컨텍스트 매니저는 파이썬 프로그래밍에서 매우 강력하고 유용한 도구입니다. 각각의 개념과 활용 방법을 이해하고 적절히 사용하면 코드의 가독성과 효율성을 크게 향상시킬 수 있습니다. 이러한 고급 기능들을 마스터하면 파이썬으로 더욱 강력하고 유연한 프로그램을 작성할 수 있습니다.

728x90