클래스 애트리뷰트를 표시하라

매일 공부(ML)·2022년 7월 19일
0

이어드림

목록 보기
102/146

set_name으로 클래스 애트리뷰트를 표시하라


메타클래스 기능

  • 클래스가 정의된 후 클래스가 실제로 사용 전 프로퍼티 변경 혹시 표시

  • 디스크립터 활용

    • 애트리뷰트가 포함된 클래스 내부 깊이 살피기
#애트리뷰트와 컬럼 이름 연결
#컬럼 이름을 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()

Summary

  • 메타클래스를 사용하면 어떤 클래스가 완전히 정의되기 전에 클래스의 애트리뷰트를 변경

  • 디스크립터와 메타클래스를 조합하면 강력한 실행 시점 코드 검사와 선언적인 동작 만들 수 있다

  • set_name 특별 메서드를 디스크립터 클래스에 정의하면 디스크립터가 포함된 클래스의 프로퍼티 이름 처리

  • 디스크립터가 변경한 클래스의 인스턴스 딕셔너리에 데이터를 저장하게 만듦ㄴ 메모리 누수를 피할 수 있고, weakref 내장 메서드를 사용할 필요가 없다.

profile
성장을 도울 아카이빙 블로그

0개의 댓글