[코드잇] 쉽게 배우는 파이썬 문법 - 프로퍼티(Property) 3편

코드잇

안녕하세요, 온라인 코딩 스쿨 코드잇입니다.

오늘은 지난 시간에 이어 파이썬의 프로퍼티에 대해서 알려드리도록 하겠습니다.

쉽게 배우는 파이썬 문법 - 프로퍼티(Property) 1편

쉽게 배우는 파이썬 문법 - 프로퍼티(Property) 2편

이번에는 프로퍼티가 어떤 원리로 동작하는지를 설명해드리려고 합니다. 하지만 그 전에 알아야할 게 있습니다.

그건 바로 파이썬의 디스크립터(Descriptor)가 무엇인지를 아는 겁니다.

파이썬에서 하나의 객체(A)는 다른 객체(B)를 속성으로 가질 수 있습니다. 이때 속성이 되는 그 객체(B)의 값을

- 읽거나,

- 쓰거나,

- 그 객체를 속성에서 삭제하려고 할 때

이루어질 동작이 미리 정의된 객체(B)를 디스크립터라고 합니다.

이건 말로만 설명하면 이해하기 어려우니 코드를 보여드릴게요. 아래와 같은 코드가 있다고 해봅시다.

class CharacterInfo:
    def __init__(self, power, speed):
        self.power = power
        self.speed = speed

    def __get__(self, obj, objtype):
        print('(GET)정보 조회됨')
        return ('공격력 : '+str(self.power) + ' / 스피드 : ' + self.speed)

    def __set__(self, obj, val):
        print('(UPDATE)정보 갱신 시작')
        self.power = val.power
        self.speed = val.speed

    def __delete__(self, obj):
        print('(DELETE)정보 삭제하기')
        self.power =''
        self.speed = ''


class Guardian:
    info = CharacterInfo(10, '50km/h')


g1 = Guardian()   # g1 이라는 수호천사 인스턴스 하나 생성
print(g1.info)   # 인스턴스 g1의 초기 정보 출력 
info_after_upgrade = CharacterInfo(15, '70km/h')   # 업그레이드 아이템 적용 후 캐릭터 정보
g1.info = info_after_upgrade   # 새 캐릭터 정보를 인스턴스 g1 에 설정
print(g1.info)   # 인스턴스 g1의 정보 출력
del g1.info   # 인스턴스 g1의 정보 삭제
print(g1.info)   # 인스턴스 g1의 정보 출력

지금 어떤 게임의 캐릭터 중에서 수호천사 캐릭터를 나타내는 Guardian 클래스와 각 캐릭터의 정보(공격력과 스피드)를 담고있는 Characterinfo 클래스가 있습니다. 수호천사 캐릭터라면 모두 같은 공격력과 스피드를 가질테니까 Guardian 클래스 안에 Characterinfo 클래스의 인스턴스를 info 라는 클래스 변수(속성)로 두었습니다.

Info 클래스를 살펴보면 캐릭터의 공격력을 나타내는 power, 스피드를 나타내는 speed 라는 인스턴스 변수를 갖고 있습니다. 그리고 우리가 자주 봐온 인스턴스 초기화를 위한 __init__ 메소드도 있네요. 그런데 익숙하지 않은 메소드들이 보이네요? 지금

__get__ 메소드와

__set__ 메소드와

__delete__ 메소드가

정의되어 있는데요.

바로 이런 메소드들이 정의되어 있는 클래스로 만든 객체를 디스크립터라고 합니다.(꼭 3가지 메소드 다 정의되어 있을 필요는 없습니다. 3가지 중 하나만 있어도 디스크립터입니다.) 이런 메소드들은 어떤 역할을 하는 걸까요?

지금 CharacterInfo 클래스와 Guardian 클래스를 사용하는 실행 코드에는 각 줄마다 주석이 달려있는데요. 수호천사 인스턴스의 초기 정보를 출력하고, 아이템으로 업그레이드 후에 정보를 재설정하고, 정보를 아예 삭제하는 코드입니다. 코드를 실행하면, 이런 결과가 출력됩니다.

(GET)정보 조회됨
공격력 : 10 / 스피드 : 50km/h
(UPDATE)정보 갱신 시작
(GET)정보 조회됨
공격력 : 15 / 스피드 : 70km/h
(DELETE)정보 삭제하기
(GET)정보 조회됨
공격력 :  / 스피드 :

캐릭터 정보(공격력과 스피드)가 출력되는 부분은 출력된 이유를 알겠는데 (GET)정보 조회됨 , (UPDATE)정보 갱신 시작 , (DELETE)정보 삭제하기 같은 문자열은 왜 출력됐을까요? 바로 그 이유에 디스크립터의 비밀이 숨어있습니다.

위 코드 내용 중

1. print(g1.info) 에서 이 부분(g1.info)이 실행될 때 CharacterInfo 클래스의 __get__ 메소드가 실행되기 때문에 그렇습니다.((GET)정보 조회됨 출력)

2. 이 부분(g1.info = new_info )이 실행될 때는 CharacterInfo 클래스의 __set__ 메소드가 실행되기 때문에 그렇습니다.((UPDATE)정보 갱신 시작 출력)

3. 이 부분(del g1.info)은 g1 인스턴스에서 info 속성을 없애라는 명령입니다. del은 어떤 인스턴스의 속성을 삭제할 때 사용되는 함수입니다. del g1.info 가 실행될 때는 CharacterInfo 클래스의 __delete__ 메소드가 실행되기 때문에 그렇습니다.((DELETE)정보 삭제하기 출력)

그러니까 파이썬에서는 클래스 안에

__get__ 메소드를 정의하면 그 클래스의 인스턴스를 읽으려고 할 때 __get__ 메소드가 실행되고,

그 클래스의 인스턴스를 설정하려고 할 때는 __set__ 메소드가 실행됩니다.

그리고 그 인스턴스를 속성 중에서 없애려고 할 때는 __delete__가 호출되구요.

그럼 각 메소드의 파라미터의 의미는 뭘까요?

먼저 def __get__(self, obj, objtype): 에서

self 는 디스크립터 자신(디스크립터도 하나의 인스턴스니까요)

obj 는 디스크립터를 자신의 속성으로 갖고 있는 바로 그 인스턴스

objtypeobj 의 클래스를 말합니다. 지금 이 경우에는 Guardian 클래스겠죠?

그리고 def __set__(self, obj, val): 에서

self, obj__get__ 메소드의 것과 같은 파라미터입니다.

val 는 새로 설정되는 인스턴스를 말합니다. 그러니까 g1.info = new_info 이 코드에서 new_info를 말하는 것이죠. 그러니까 지금 아까 __set__ 메소드에서 아래처럼 val 파라미터의 power 속성과 speed 속성을 가져온 것이죠.

def __set__(self, obj, val):
        print('(UPDATE)정보 갱신 시작')
        self.power = val.power
        self.speed = val.speed

마지막으로 def __delete__(self, obj): 에서

self, obj__get__ 메소드의 것과 같은 파라미터입니다.

각 메소드의 파라미터의 의미가 이해가 되시죠?

이제 디스크립터가 뭔지 정리해드릴게요.

__get__ 메소드, __set__ 메소드, __delete__ 메소드 중 하나라도 정의된 클래스가 있을 때, 그 클래스로 만든 인스턴스를 바로 디스크립터(descriptor)라고 하는 겁니다. 그리고 이 디스크립터를 다른 클래스에서 그 속성으로 사용했을 때 상황에 맞는(값 읽기, 값 설정, 값 삭제) 디스크립터의 각 메소드가 호출되는 겁니다.

디스크립터가 뭔지 이해되셨나요? 이런 기능이 왜 필요한지 궁금할 수도 있는데요. 이 디스크립터는 파이썬에서 프로퍼티(property), 클래스 메소드(classmethod), 정적 메소드(staticmethod) 등을 만드는 기본 토대가 되는 중요한 개념이자 규약입니다. 다음 번에 디스크립터에 대해 조금 더 깊은 내용을 알아봅시다.

기업문화 엿볼 때, 더팀스

로그인

/