프로그래밍파이썬 개발자

파이썬 데코레이터에 매개 변수가 무엇이며, 어떻게 구현되는지, 어디에 사용하는 것이 정당한지, 매개 변수가 있는 사용자 정의 데코레이터를 작성할 때 어떤 점이 중요한지에 대한 질문입니다.

Hintsage AI 어시스턴트로 면접 통과

답변

문제의 역사

데코레이터는 파이썬의 가장 강력한 도구 중 하나로 기능이나 메서드의 동작을 수정할 수 있습니다. 때로는 함수만 단순히 "감싸는 것"이 아니라 매개변수(인자)를 사용하여 데코레이터를 설정해야 합니다. 이러한 경우는 로깅, 시간 확인, 접근 제한 등에 사용할 수 있습니다.

문제

일반적인 데코레이터는 감싸야 할 하나의 함수만을 받습니다. 데코레이터에 매개 변수를 전달해야 할 경우, 문법이 더 복잡해져 종종 오류가 발생하고, 특히 함수 중첩 및 *args/**kwargs 전파 시 문제가 발생합니다.

해결책

매개 변수가 있는 데코레이터는 고차 함수로 구현됩니다. 먼저 외부 "데코레이터" 함수가 인자를 사용해 호출되고, 그 함수는 데코레이터를 생성하여 반환하며, 데코레이터는 함수를 받아서 래퍼(wrapper)를 반환합니다:

def repeat(n): def decorator(func): def wrapper(*args, **kwargs): result = None for _ in range(n): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(3) def greet(name): print(f"Hello, {name}!") greet("Python") # 출력: Hello, Python! (3번)

주요 특징:

  • 매개 변수가 있는 데코레이터는 항상 함수의 3중 중첩을 통해 구현됩니다.
  • 래퍼는 결과를 반환해야 하며, *args/**kwargs를 올바르게 전파해야 합니다.
  • 메타데이터를 유지하기 위해 functools.wraps를 잊지 마세요.

함정 질문.

왜 매개 변수가 있는 데코레이터를 일반적인 데코레이터처럼 구현할 수 없습니까?

@decorator를 사용하면 파이썬은 함수를 인수로 데코레이터에 전달합니다. 괄호를 추가하면 (@decorator()), 파이썬은 먼저 함수를 호출하고, 그 결과만 데코레이터로 해석됩니다.

def deco(func): # 일반적인 데코레이터: @deco def deco_with_args(arg): # 인자가 있는 데코레이터: @deco_with_args(arg)

호출 수준에서 매개 변수가 있는 데코레이터가 매개 변수가 없는 데코레이터와 본질적으로 어떻게 다릅니까?

매개 변수가 없는 데코레이터는 함수 자체를 입력으로 받고, 매개 변수가 있는 데코레이터는 함수를 받지 않고 매개 변수를 받으며, 데코레이터를 반환합니다.

functools.wraps를 올바르게 사용하는 방법과 그 이유는 무엇입니까?

functools.wraps(func)는 래퍼 안에서 원본 함수의 이름, 문서 문자열 및 기타 메타데이터를 유지합니다, 그렇지 않으면 모든 이러한 정보가 래퍼에서 대체되어 디버깅 및 introspection에 방해가 됩니다.

import functools def deco_with_args(arg): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper return decorator

전형적인 오류 및 안티 패턴

  • 세 개의 중첩 함수의 필요성을 잊어버리고, 두 개(또는 하나만) 만든다, 결과는 데코레이터가 아니게 됩니다.
  • *args/**kwargs를 내부 래퍼로 전파하지 않습니다.
  • functools.wraps가 없어서 함수의 메타 정보를 잃습니다.

실제 사례

부정적 케이스

매개 변수를 고려하지 않거나 잘못된 수의 중첩 함수를 구현했습니다:

def log(level): def wrapper(func): # 오류 - 래퍼는 더 깊어야 함 print(f"Log: {level}") func() # 함수가 데코레이터로 반환되지 않음 return wrapper @log("INFO") def action(): print("Work!")

장점:

  • 간단하게 보입니다.

단점:

  • 데코레이터가 작동하지 않으며, 함수는 action()이 호출될 때가 아니라 데코레이션하는 단계에서 호출됩니다.

긍정적 케이스

functools.wraps 및 올바른 중첩 함수 사용:

import functools def timer(units): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): import time start = time.time() result = func(*args, **kwargs) end = time.time() if units == 'ms': duration = (end - start) * 1000 else: duration = end - start print(f"Duration: {duration:.4f} {units}") return result return wrapper return decorator @timer('ms') def op(): sum(range(1000)) op()

장점:

  • 올바른 구조, 확장 용이, 깔끔한 로그

단점:

  • 특히 초보자에게는 더 읽기 어려움