메타클래스 기능
클래스가 정의된 후 클래스가 실제로 사용 전 프로퍼티 변경 혹시 표시
디스크립터 활용
#애트리뷰트와 컬럼 이름 연결
#컬럼 이름을 Field 디스크립터에 저장
# setattr 내장 함수 사용
class Field:
def __init__(self, name):
self.name = name
self.internal_name = '_' + self.name
def __get__(self, instance, instance_type):
if instance is None:
return self
return getattr(instance, self.internal_name, '')
def __set__(self, instance, value):
setattr(instance, self.internal_name, value)
# 메모리 누수를 막기 위해서 위의 방법보단 이 방법 활용
class Customer:
#클래스 애트리뷰트
first_name = Field('first_name')
last_name = Field('last_name')
prefix = Field('prefix')
suffix = Field('suffix')
#메타 클래스를 활용하면 위의 문제점인 중복을 줄일 수 있다.
class Meta(type):
def __new__(meta, name, bases, class_dict):
for key, value in class_dict.items():
if isinstance(value, Field):
value.name = key
value.internal_name = '_' + key
cls = type.__new__(meta, name, bases, class_dict)
return cls
#중복 문제 해결
# 메타클래스를 사용하는 기반 클래스 정의로 데이터베이스 로우를 표현하는 모든 클래스는 기반 클래스를 상속해서 메타클래스 사용
class DatabaseRow(metaclass=Meta):
pass
#컬럼 이름을 받는 대신, Meta.__new__ 메서드가 애트리뷰트를 설정
class Field:
def __init__(self):
#이 두 정보를 매타클래스가 채워준다
self.name = None
self.internal_name = None
def __get__(self, instance, instance_type):
if instance is None:
return self
return getattr(instance, self.internal_name,'')
def __set__(self, instance, value):
setattr(instance, self.internal_value, value)
"""
문제점은 계층 구조로 인해서 어쩔 수 없이 databaserow를 상속할 수 없는 경우 Field 클래스를 프로퍼티 사용 못함
DatabaseRow를 상속하지 않으면 코드가 깨짐
class BrokenCustomer:
first_name = Field()
last_name = Field()
prefix = Field()
suffix = Field()
"""
set_name 특별 메서드 활용
버전: 파이썬 3.6이상
class Field:
def __init__(self):
self.name = None
self.internal_name = None
def __set_name__(self, owner, name):
#클래스가 생성될 때 모든 스크립터에 대해 이 메서드 호출
self.name = name
self.internal_name = '_' + name
def __get__(self, instance, instance_type):
if instance is None:
return self
return getattr(instance, self.internal_name, '')
def __set__(self, instance, value):
setattr(instance, self.internal_name, value)
#이젠 특정 기반 클래스 상속 혹은 메타클래스 없이도 가능
class FixedCustomer:
first_name = Field()
last_name = Field()
prefix = Field()
suffix()
메타클래스를 사용하면 어떤 클래스가 완전히 정의되기 전에 클래스의 애트리뷰트를 변경
디스크립터와 메타클래스를 조합하면 강력한 실행 시점 코드 검사와 선언적인 동작 만들 수 있다
set_name 특별 메서드를 디스크립터 클래스에 정의하면 디스크립터가 포함된 클래스의 프로퍼티 이름 처리
디스크립터가 변경한 클래스의 인스턴스 딕셔너리에 데이터를 저장하게 만듦ㄴ 메모리 누수를 피할 수 있고, weakref 내장 메서드를 사용할 필요가 없다.