클래스 메타프로그래밍
클래스 메타프로그래밍
클래스 메타프로그래밍은 클래스의 정의와 동작을 동적으로 변경하거나 생성하는 기술을 말한다. 이는 Python에서 메타클래스를 사용하여 구현된다. 메타클래스는 클래스의 클래스로, 클래스가 생성되는 방식을 제어할 수 있다. 이 개념을 이해하기 위해서는 메타클래스, type 함수, 그리고 new 및 init 메서드에 대한 이해가 필요하다.
메타클래스
메타클래스는 다른 클래스를 생성하는 클래스이다. Python에서 모든 클래스는 기본적으로 type 메타클래스를 사용한다. type 메타클래스는 클래스가 생성되는 방식을 정의한다. 메타클래스를 직접 정의하여 클래스 생성 시 커스터마이징할 수 있다.
메타클래스 정의
메타클래스를 정의하려면 type을 상속받아 새로운 클래스를 정의한다. 이 클래스에서 __new__와 init 메서드를 오버라이딩하여 클래스 생성 및 초기화 로직을 커스터마이징할 수 있다.
class MyMeta(type):
def __new__(cls, name, bases, dct):
print(f"Creating class {name}")
return super().__new__(cls, name, bases, dct)
def __init__(cls, name, bases, dct):
print(f"Initializing class {name}")
super().__init__(name, bases, dct)
메타클래스 사용
정의한 메타클래스를 클래스 정의 시 metaclass 속성을 사용하여 지정한다.
class MyClass(metaclass=MyMeta):
def __init__(self):
print("Instance of MyClass created")
# 클래스 생성 시 출력
# Creating class MyClass
# Initializing class MyClass
# 인스턴스 생성 시 출력
instance = MyClass()
# Instance of MyClass created
메타클래스의 유용성
메타클래스를 사용하면 다음과 같은 작업을 수행할 수 있다.
- 클래스 생성 시점에 속성 추가/변경: 클래스가 생성될 때 자동으로 속성을 추가하거나 변경할 수 있다.
- 클래스 유효성 검사: 클래스가 생성될 때 특정 조건을 만족하는지 검사할 수 있다.
- 싱글톤 패턴 구현: 메타클래스를 사용하여 클래스의 인스턴스가 하나만 생성되도록 제어할 수 있다.
싱글톤 패턴
메타클래스를 사용하여 싱글톤 패턴을 구현하는 예제이다.
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]
class SingletonClass(metaclass=SingletonMeta):
def __init__(self):
print("Instance created")
# 인스턴스 생성
singleton1 = SingletonClass()
singleton2 = SingletonClass()
# 두 인스턴스는 동일한 객체를 참조
print(singleton1 is singleton2) # True
이 예제에서는 SingletonMeta 메타클래스가 클래스의 인스턴스를 관리하여 동일한 클래스의 인스턴스가 하나만 생성되도록 한다.
메타프로그래밍은 매우 강력한 도구로, 코드의 유연성과 재사용성을 높이는 데 유용하지만, 남용하면 코드의 복잡성을 증가시킬 수 있다. 따라서 신중하게 사용해야 한다.
클래스 팩토리
클래스 팩토리는 클래스 인스턴스를 생성하는 함수나 메서드로, 필요한 속성이나 메서드를 동적으로 추가하거나 수정할 수 있는 기능을 제공한다. 이는 주로 객체 지향 프로그래밍에서 유연성과 재사용성을 높이기 위해 사용된다. 클래스 팩토리를 사용하면 런타임에 클래스의 동작을 변경하거나 클래스를 조건에 따라 생성할 수 있다.
기본 클래스 팩토리
클래스 팩토리를 정의하려면 함수 내에서 클래스 정의를 포함하고, 이 함수를 호출할 때 해당 클래스를 반환하도록 한다.
def create_class(class_name):
class DynamicClass:
def __init__(self, value):
self.value = value
def display(self):
print(f"{class_name}: {self.value}")
return DynamicClass
# 클래스 팩토리 호출
MyClass = create_class("MyClass")
# 클래스 인스턴스 생성
instance = MyClass("Hello, World!")
instance.display() # 출력: MyClass: Hello, World!
이 예제에서 create_class 함수는 동적으로 DynamicClass를 생성하고 반환한다. 반환된 클래스를 사용하여 인스턴스를 생성하고 메서드를 호출할 수 있다.
고급 클래스 팩토리
더 복잡한 요구 사항을 처리하기 위해, 클래스 팩토리는 다양한 인자를 받아 클래스의 동작을 더 정교하게 커스터마이징할 수 있다.
def create_custom_class(class_name, base_class, methods):
class CustomClass(base_class):
def __init__(self, value):
self.value = value
for method_name, method in methods.items():
setattr(CustomClass, method_name, method)
CustomClass.__name__ = class_name
return CustomClass
# 기본 클래스 정의
class BaseClass:
def base_method(self):
print("Base method")
# 추가할 메서드 정의
methods = {
"custom_method": lambda self: print(f"Custom method in {self.value}")
}
# 클래스 팩토리 호출
CustomClass = create_custom_class("CustomClass", BaseClass, methods)
# 클래스 인스턴스 생성
instance = CustomClass("MyCustomClass")
instance.base_method() # 출력: Base method
instance.custom_method() # 출력: Custom method in MyCustomClass
이 예제에서는 create_custom_class 함수가 기본 클래스와 추가 메서드를 받아 새로운 클래스를 생성한다. 반환된 클래스는 기본 클래스의 메서드를 상속받고, 추가 메서드를 포함한다.
클래스 팩토리의 유용성
클래스 팩토리는 다음과 같은 상황에서 유용하게 사용될 수 있다.
- 동적 클래스 생성: 런타임에 클래스를 생성하여 특정 요구 사항을 만족시키는 데 유용하다.
- 유연한 객체 생성: 여러 유형의 객체를 동적으로 생성하여 코드의 유연성과 재사용성을 높일 수 있다.
- 플러그인 시스템: 플러그인 시스템을 구현할 때, 플러그인 클래스를 동적으로 생성하여 로드할 수 있다.
- 테스트 코드 생성: 테스트 코드 작성 시, 다양한 조건에 따라 클래스를 생성하고 테스트할 수 있다.
클래스 팩토리는 강력하고 유연한 도구이지만, 남용하면 코드의 가독성과 유지 보수성을 해칠 수 있으므로 필요한 경우에만 사용해야 한다.
디스크립터를 커스터마이즈하기 위한 클래스 데커레이터
디스크립터는 속성 접근을 제어할 수 있는 특별한 객체로, get, set, delete 메서드를 구현하여 속성 접근을 커스터마이즈할 수 있다. 클래스 데코레이터를 사용하면 클래스에 디스크립터를 쉽게 적용하고 커스터마이즈할 수 있다.
디스크립터 기본 예제
class Descriptor:
def __init__(self, name=None):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
print(f"Getting {self.name}")
return instance.__dict__[self.name]
def __set__(self, instance, value):
print(f"Setting {self.name} to {value}")
instance.__dict__[self.name] = value
def __delete__(self, instance):
print(f"Deleting {self.name}")
del instance.__dict__[self.name]
클래스 데코레이터 정의
클래스 데코레이터는 클래스에 디스크립터를 자동으로 적용하도록 설정할 수 있다. 데코레이터는 클래스를 입력으로 받아서 새로운 클래스를 반환한다.
def add_descriptor(cls):
for key, value in cls.__dict__.items():
if isinstance(value, Descriptor):
value.name = key
return cls
이 데코레이터는 클래스의 모든 디스크립터 인스턴스를 찾아 name 속성을 설정한다.
디스크립터와 데코레이터 사용
이제 클래스 데코레이터와 디스크립터를 함께 사용하여 속성을 커스터마이즈해보겠다.
@add_descriptor
class MyClass:
attr1 = Descriptor()
attr2 = Descriptor()
# 인스턴스 생성
instance = MyClass()
# 속성 설정
instance.attr1 = 10 # 출력: Setting attr1 to 10
instance.attr2 = 20 # 출력: Setting attr2 to 20
# 속성 접근
print(instance.attr1) # 출력: Getting attr1, 10
print(instance.attr2) # 출력: Getting attr2, 20
# 속성 삭제
del instance.attr1 # 출력: Deleting attr1
고급 예제: 유효성 검사를 추가하는 디스크립터
더 나아가서, 유효성 검사를 추가하는 디스크립터를 만들어보겠다.
class ValidatedDescriptor(Descriptor):
def __init__(self, name=None, validator=None):
self.validator = validator
super().__init__(name)
def __set__(self, instance, value):
if self.validator and not self.validator(value):
raise ValueError(f"Invalid value for {self.name}: {value}")
super().__set__(instance, value)
유효성 검사기를 디스크립터에 전달하여 값 설정 시 검사를 수행한다.
def positive_validator(value):
return value > 0
def add_validated_descriptor(cls):
for key, value in cls.__dict__.items():
if isinstance(value, ValidatedDescriptor):
value.name = key
return cls
@add_validated_descriptor
class MyValidatedClass:
positive_attr = ValidatedDescriptor(validator=positive_validator)
# 인스턴스 생성
validated_instance = MyValidatedClass()
# 유효한 값 설정
validated_instance.positive_attr = 10 # 출력: Setting positive_attr to 10
# 유효하지 않은 값 설정
try:
validated_instance.positive_attr = -5 # ValueError 발생
except ValueError as e:
print(e) # 출력: Invalid value for positive_attr: -5
- 디스크립터 정의: get, set, delete 메서드를 구현하여 속성 접근을 제어한다.
- 클래스 데코레이터 정의: 클래스를 입력으로 받아서 새로운 클래스를 반환하며, 클래스의 디스크립터를 설정한다.
- 디스크립터와 데코레이터 사용: 디스크립터를 클래스 속성으로 정의하고 클래스에 데코레이터를 적용하여 속성 접근을 커스터마이즈한다.
- 유효성 검사 디스크립터: 디스크립터에 유효성 검사 기능을 추가하여 값 설정 시 검사를 수행한다.
이렇게 하면 클래스에 디스크립터를 쉽게 추가하고 관리할 수 있으며, 클래스의 속성 접근을 유연하게 제어할 수 있다.
임포트 타임과 런타임
임포트 타임과 런타임은 Python 프로그램의 실행 시점에서 중요한 개념이다. 각각의 시점은 코드의 실행 순서와 동작 방식을 이해하는 데 도움이 된다.
임포트 타임 (Import Time)
임포트 타임은 Python 모듈이 처음 임포트될 때 발생하는 시점이다. 이 시점에 모듈의 최상위 코드가 실행됩니다. 모듈을 임포트할 때, Python은 다음 단계를 거친다:
- 모듈 찾기: Python은 지정된 모듈을 찾기 위해 sys.path에 나열된 디렉토리에서 모듈 파일을 검색한다.
- 모듈 로딩: 모듈이 발견되면 Python은 이를 로드하고, 모듈 객체를 만든다.
- 모듈 초기화: 모듈의 최상위 수준 코드를 실행합니다. 이 단계에서 모듈의 함수와 클래스가 정의되고, 초기화 코드가 실행된다.
# my_module.py
print("Module my_module is being imported")
class MyClass:
print("Defining MyClass")
def __init__(self):
print("MyClass instance created")
def method(self):
print("Method in MyClass")
# main.py
print("Before importing my_module")
import my_module
print("After importing my_module")
'''
Before importing my_module
Module my_module is being imported
Defining MyClass
After importing my_module
'''
위 코드에서, my_module.py가 임포트될 때 다음 순서로 코드가 실행된다:
- "Module my_module is being imported"가 출력된다.
- MyClass가 정의된다. 이 때 "Defining MyClass"가 출력된다.
- main.py에서 import my_module이 실행될 때 위의 두 출력문이 나타나고, "After importing my_module"가 출력된다.
런타임 (Run Time)
런타임은 프로그램이 실제로 실행되는 시점이다. 이는 임포트 타임 이후의 시점으로, 프로그램의 함수 호출, 루프 실행, 조건문 평가 등이 이루어지는 동안이다.
# main2.py
class MyClass:
def __init__(self):
print("MyClass instance created")
def method(self):
print("Method in MyClass")
print("Creating MyClass instance")
instance = MyClass() # MyClass 인스턴스 생성
print("Calling method")
instance.method() # 메서드 호출
'''
Creating MyClass instance
MyClass instance created
Calling method
Method in MyClass
'''
위 코드에서, MyClass 정의는 임포트 타임에 이루어지고, 실제 인스턴스 생성과 메서드 호출은 런타임에 발생한다.
클래스 관점에서 임포트 타임과 런타임의 차이점
- 클래스 정의 시점:
- 임포트 타임: 모듈이 임포트될 때 클래스 정의가 이루어진다.
- 런타임: 클래스 정의가 완료된 후 인스턴스 생성, 메서드 호출 등이 이루어진다.
- 주요 동작:
- 임포트 타임: 클래스의 정의, 모듈의 최상위 코드 실행, 클래스 메타데이터 초기화.
- 런타임: 클래스 인스턴스 생성, 메서드 호출, 속성 접근, 제어 흐름 실행.
- 메타클래스와 클래스 데코레이터:
- 메타클래스와 클래스 데코레이터는 클래스 정의 시점(임포트 타임)에 클래스의 속성이나 동작을 수정할 수 있다.
- 런타임에는 수정된 클래스의 인스턴스가 생성되고, 수정된 동작이 실행된다.
메타클래스와 클래스 데코레이터
class MyMeta(type):
def __new__(cls, name, bases, dct):
print(f"Creating class {name}")
return super().__new__(cls, name, bases, dct)
def class_decorator(cls):
print(f"Decorating class {cls.__name__}")
cls.decorated = True
return cls
@class_decorator
class MyClass(metaclass=MyMeta):
def __init__(self):
print("MyClass instance created")
def method(self):
print("Method in MyClass")
print("Before creating MyClass instance")
instance = MyClass()
print("After creating MyClass instance")
print(f"MyClass is decorated: {hasattr(MyClass, 'decorated')}")
'''
Decorating class MyClass
Creating class MyClass
Before creating MyClass instance
MyClass instance created
After creating MyClass instance
MyClass is decorated: True
'''
위 코드에서, 메타클래스와 클래스 데코레이터는 클래스 정의 시점에 실행된다.
- 임포트 타임: 모듈이 임포트될 때 발생하며, 클래스 정의, 메타클래스의 new 및 init 메서드, 클래스 데코레이터 등이 실행된다.
- 런타임: 프로그램 실행 시 발생하며, 클래스 인스턴스 생성, 메서드 호출, 속성 접근 등이 이루어진다.
- 메타클래스와 클래스 데코레이터: 클래스 정의 시점에 클래스의 동작을 수정할 수 있으며, 수정된 클래스는 런타임에 인스턴스 생성 및 메서드 호출에 반영된다.
이해를 돕기 위해 예제와 함께 설명하였으며, 이를 통해 임포트 타임과 런타임의 차이와 역할을 명확히 이해할 수 있다.
메타클레스 기본 지식
메타클래스는 클래스의 클래스이다. 즉, 클래스가 인스턴스를 생성하는 것처럼 메타클래스는 클래스를 생성한다. Python에서 모든 클래스는 실제로 메타클래스의 인스턴스이다. 기본적으로 모든 클래스는 type 메타클래스의 인스턴스이다.
메타클래스의 기본 개념
- 클래스 생성: 메타클래스는 클래스를 생성하고, 이 클래스는 나중에 객체(인스턴스)를 생성한다.
- 커스터마이즈 클래스 생성: 메타클래스를 사용하면 클래스를 생성하는 방식을 커스터마이즈할 수 있다. 이는 클래스의 속성, 메서드 등을 동적으로 추가하거나 수정할 수 있음을 의미한다.
- 사용 예: 메타클래스는 주로 프레임워크나 라이브러리에서 클래스의 동작을 제어하고 확장하기 위해 사용된다.
type 메타클래스
type은 기본적으로 모든 클래스의 메타클래스이다. type을 사용하여 동적으로 클래스를 생성할 수 있다.
# type을 사용한 동적 클래스 생성
MyClass = type('MyClass', (object,), {'attr': 100})
# MyClass 인스턴스 생성
instance = MyClass()
print(instance.attr) # 출력: 100
위 예제에서 type 함수는 클래스 이름, 상속받을 부모 클래스, 클래스 속성을 인수로 받아 새로운 클래스를 생성한다.
사용자 정의 메타클래스
사용자 정의 메타클래스를 만들기 위해 type을 상속받아 새로운 클래스를 정의한다. 이 클래스에서 __new__와 init 메서드를 오버라이드하여 클래스 생성 방식을 커스터마이즈할 수 있다.
class MyMeta(type):
def __new__(cls, name, bases, dct):
print(f"Creating class {name}")
return super().__new__(cls, name, bases, dct)
def __init__(cls, name, bases, dct):
print(f"Initializing class {name}")
super().__init__(name, bases, dct)
# 메타클래스를 사용한 클래스 정의
class MyClass(metaclass=MyMeta):
def __init__(self):
print("Instance of MyClass created")
# 클래스 생성 시 출력
# Creating class MyClass
# Initializing class MyClass
# 인스턴스 생성 시 출력
instance = MyClass()
# Instance of MyClass created
'''
Creating class MyClass
Initializing class MyClass
Instance of MyClass created
'''
메타클래스의 주요 메서드
- new 메서드: 클래스가 생성될 때 호출된다. 클래스의 이름, 부모 클래스, 클래스 속성 사전을 인수로 받아 새로운 클래스를 반환한다.
- init 메서드: 클래스가 초기화될 때 호출된다. 생성된 클래스의 인스턴스를 초기화한다.
메타클래스의 활용
- 클래스 속성 추가/변경: 클래스가 생성될 때 자동으로 속성을 추가하거나 변경할 수 있다.
- 클래스 유효성 검사: 클래스가 생성될 때 특정 조건을 만족하는지 검사할 수 있다.
- 싱글톤 패턴 구현: 메타클래스를 사용하여 클래스의 인스턴스가 하나만 생성되도록 제어할 수 있다.
클래스 속성 추가
class AttributeAddingMeta(type):
def __new__(cls, name, bases, dct):
dct['added_attr'] = 'I am an added attribute'
return super().__new__(cls, name, bases, dct)
class MyClass(metaclass=AttributeAddingMeta):
pass
instance = MyClass()
print(instance.added_attr) # 출력: I am an added attribute
위 예제에서는 AttributeAddingMeta 메타클래스가 added_attr 속성을 클래스에 추가한다.
- 메타클래스란: 클래스의 클래스로, 클래스를 생성하는 방식을 제어할 수 있다.
- 기본 메타클래스: Python의 기본 메타클래스는 type이다.
- 사용자 정의 메타클래스: type을 상속받아 __new__와 init 메서드를 오버라이드하여 커스터마이즈할 수 있다.
- 활용 사례: 클래스 속성 추가/변경, 클래스 유효성 검사, 싱글톤 패턴 구현 등.
메타클래스는 매우 강력하고 유용한 도구이지만, 복잡성을 증가시킬 수 있으므로 신중하게 사용해야 한다.
메타클래스에 prepare() 특별 메소드
prepare()는 Python 3에서 도입된 메타클래스의 특별 메서드로, 클래스 본문이 실행되기 전에 클래스 네임스페이스를 초기화하는 데 사용된다. 이 메서드는 클래스 본문이 어떤 객체에 정의될지 결정하는 역할을 한다. 기본적으로 클래스 네임스페이스는 일반적인 딕셔너리(dict)이지만, prepare() 메서드를 사용하여 커스텀 매핑 객체를 사용할 수 있다.
prepare() 메서드의 역할
- 네임스페이스 준비: prepare() 메서드는 클래스 정의가 시작되기 전에 호출되며, 클래스의 네임스페이스로 사용할 객체를 반환한다.
- 매개변수: 이 메서드는 클래스 이름과 부모 클래스 목록을 인수로 받는다.
- 반환값: 클래스 정의에 사용할 매핑 객체를 반환한다.
기본 prepare() 메서드
기본적으로 type 메타클래스의 prepare() 메서드는 빈 딕셔너리를 반환한다.
class MyMeta(type):
@classmethod
def __prepare__(cls, name, bases, **kwargs):
print("Preparing namespace")
return {}
class MyClass(metaclass=MyMeta):
pass
위 코드에서 MyMeta 메타클래스의 prepare() 메서드는 클래스를 정의하기 전에 "Preparing namespace"를 출력하고 빈 딕셔너리를 반환한다.
prepare() 메서드의 활용
prepare() 메서드를 활용하면 클래스 네임스페이스를 커스터마이즈할 수 있다. 예를 들어, 클래스를 정의하는 순서대로 속성을 저장하는 OrderedDict를 사용할 수 있다.
from collections import OrderedDict
class OrderedMeta(type):
@classmethod
def __prepare__(cls, name, bases, **kwargs):
return OrderedDict()
def __new__(cls, name, bases, classdict):
classdict['ordered'] = [key for key in classdict.keys() if not key.startswith('__')]
return super().__new__(cls, name, bases, classdict)
class MyClass(metaclass=OrderedMeta):
x = 1
y = 2
print(MyClass.ordered) # 출력: ['x', 'y']
이 예제에서는 OrderedMeta 메타클래스가 OrderedDict를 반환하도록 prepare() 메서드를 오버라이드하여, 클래스 정의 시 속성들이 정의된 순서를 기록한다.
고급 예제: 디스크립터와 결합
prepare() 메서드를 사용하여 디스크립터의 동작을 더욱 정교하게 커스터마이즈할 수 있다.
class Descriptor:
def __init__(self, name=None):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
print(f"Getting {self.name}")
return instance.__dict__[self.name]
def __set__(self, instance, value):
print(f"Setting {self.name} to {value}")
instance.__dict__[self.name] = value
def __delete__(self, instance):
print(f"Deleting {self.name}")
del instance.__dict__[self.name]
class CustomMeta(type):
@classmethod
def __prepare__(cls, name, bases, **kwargs):
return OrderedDict()
def __new__(cls, name, bases, classdict):
for key, value in classdict.items():
if isinstance(value, Descriptor):
value.name = key
return super().__new__(cls, name, bases, classdict)
class MyClass(metaclass=CustomMeta):
attr1 = Descriptor()
attr2 = Descriptor()
instance = MyClass()
instance.attr1 = 10 # 출력: Setting attr1 to 10
print(instance.attr1) # 출력: Getting attr1, 10
이 예제에서는 CustomMeta 메타클래스가 prepare() 메서드를 사용하여 OrderedDict를 반환하고, 클래스가 정의될 때 디스크립터의 이름을 설정한다.
- prepare() 메서드: 메타클래스의 특별 메서드로, 클래스 네임스페이스를 초기화하는 역할을 한다.
- 사용 목적: 기본적으로 빈 딕셔너리를 반환하지만, 커스텀 매핑 객체를 반환하여 클래스 정의를 커스터마이즈할 수 있다.
- 활용 사례: 속성 정의 순서를 기록하거나, 클래스 속성을 동적으로 변경하는 등의 고급 기능 구현에 사용된다.
이와 같이 prepare() 메서드를 사용하면 클래스 정의 과정에서 네임스페이스를 유연하게 제어할 수 있으며, 디스크립터와 결합하여 다양한 커스터마이징을 할 수 있다.
객체로서의 클래스
Python에서 클래스는 단순히 객체를 정의하는 도구일 뿐만 아니라, 클래스 자체도 객체이다. 이를 통해 클래스는 다른 객체처럼 다룰 수 있으며, 동적으로 생성, 수정, 전달, 반환, 저장 등이 가능하다. 이 개념은 Python의 유연성과 동적 특성을 크게 확장한다.
클래스 객체의 기본 개념
- 클래스 객체: Python에서 클래스 정의는 그 자체로 객체를 생성하는 것이다. 이 객체는 다른 객체처럼 다룰 수 있다.
- 동적 클래스 생성: Python에서는 type 함수를 사용하여 동적으로 클래스를 생성할 수 있다. type 함수는 클래스를 생성하는 데 사용되는 내장 메타클래스이다.
클래스 객체의 특성
클래스 객체는 다음과 같은 특성을 가진다:
- 속성 및 메서드 접근: 클래스 객체의 속성과 메서드에 접근할 수 있다.
- 클래스 메서드 호출: 클래스 객체를 통해 메서드를 호출할 수 있다.
- 인스턴스 생성: 클래스 객체를 사용하여 인스턴스를 생성할 수 있다.
클래스 객체 속성 접근 및 메서드 호출
class MyClass:
class_attr = 42
def __init__(self, value):
self.instance_attr = value
def instance_method(self):
return f"Instance method called with {self.instance_attr}"
@classmethod
def class_method(cls):
return f"Class method called with {cls.class_attr}"
# 클래스 객체를 통해 속성 접근 및 메서드 호출
print(MyClass.class_attr) # 출력: 42
print(MyClass.class_method()) # 출력: Class method called with 42
# 인스턴스 생성
instance = MyClass(100)
print(instance.instance_method()) # 출력: Instance method called with 100
동적 클래스 생성
Python에서는 type 함수를 사용하여 동적으로 클래스를 생성할 수 있다. type 함수는 세 개의 인수를 받는다: 클래스 이름, 부모 클래스의 튜플, 클래스 속성 사전.
# 동적 클래스 생성
DynamicClass = type('DynamicClass', (object,), {'class_attr': 42, 'class_method': lambda cls: f"Class method called with {cls.class_attr}"})
# 클래스 객체를 통해 속성 접근 및 메서드 호출
print(DynamicClass.class_attr) # 출력: 42
print(DynamicClass.class_method(DynamicClass)) # 출력: Class method called with 42
# 인스턴스 생성
dynamic_instance = DynamicClass()
클래스 객체의 활용
클래스 객체로서의 클래스를 활용하면 다음과 같은 작업이 가능하다:
- 동적 클래스 생성 및 수정: 프로그램 실행 중에 클래스를 동적으로 생성하거나 수정할 수 있다.
- 클래스 팩토리 패턴 구현: 클래스를 반환하는 함수를 정의하여 다양한 유형의 클래스를 동적으로 생성할 수 있다.
- 메타프로그래밍: 메타클래스를 사용하여 클래스 생성 방식을 커스터마이즈하고, 동적 속성 추가 및 클래스 유효성 검사를 수행할 수 있다.
클래스 팩토리
클래스 팩토리 패턴을 사용하여 다양한 유형의 클래스를 생성하는 예제이다.
def create_class(class_name, base_classes, attributes):
return type(class_name, base_classes, attributes)
# 동적 클래스 생성
MyDynamicClass = create_class('MyDynamicClass', (object,), {'attr': 100, 'method': lambda self: f"Method called with {self.attr}"})
# 인스턴스 생성
dynamic_instance = MyDynamicClass()
print(dynamic_instance.attr) # 출력: 100
print(dynamic_instance.method()) # 출력: Method called with 100
메타프로그래밍
메타클래스를 사용하여 클래스 생성 방식을 커스터마이즈하는 예제이다.
class MyMeta(type):
def __new__(cls, name, bases, dct):
dct['added_attr'] = 'I am added by MyMeta'
return super().__new__(cls, name, bases, dct)
class MyClass(metaclass=MyMeta):
pass
# 클래스 객체를 통해 추가된 속성 접근
print(MyClass.added_attr) # 출력: I am added by MyMeta
# 인스턴스 생성
instance = MyClass()
print(instance.added_attr) # 출력: I am added by MyMeta
- 클래스 객체란: Python에서 클래스 정의는 그 자체로 객체를 생성하는 것으로, 클래스는 다른 객체처럼 다룰 수 있다.
- 동적 클래스 생성: type 함수를 사용하여 동적으로 클래스를 생성할 수 있다.
- 클래스 객체의 활용: 동적 클래스 생성 및 수정, 클래스 팩토리 패턴, 메타프로그래밍 등 다양한 활용이 가능하다.
클래스를 객체로 다루는 이 개념을 이해하면 Python의 유연하고 동적인 특성을 더욱 잘 활용할 수 있다.
모든 코드는 github에 저장되어 있습니다.