파이썬에는 @property라는 데코레이터가 있습니다. @property를 사용하면 보다 pythonic 하게 코드를 작성할 수 있다는 장점이 있습니다. 또 어떤 상황에서 파이썬 @property 데코레이터를 사용하면 좋을까요 ?
@property를 알아가기전 접근제한자와 접근제한자가 등장한 배경인 캡슐화에 대해 간단하게 짚고 넘어가겠습니다.
💡 캡슐화(Encapsulation): 객체의 속성과 행위를 하나로 묶고 실제 구현 내용 일부를 외부에 감추어 은닉
💊 캡슐화는 OOP의 특징 중 하나이며, 객체가 독립접으로 역할을 수행하기 위해 필요한 데이터와 기능을 한 곳에 패키징 한 것을 의미합니다. 패키징 된 다양한 기능은 캡슐과 같이 감싸져 있어 외부에서는 객체의 내부 구현을 알기 어렵습니다
💊 따라서 객체가 외부 공개를 허용한 일부의 인터페이스를 통해서만 객체에 접근 할 수 있습니다. 이를 통해 외부에 의한 중요 정보 탈취 및 무차별적인 객체 손상을 방지함으로써 정보은닉이 실현될 수 있습니다
💊 캡슐화를 통해 정보은닉이라는 하나의 목적을 달성하기 위해 접근제한자
를 이용하게 됩니다
🔒 접근제한자는 객체 정보은닉을 달성하기위해 객체에 존재하는 속성(필드)와 기능(메서드)에 대한 접근을 제한하는 역할을 수행합니다
🔒 접근제한자는 크게 private
, default
, protected
, public
4가지가 있습니다. 일반적으로는 완전공개(public)와 완전비공개(private)를 주로 사용합니다. 자바의 경우 객체의 속성이나 매서드 앞에 접근제한자를 붙힘으로써 접근 제한 여부를 나타냅니다.
구분 | 공개부분 | 접근불가능 |
---|---|---|
public | 같은 클래스 내에서 접근 가능 | 없음 |
default | 패키지 공개 | 자식 패키지 + 다른 패키지 |
protected | 패키지 공개 + 자식 패키지 | 다른 패키지 |
private | 같은 클래스 공개 | 다른 패키지, 다른 클래서 |
🔒 private로 보호된 필드는 외부에서 접근이 불가능합니다. 따라서 getter/setter method를 공개하여 외부에서 메소드를 통해 접근하도록 유도하여, 데이터를 우회하여 가져오거나 변경하며 객체의 무결성을 유지할 수 있습니다
🔒 특히 setter의 경우, 내부 속성의 값을 다시 설정할 때 특정 조건을 두어 재설정에 대한 안정성을 높힐 수 있습니다
🔐 파이썬에는 별도의 접근제한자가 없지만(?) 있습니다(?)
🔐 public, private와 같은 명시적인 접근제한자는 없지만 네이밍 컨벤션을 통해 접근 여부를 표기합니다.
def __init__(self):
self.public_property = 1
self._protected_property = 1
self.__privated_property = 1
구분 | 공개부분 | 접근불가능 |
---|---|---|
public_property | 외부에서 접근 가능 | 없음 |
_protected_property | 자기 자신 클래스와 상속된 클래스에서 사용 가능 | 없음 |
__privated_property | 자기 자신 클래스 | 외부에서 접근 불가능. 접근시 에러 발생 |
🔐 파이썬도 마찬가지로 접근 제한자가 지정되어 있는 변수들에 대한 getter/setter method를 생성하여 객체 내부의 속성을 읽거나 수정할 수 있습니다.
# getter / setter 선언
class GenG:
def __init__(self): # 객체 초기화
self.__jungle = 'peanut'
def get_jungle(self):
return self.__jungle
def set_jungle(self, value):
self.__jungle = value
# getter / setter 호출
gen_g = GenG()
print(gen_g.get_jungle()) # peanut
gen_g.set_jungle('youngjae')
print(gen_g.get_jungle()) # youngjae
📍 파이썬의 @property 데코레이터는 getter/setter를 좀 더 우아하고 간결하게 표현할 수 있습니다
class GenG:
def __init__(self): # 객체 초기화
self.__jungle = 'peanut'
@property
def jungle(self):
return self.__jungle
@jungle.setter
def jungle(self, value):
if not getattr(value, str):
raise '타입이 맞지 않습니다'
self.__jungle = value
# getter / setter @property 호출
gen_g = GenG()
print(gen_g.jungle) # peanut
gen_g.jungle = 'youngjae'
print(gen_g.jungle) # youngjae
📍 기존 getter / setter과의 차이가 보이시나요 ?
📍 여기서 주의할 점은 @property 가 @getter.setter보다 위에 있어야합니다
🖋 기본적으로 @property의 목적은 객체의 프로퍼티에 직접 접근을 제한하기 위해 사용합니다
🖋 @property를 잘 활용하면 코드의 재활용성과 가독성이 높아지고, 유지보수가 용이합니다
🖋 코드를 작성하다보면 자연스럽게 조건 분기를 타는 경우가 많습니다. 하지만 조건 분기가 많아질수록 코드의 가독성이 떨어질 수 있는데 이때 @property를 잘 활용할 수 있습니다
🖋 2개 이상의 조건이나 단순 3항연산자로 표현하기에는 길어지거나, 그 의미를 파악하기 어려울 경우에 @property를 활용하면 보다 깔금한 코드를 작성할 수 있습니다
🖋 복잡한 연산이지만 별도의 매개변수 없이 객체 내부의 속성의 값으로만 연산하여 값을 도출 할 수 있을 때 @property를 활용하면 보다 직관적으로 나타낼 수 있습니다