1. 메타프로그래밍이란?
메타프로그래밍이란 코드를 통해 프로그램이 자기 자신이나 다른 프로그램을 다루고 수정하는 기술입니다. 메타프로그래밍을 사용하면 프로그램을 동적으로 생성, 조작 및 수정할 수 있습니다. 파이썬에서는 메타프로그래밍 기술을 통해 코드의 가독성과 재사용성을 높이고, 중복을 줄일 수 있습니다.
2. 주요 메타프로그래밍 기술
2.1 리플렉션 (Reflection)
프로그램이 자신의 구조와 속성을 검사하고 수정할 수 있는 능력입니다. 파이썬에서는 getattr, setattr, hasattr, delattr 등의 내장 함수를 사용하여 객체의 속성을 동적으로 접근할 수 있습니다.
class Example:
def __init__(self, x):
self.x = x
example = Example(42)
attr_name = 'x'
# 속성 검사
print(hasattr(example, attr_name)) # True
# 속성 가져오기
value = getattr(example, attr_name)
print(value) # 42
# 속성 설정하기
setattr(example, attr_name, 13)
print(example.x) # 13
# 속성 삭제하기
delattr(example, attr_name)
print(hasattr(example, attr_name)) # False
2.2 동적 코드 생성 및 실행
exec와 eval 함수를 사용하여 문자열로 표현된 파이썬 코드를 동적으로 실행할 수 있습니다. exec()는 문자열로 표현된 파이썬 문장을 실행할 수 있습니다. 이 함수는 주로 제어문, 함수, 클래스 정의 등을 동적으로 실행할 때 사용됩니다. eval()은 문자열로 표현된 파이썬 표현식을 평가하고 그 결과를 반환합니다. 이 함수는 주로 산술 연산, 리스트 조작 등의 강단한 표현식을 동적으로 평가할 때 사용됩니다.
# exec: 동적으로 코드 블록을 실행
code_block = """
def hello(name):
print(f"안녕하세요, {name}님!")
"""
exec(code_block)
hello("홍길동") # 안녕하세요, 홍길동님!
# eval: 동적으로 표현식을 평가
expression = "3 * (2 + 5)"
result = eval(expression)
print(result) # 21
2.3 데코레이터 (Decorator)
함수나 클래스의 동작을 변경하거나 확장할 수 있는 기능입니다. 데코레이터는 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있습니다.
def timer_decorator(func):
import time
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} 실행 시간: {end_time - start_time:.3f} 초")
return result
return wrapper
@timer_decorator
def example_function():
import time
time.sleep(1)
example_function() # example_function 실행 시간: 1.001 초
2.4 메타클래스 (Metaclass)
클래스의 행동과 속성을 정의하는 기본 클래스입니다. 간단히 말해 메타클래스는 클래스를 만드는 클래스입니다. 메타클래스를 사용하면 클래스의 생성과 초기화 과정을 제어할 수 있으며, 이를 통해 코드를 더욱 유연하게 만들 수 있습니다. 파이썬에서 모든 클래스는 기본적으로 type이라는 메타클래스를 사용하여 생성됩니다. 클래스 정의가 실행되면, 파이썬은 메타클래스의 __new__ 및 __init__ 메서드를 호출하여 클래스 객체를 생성하고 초기화합니다.
아래 코드는 사용자가 정의한 메타클래스를 사용하여 클래스를 만들 때 추가적인 변경사항을 적용하는 코드입니다.
- CustomMetaclass라는 이름의 사용자 정의 메타 클래스를 생성합니다. 그리고 __new__ 메서드를 오버라이드 합니다. 이 메서드는 클래스 생성 과정에서 호출되며, 클래스 이름(name), 기반 클래스(bases), 클래스 딕셔너리(dct)를 인자로 받습니다.
- __new__ 메서드 내에서 super().new__(cls, name, bases, dct)를 호출하여 기본 클래스 생성 로직을 수행합니다. 이를 통해 새로운 클래스 객체인 new_class가 생성됩니다. super()함수는 상속 관계에 있는 클래스들의 메서드를 호출하기 위해 사용되는 내장 함수입니다.
- new_class 객체에 new_attr이라는 새로운 속성을 추가하고 속성값을 "새로운 속성"으로 설정합니다.
- 수정된 클래스 객체인 new_class를 반환합니다.
- Example 클래스를 정의하면서, 사용자 정의 메타클래스인 CustomMetaclass를 메타클래스로 지정합니다.
- Example 클래스의 인스턴스 example을 생성합니다.
- example 객체에서 new_attr 속성의 값을 출력합니다. 결과로 "새로운 속성"이 출력됩니다.
class CustomMetaclass(type):
def __new__(cls, name, bases, dct):
print(f"메타클래스에서 클래스 생성: {name}")
new_class = super().__new__(cls, name, bases, dct)
new_class.new_attr = "새로운 속성"
return new_class
class Example(metaclass=CustomMetaclass):
pass
example = Example()
print(example.new_attr) # 새로운 속성