자바의 어노테이션의 본질은 메타데이터이며 그 자체로 실행이 가능하지 않고 컴파일러나 프레임워크가 읽기 위한 주석 같은 용도이다.
그러나 파이썬의 데코레이터는 고차 함수로 그 자체로 실행 주체가 되며 함수를 실행할 대상 함수를 직접 수정하거나 확장이 가능하다.
핵심은 함수를 인자로 받아서 새로운 함수를 반환하는 함수이다. 그러므로 데코레이터는 고차 함수의 일종이라고 볼 수 있다.
def my_decorator(func): # 데코레이터 함수 (func를 인자로 받음)
def wrapper(*args, **kwargs): # 원본 함수를 감싸는 내부 함수
print("함수 실행 전에 추가할 기능")
result = func(*args, **kwargs) # 원본 함수 실행
print("함수 실행 후에 추가할 기능")
return result
return wrapper # 새로운 함수(wrapper) 반환
@my_decorator # 이 구문은 아래 코드와 동일하게 작동합니다.
def say_hello():
print("Hello!")
## 결과
# 함수 실행 전에 추가할 기능"
# Hello!
# 함수 실행 후에 추가할 기능
비동기 컨텍스트 관리자를 제너레이터 함수 형태로 간결하게 정의해주는 도구
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
FastAPI 라이프사이클 이벤트 핸들러
앱 시작 시: MongoDB 연결 등 초기화 작업
앱 종료 시: 리소스 정리
"""
# MongoDB 연결 초기화
await mongodb_manager.connect()
yield
# 종료 시 정리
await mongodb_manager.disconnect()
__call__() 함수는 객체를 마치 함수를 사용하듯이 사용 가능하다.
랭그래프의 노드는 함수로 정의하는 경우가 많은데 객체를 써서 랭그래프의 노드를 정의할 경우 __call__() 함수를 사용해서 랭그래프 내부에서 객체를 함수처럼 사용하도록 할 수 있다.
랭그래프에서 그래프에 노드를 추가할때 __call__() 함수 없이 객체를 전달하면 에러가 발생하고, __call__() 함수 없다면 객체의 메서드를 전달해야 에러가 발생하지 않는다. __call__() 함수가 있다면 객체를 그대로 전달해도 에러가 발생하지 않는다.
# 인스턴스 생성
counter_obj = Counter()
# 메서드를 직접 호출해야 함
result = counter_obj.add(5)
counter_obj = Counter()
# 출력: 카운터 객체가 생성되었습니다.
# 인스턴스를 함수처럼 바로 호출!
result = counter_obj(5)
가변 키워드 인자
async def ainvoke(self, input, config=None, *, stop=None, **kwargs):
llm_result = await self.agenerate_prompt(
[self._convert_input(input)],
stop=stop,
**kwargs # ← 모두 그대로 전달
)
llm.ainvoke('temperature'=0.7, 'max_tokens'=1000, 'top_p'=0.9, 'stop_sequences'=["END"])
패킹
def print_names(first_name, *middle_names, last_name):
"""
첫 이름과 마지막 이름을 받고, 그 사이에 전달된 모든 이름을 튜플로 묶어 처리합니다.
"""
print(f"첫 이름 (First Name): {first_name}")
if middle_names:
print(f"중간 이름들 (Packed as Tuple): {middle_names}")
print(f"중간 이름 개수: {len(middle_names)}")
else:
print("중간 이름 없음")
print(f"마지막 이름 (Last Name): {last_name}")
print("-" * 20)
print_names("James", "Alfred", "B", "C", last_name="Smith")
# 출력:
# 첫 이름 (First Name): James
# 중간 이름들 (Packed as Tuple): ('Alfred', 'B', 'C')
# 중간 이름 개수: 3
# 마지막 이름 (Last Name): Smith
# --------------------
튜플이란 여러 개의 값을 순서대로 담을 수 있는 시퀀스 자료형 리스트와 비슷하나 불변성을 지니고 있음 또한 정수, 문자열, 리스트, 심지어 다른 튜플 등 모든 종류의 객체를 요소로 포함 가능
언패킹
# Unpacking 예시 (함수 호출 시)
def total(a, b, c):
print(a + b + c)
numbers = [10, 20, 30]
total(*numbers) # 3개의 개별 인자 total(10, 20, 30)로 풀림
특별 인자
def configure_settings(log_level, *, timeout=30, debug=False):
"""
log_level은 위치(또는 키워드)로 전달 가능하지만,
timeout과 debug는 반드시 키워드(이름)로만 전달해야 합니다.
"""
print(f"Log Level: {log_level}")
print(f"Timeout: {timeout}")
print(f"Debug Mode: {debug}")
print("-" * 20)
# 1. 정상 호출: 모든 키워드 전용 인자를 이름으로 명시
configure_settings("INFO", timeout=60, debug=True)
# 출력:
# Log Level: INFO
# Timeout: 60
# Debug Mode: True
# --------------------
| 출처 | Python 표준 라이브러리 (dataclasses) | 외부 라이브러리 (pydantic) |
| 주요 목적 | 단순 데이터 저장 (POJO) | 데이터 검증 + 직렬화/역직렬화 |
| 타입 힌트 활용 | 단순 문법적 힌트 (검증 없음) | 실제 타입 강제 + 자동 변환 |
| 검증(Validation) | ❌ 없음 | ✅ 있음 (타입·값 자동 검사) |
| JSON 직렬화 | 수동 구현 필요 | .dict(), .json() 제공 |
| 성능 | 더 빠름 (순수 Python 객체) | 약간 느림 (검증 로직 추가됨) |
| 사용 분야 | 내부 로직, 임시 데이터 저장 | API, DTO, 설정, 입력 검증 |
인스턴스 변수는 각 인스턴스가 개별로 가지는 변수를 의미하고, 클래스 변수는 각 인스턴스가 모두 공유하는 변수를 의미한다.
from typing import ClassVar
@dataclass
class Dog:
species: ClassVar[str] = "Canine"
name: str