캡슐화
파이썬은 클래스를 작성할 때, 변수나 함수 앞에 '__' 를 붙여 캡슐화, 즉 은닉을 할 수 있습니다.
예를 들어 객체 내 변수에 접근할 때, 일반적으로 다음처럼 '.'과 변수명만으로 쉽게 접근할 수 있습니다.
class Robot:
def __init__(self, name, num):
self.name = name
self.num = num
robot = Robot('다코', '0001')
print(robot.name, robot.num)
'''
결과
>>> 다코 0001
'''
하지만 이것은 외부에서 변수를 쉽게 조작할 수 있음을 의미합니다.
이를 방지하기 위해 변수앞에 '__'를 붙여 외부에서 접근할 수 없도록 막을 수 있습니다.
class Robot:
def __init__(self, name, num):
self.__name = name
self._num = num # _는 __처럼 숨기지는 못하지만 숨긴다고 가정하는 표시이다.
robot = Robot('다코', '0001')
print(robot.__name) # 외부에서는 접근할 수 없다.
'''
결과
>>> AttributeError: 'Robot' object has no attribute '__name'
'''
접근하지 못한다면 왜 변수를 할당하냐고 생각할 수 있겠지만, 외부에서 직접 접근하지 못할 뿐 내부에서는 사용 가능합니다.
class Robot:
def __init__(self, name, num):
self.__name = name
self._num = num
def call(self):
print(f'이름: {self.__name}, 일련번호: {self._num}, 호출합니다.') # 내부 호출
robot = Robot('다코', '0001')
robot.call() # 메서드 호출시에는 변수 사용가능.
'''
결과
>>> 이름: 다코, 일련번호: 0001, 호출합니다.
'''
이렇듯 중요한 데이터이거나 조심히 다뤄야하는 변수 혹은 함수의 경우 캡슐화를 통해 외부의 접근을 제한할 수 있습니다.
하지만 그럼에도 외부의 조작이 필요한 때가 있을 수 있습니다.
그런 경우에는 @property 데코레이터를 사용하여 변수를 가져오거나 수정할 수 있습니다.
@property와 getter
우선 @property를 이용해 변수에 접근(getter)하는 방법입니다.
class Robot:
def __init__(self, name, num):
self.__name = name
self._num = num
@property
def name(self): # 여기서 함수명은 곧 변수명처럼 다뤄진다.
return self.__name
robot = Robot('다코', '0001')
print(robot.name)
'''
결과
>>> 다코
'''
'robot.name'이라는 명령으로 name 변수를 불러올 수 있음이 확인됩니다.
여기서 name은 항수명이지만 '()'을 붙이지 않고 마치 변수명을 호출하듯이 사용합니다.
(엄밀히 말하면 '__name'을 직접 불러오는 게 아닌, 함수를 사용해 불러오는 방식입니다.)
하지만, 접근이 되었다하더라도 name을 외부에서 수정할 수는 없습니다.
이것은 단순히 변수를 호출(getter)한 것에 지나지 않습니다.
# 만약 변수를 외부에서 재할당 한다면 다음과 같은 에러를 만나게 됩니다.
robot.name = '스누피'
'''
결과
>>> AttributeError: can't set attribute
'''
@property와 setter
외부에서 변수를 수정해야하는 경우에는 setter를 사용해야 합니다.
class Robot:
def __init__(self, name, num):
self.__name = name
self._num = num
@property
def name(self):
return self.__name
@name.setter
def name(self, new_name): # 여기서 함수명은 곧 변수명처럼 다뤄진다.
'''
...유효성 검사...
'''
self.__name = new_name
return self.__name
robot = Robot('다코', '0001')
print(robot.name)
robot.name = '스누피' # setter 함수가 호출된 순간이다.
print(robot.name)
'''
결과
>>> 다코
>>> 스누피
'''
setter는 ['@property를 사용한 함수명'.setter] 형태로 사용합니다.
위의 코드 robot.name = '스누피'가 동작하는 시점에 setter 함수가 호출되고 변수가 새롭게 할당됩니다.
이후 name 호출 시에 '스누피'가 표시됩니다.
이제 setter를 활용하여 숨겨진 변수를 외부에서 할당까지 할 수 있게 되었습니다.
그러나 여기서 의문이 들 것입니다.
이럴 거면 왜 숨기는 거지?
일반 변수를 사용하면 쉽게 할 수 있는 것을 굳이 어렵게 만든 것처럼 보입니다.
하지만 setter는 단순히 데이터를 수정하는데 그치지 않습니다.
해당 코드를 다시 보겠습니다.
@name.setter
def name(self, new_name):
'''
...유효성 검사...
'''
self.__name = new_name
return self.__name
중간에 '유효성 검사'라고 기재되어있습니다.
파이썬에서 변수 재할당은 그 내용은 물론 타입까지도 상관없이 재할당 해버립니다.
만약 문자를 할당해야 하는 변수인데 숫자를 할당하면 버그가 생길 겁니다.
하지만 setter는 그러한 문제를 방지할 수 있습니다.
새롭게 들어온 new_name의 변수가 유효한 데이터인지 확인할 수 있는 것입니다.
setter는 외부에서는 단순히 변수를 호출하는 것처럼 보이지만
그 내부는 함수로 구성되어 있기 때문에 개발자는 함수 내에 코드를 추가할 수 있습니다.
@name.setter
def name(self, new_name):
# new_name이 문자가 아니라면 ValueError
if not type(new_name) == str:
raise ValueError
self.__name = new_name
return self.__name
이제 name을 재할당할 때 문자가 아닌 타입이 들어오면 ValueError를 내뱉습니다.
이렇듯 개발자는 미리 작성해둔 setter 함수로 객체의 변수를 보다 안전하게 관리할 수 있습니다.
정리
- 캡슐화는 내부 변수나 함수를 보다 private 하게 관리하기 위해서 사용합니다.
- 파이썬에서 객체는 내부 변수나 함수 앞에 '__'을 붙여 외부의 접근을 막을 수 있습니다. (캡슐화)
- 외부의 접근을 막았지만 필요한 경우 @property를 사용해 접근할 수 있습니다. (getter)
- 외부에서 수정을 요청할 경우 유효성 및 추가 작업을 수행할 수 있습니다. (setter)
레퍼런스
- 타입 파이썬! 올바른 class 사용법과 객체지향 프로그래밍
- 캡슐화 2
'나는 이렇게 학습한다 > Language' 카테고리의 다른 글
Python _ @dataclass 사용법과 타입 확인 (0) | 2022.05.18 |
---|---|
Python _ 런타임 중에 스크립트 파일 실행하기 (0) | 2022.04.28 |
Python _ @classmethod, @staticmethod 란 무엇인가? (0) | 2022.03.31 |
Python _ magic method를 사용하여 객체 커스텀하기 (0) | 2022.03.31 |
Python _ isinstance로 타입을 체크하자. (0) | 2022.03.30 |