본문 바로가기
코딩취미/Python

코드 재사용 및 최적화를 위해 사용하는 functools 주요 기능 6가지 (partial, lru_cache, cmp_to_key, reduce, total_ordering, wraps)

by 브링블링 2024. 5. 31.
728x90

코드 재사용 및 최적화를 위해 사용하는 functools 주요 기능 6가지 

functools는 파이썬의 표준 라이브러리 모듈로, 고차 함수(higher-order functions)와 관련된 여러 가지 유용한 함수들을 제공합니다. 처음 도입은 파이썬 2.5였습니다. 이 functools 모듈은 함수형 프로그래밍 패러다임을 지원하기 위해 개발되었기때문에 코드의 재사용성과 가독성을 높입니다. 함수형 프로그래밍은 함수를 일급 객체로 취급하며, 함수의 조합과 변형을 통한 코딩을 지향합니다. 주요 특징은 3가지 입니다.

  1. 재사용성: 자주 사용하는 패턴이나 기능을 함수로 묶어 재사용할 수 있도록 합니다.
  2. 가독성: 코드의 가독성을 높이고, 중복 코드를 줄여줍니다.
  3. 성능 최적화: 특정 함수의 실행을 캐싱하거나, 다른 방법으로 최적화할 수 있는 기능을 제공합니다.

그리고 functools 모듈은 다음과 같은 상황에서 유용합니다:

  1. 반복적인 인수 고정: 동일한 인수를 반복적으로 사용해야 할 때 partial을 사용하여 함수 호출을 단순화합니다.
  2. 성능 최적화: 계산이 오래 걸리는 함수의 결과를 캐싱하여 성능을 높이고 싶을 때 lru_cache를 사용합니다.
  3. 누적 계산: 리스트 등의 iterable 객체에 대해 누적 계산을 수행할 때 reduce를 사용합니다.
  4. 데코레이터 작성: 함수의 메타데이터를 유지하면서 데코레이터를 작성할 때 wraps를 사용합니다.

functools의 주요 기능

1) partial

partial 함수는 어떤 함수의 인수를 고정하여 새로운 함수를 생성합니다. 이를 통해 매번 동일한 인수를 반복해서 입력할 필요 없이 함수 호출을 단순화할 수 있습니다.

from functools import partial

def multiply(x, y):
    return x * y

# y 값을 2로 고정한 새로운 함수 생성
double = partial(multiply, y=2)

print(double(5))  # 출력: 10

 

2) lru_cache

lru_cache는 Least Recently Used (LRU) 캐싱을 구현한 데코레이터로, 동일한 입력값에 대해 계산된 결과를 캐시하여 함수의 실행 속도를 높일 수 있습니다.

from functools import lru_cache
import time

@lru_cache(maxsize=None)
def slow_function(n):
    time.sleep(2)  # 계산이 오래 걸리는 함수 시뮬레이션
    return n * n

start = time.time()
print(slow_function(4))  # 처음 호출, 2초 지연
print("Elapsed time:", time.time() - start)

start = time.time()
print(slow_function(4))  # 캐시된 결과 사용, 지연 없음
print("Elapsed time:", time.time() - start)

 

3) cmp_to_key

cmp_to_key는 Python 2 스타일의 비교 함수를 Python 3 스타일의 키 함수로 변환하는 데 사용됩니다. Python 3에서는 비교 함수 대신 키 함수를 사용하여 정렬을 수행합니다. 아래는 문자열 리스트를 길이에 따라 정렬하는 예제입니다.

from functools import cmp_to_key

# Python 2 스타일 비교 함수
def compare_len(str1, str2):
    if len(str1) < len(str2):
        return -1
    elif len(str1) > len(str2):
        return 1
    else:
        return 0

# 비교 함수를 키 함수로 변환
key_func = cmp_to_key(compare_len)

# 정렬할 문자열 리스트
words = ["apple", "banana", "cherry", "date", "elderberry", "fig", "grape"]

# 키 함수를 사용하여 정렬
sorted_words = sorted(words, key=key_func)

print("Sorted words by length:", sorted_words)
728x90

4) reduce

reduce 함수는 함수와 iterable을 인수로 받아, iterable의 요소들을 누적적으로 함수에 적용하여 단일 결과를 반환합니다. Python 3부터는 내장 함수에서 functools 모듈로 이동되었습니다

from functools import reduce

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

# 누적 합계 계산
total = reduce(lambda x, y: x + y, numbers)
print(total)  # 출력: 15

 

5) total_ordering

total_ordering은 클래스에 비교 연산자 메소드를 최소한으로 구현하여 나머지 비교 연산자를 자동으로 추가해주는 데코레이터입니다. 이를 통해 코드의 중복을 줄이고 클래스 정의를 간결하게 만들 수 있습니다. 아래는 total_ordering을 사용하여 클래스에 비교 연산자를 구현하는 예제입니다.

from functools import total_ordering

@total_ordering
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        if isinstance(other, Person):
            return self.age == other.age
        return NotImplemented

    def __lt__(self, other):
        if isinstance(other, Person):
            return self.age < other.age
        return NotImplemented

    def __repr__(self):
        return f"Person(name={self.name}, age={self.age})"

# Person 객체 생성
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)
person3 = Person("Charlie", 30)

# 비교 연산자 사용
print(person1 == person2)  # 출력: False
print(person1 == person3)  # 출력: True
print(person1 < person2)   # 출력: False
print(person2 < person1)   # 출력: True
print(person1 > person2)   # 출력: True

# 정렬 예제
people = [person1, person2, person3]
sorted_people = sorted(people)
print(sorted_people)  # 출력: [Person(name=Bob, age=25), Person(name=Alice, age=30), Person(name=Charlie, age=30)]

 

6) wraps

wraps 데코레이터는 데코레이터를 만들 때, 원래 함수의 메타데이터(이름, 문서 문자열 등)를 유지하기 위해 사용됩니다. 이는 디버깅과 문서화에 매우 유용합니다.

from functools import wraps

def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = f(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    """Prints a greeting."""
    print(f"Hello, {name}!")

say_hello("Alice")
print(say_hello.__name__)  # 출력: say_hello
print(say_hello.__doc__)  # 출력: Prints a greeting.

 

728x90