Object-Oriented Programming - Property Decorators

Contents

  • Refactoring Hell
  • Property Decorators

Refactoring Hell

class Student:

    def __init__(self, first, last, health):
        self.first = first
        self.last = last
        self.health = health
        self.email = first + '.' + last + '@korea.ac.kr'

    def fullname(self):
        return f'{self.first} {self.last}'

stu_1 = Student('Tom', 'Lee', 100)

>>> print(stu_1.first) #attribute
Tom
>>> print(stu_1.fullname()) #method
Tom Lee
>>> print(stu_1.email) #attribute
Tom.Lee@korea.ac.kr

>>> stu_1.first = 'Thomas' #update first attribute
>>> print(stu_1.first) #attribute
Thomas
>>> print(stu_1.fullname()) #method
Thomas Lee
>>> print(stu_1.email) #attribute
Tom.Lee@korea.ac.kr

위 예시에는 한가지 문제점이 있다.

  1. stu_1 인스턴스를 생성
  2. fullname() 메서드를 호출
  3. email attribute 호출

위 3가지 코드는 정상적으로 실행된다. 하지만 stu_1.firstTom 에서 Thomas 라는 값으로 업데이트를 한 뒤 똑같이 출력을 해보면,

  1. stu_1.firstThomas 로 정상적으로 update
  2. stu_1.fullname() 메서드도 update 된 stu_1.first 를 통해 호출
  3. stu_1.email 은 업데이트 되기 전 Tom.Lee@korea.ac.kr 로 고정

first 는 업데이트 되었지만 email 은 업데이트 되지 않았다. 왜일까?

emailfirst 를 통해 정의되는 attribute 이지만, email attribute 는 인스턴스가 생성되는 그 순간 이미 정의되기 때문이다. 즉, stu_1.email 을 직접적으로 update 하지 않는 이상 first 를 update 하더라도 email 은 고정이다.

fullname() 메서드는 업데이트된 값으로 정상적으로 호출된다. 메서드는 본질적으로 함수이기 때문에 call 되는 순간 scope 에 올려지기 때문이다. 그렇다면 email 도 업데이트 된 first 를 반영하기 위해 메서드로 만들면 된다.

def email(self):
		return f'{self.first}.{self.last}@korea.ac.kr'

하지만 기존에 attribute 였던 코드를 메서드로 고치는 순간 아주 큰 문제가 발생한다. 기존의 사용자들은 인스턴스의 이메일을 호출하기 위해 자신의 코드를 전부 stu_1.email 에서 stu_1.email() 로 고쳐야 된다는 refactoring 문제에 직면한다.

넘파이 모듈을 예시로 들어보자. np.ndim 을 통해 객체의 차원을 호출하던 사용자들은 전부 np.ndim() 으로 고쳐야 할 것이다. 넘파이 개발자들이 이 수정을 하는 순간 전세계에 넘파이를 사용하는 모든 웹앱과 딥러닝 프레임워크는 이 작은 변화를 위해 수천 줄의 코드를 수정해야 하는 대참사가 벌어진다.

Property Decorators

바로 이런 상황을 위해 property decorator 가 존재한다.

class Student:

    def __init__(self, first, last, health):
        self.first = first
        self.last = last
        self.health = health

    def fullname(self):
        return f'{self.first} {self.last}'

    @property
    def email(self):
        return f'{self.first}.{self.last}@korea.ac.kr'

>>> stu_1 = Student('Tom', 'Lee', 100)
>>> stu_1.first = 'Thomas'
>>> print(stu_1.first)
Thomas
>>> print(stu_1.fullname())
Thomas Lee
>>> print(stu_1.email)
Thomas.Lee@korea.ac.kr

Property decorator 을 통해 기존에 attribute 였던 email 을 property 메서드로 새로 정의했다. Property 메서드는 메서드를 호출 할 시 () 를 포함하지 않아도 된다는 특징이 있다. 그렇기에 기존 코드를 사용하던 유저들은 stu_1.email 코드를 그대로 사용할 수 있게 된다.

Property decorator 는 인스턴스가 이미 선언된 후 인스턴스의 attribute 를 재정의 할 때 유용하다.