[Python] 메타프로그래밍이란?

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)  # 새로운 속성