[Python]Descriptor (디스크립터)

Descriptor에 대해 알아보자.

"""
Python Advanced - Descriptor

디스크립터
1. 객체에서 서로다른 객체를 속성값으로 가지는 것.
2. Read, Write, Delete 등을 미리 정의 가능
3. Data descriptor(set, del), non-data descriptor(get)
4. 읽기 전용객체 생성에 장점, 클래스를 의도하는 방향으로 생성가능
"""


# 예1 : 기본적인 Descriptor 예제
class DescriptorEx1:
    def __init__(self, name='Default'):
        self.name = name

    def __get__(self, instance, owner):
        return f'Get method called. -> self : {self}, instance : {instance}, owner : {owner}, name : {self.name}'

    def __set__(self, obj, name):
        print('Set method called.')
        if isinstance(name, str):
            self.name = name
        else:
            raise TypeError('Name이 문자형이 아닙니다.')

    def __delete__(self, instance):
        print('Delete method called')
        self.name = None


class Sample1:
    name = DescriptorEx1()


s1 = Sample1()
s1.name = 'My name is jigi'
# s1.name = 10 --> TypeError: Name이 문자형이 아닙니다.

print('예1 > ', s1.name)
del s1.name
print('예1 > ', s1.name)
print()
"""
Set method called.
예1 >  Get method called. -> self : <__main__.DescriptorEx1 object at 0x1025aefd0>, instance : <__main__.Sample1 object at 0x1025aef70>, owner : <class '__main__.Sample1'>, name : My name is jigi
Delete method called
예1 >  Get method called. -> self : <__main__.DescriptorEx1 object at 0x1025aefd0>, instance : <__main__.Sample1 object at 0x1025aef70>, owner : <class '__main__.Sample1'>, name : None
"""


# 예2 : Property 클래스 사용 (Descriptor 직접구현)
# class property(fget=None, fset=None, fdel=None, doc=None)

class DescriptorEx2:
    def __init__(self, value):
        self._name = value

    def getVal(self):
        return f'Get method called -> self : {self}, name : {self._name}'

    def setVal(self, value):
        print('Set method called.')
        if isinstance(value, str):
            self._name = value
        else:
            raise TypeError('value가 문자형이 아닙니다.')

    def delVal(self):
        print('Delete method called')
        self._name = None

    name = property(getVal, setVal, delVal, 'Property Method Example')


s2 = DescriptorEx2('테스트 중입니다.')

# 최초값 확인
print('예2 > ', s2.name)
s2.name = '값을 변경함'

# s2.name = 10 --> TypeError: value가 문자형이 아닙니다.

del s2.name
print('예2 > ', s2.name)
print('예2 > ', DescriptorEx2.name.__doc__)

"""
예2 >  Get method called -> self : <__main__.DescriptorEx2 object at 0x1025aebe0>, name : 테스트 중입니다.
Set method called.
Delete method called
예2 >  Get method called -> self : <__main__.DescriptorEx2 object at 0x1025aebe0>, name : None
예2 >  Property Method Example
"""
"""
Python Advanced - Descriptor

디스크립터
1. 상황에 맞는 메소드 구현을 통한 객체 지향 프로그래밍 구현
2. Property와 달리 reuse(재사용) 가능
3. ORM Framework 사용
"""

# 예1 : 지정한 경로의 파일 개수 확인
import os


class DirectoryFileCount:
    def __get__(self, instance, obj_type=None):
        print(os.listdir(instance.dirname))
        return len(os.listdir(instance.dirname))


class DirectoryPath:
    # Descriptor instance;
    size = DirectoryFileCount()

    def __init__(self, dirname):
        self.dirname = dirname


# 현재 경로
s = DirectoryPath('./')
print('./ size = ', s.size)
"""
['py_ad_1_5.py', 'py_ad_2_2.py', 'py_ad_3_3.py', 'py_ad_1_1.py', 'py_ad_1_4.py', 'py_ad_2_3.py', 'py_ad_3_2.py', '__init__.py', 'py_ad_2_4.py', 'testfile4.txt', 'py_ad_1_3.py', 'py_ad_3_5.py', 'py_ad_3_1.py', 'testfile2.txt', 'py_ad_2_1.py', 'testfile3.txt', 'py_ad_2_5.py', 'testfile1.txt', 'py_ad_1_2.py', 'py_ad_3_4.py']
./ size =  20
"""

# 이전 경로
g = DirectoryPath('../')
print('../ size = ', g.size)
print()
"""
['level3', 'level2', 'venv', '.idea']
../ size =  4
"""

print('예1 dir >> ', dir(DirectoryPath))
print('예1 __dict__ >> ', DirectoryPath.__dict__)
print('예1 dir(s) >> ', dir(s))
print('예1 s.__dict__ >> ', s.__dict__)
print()
print()
"""
예1 dir >>  ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'size']
예1 __dict__ >>  {'__module__': '__main__', 'size': <__main__.DirectoryFileCount object at 0x102c2bfd0>, '__init__': <function DirectoryPath.__init__ at 0x102b25ca0>, '__dict__': <attribute '__dict__' of 'DirectoryPath' objects>, '__weakref__': <attribute '__weakref__' of 'DirectoryPath' objects>, '__doc__': None}
예1 dir(s) >>  ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'dirname', 'size']
예1 s.__dict__ >>  {'dirname': './'}
"""

# 예2
import logging

logging.basicConfig(
    format='%(asctime)s %(message)s',
    level=logging.INFO,
    datefmt='%Y-%m-%d %H:%M:%S'
)


class LoggedScoredAccess:
    def __init__(self, value=50):
        self.value = value

    def __get__(self, instance, owner=None):
        logging.info('Accessing %r giving %r', 'score', self.value)
        return self.value

    def __set__(self, instance, value):
        logging.info('Updating %r giving %r', 'score', value)
        self.value = value


class Student:
    # Descriptor instance
    score = LoggedScoredAccess()

    def __init__(self, name):
        # Regular instance attribute
        self.name = name


s1 = Student('Im')
s2 = Student('Jigi')

# 점수확인(s1)
print('예2 s1.score : ', s1.score)
s1.score = 70
print('예2 s1.score : ', s1.score)
"""
예2 s1.score :  50
예2 s1.score :  70
"""


# 점수확인(s2)
print('예2 s2.score : ', s2.score)
s2.score += 30
print('예2 s2.score : ', s2.score)
"""
예2 s2.score :  70
예2 s2.score :  100
"""

# __dict__ 확인
print('예2 vars(s1) : ', vars(s1))
print('예2 vars(s2) : ', vars(s2))
print('예2 s1.__dict__ : ', s1.__dict__)
print('예2 s2.__dict__ : ', s2.__dict__)
"""
예2 vars(s1) :  {'name': 'Im'}
예2 vars(s2) :  {'name': 'Jigi'}
예2 s1.__dict__ :  {'name': 'Im'}
예2 s2.__dict__ :  {'name': 'Jigi'}
"""


"""
2023-07-05 19:19:48 Accessing 'score' giving 50
2023-07-05 19:19:48 Updating 'score' giving 70
2023-07-05 19:19:48 Accessing 'score' giving 70
2023-07-05 19:19:48 Accessing 'score' giving 70
2023-07-05 19:19:48 Accessing 'score' giving 70
2023-07-05 19:19:48 Updating 'score' giving 100
2023-07-05 19:19:48 Accessing 'score' giving 100
"""

You may also like...

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다