접근 제어자

객체지향 기반 프로그래밍에서는 접근 제어자 즉, private, protected, public을 이용한 정보 은닉을 통해 외부로부터 데이터를 보호한다. 이를 통해 객체의 속성과 메소드를 하나로 묶고, 실제 구현 내용의 일부는 외부에 감추어 은닉할 수 있는 캡슐화가 가능한다. 각각의 접근 제어자는 다음과 같은 의미를 가진다.

image

접근 제어자 문법 의미
Public name 외부로부터 모든 접근 허용
Protected _name 자기 클래스 내부 혹은 상속받은 자식 클래스에서만 접근 허용
Private __name 자기 클래스 내부의 메서드에서만 접근 허용

예제

Public

공개적으로 모든 함수, 변수, 메소드를 공개적으로 사용할 수 있다. 즉, 다른 모든 클래스는 제한없이 쉽게 액세스 할 수 있다. Public Member는 일반적으로 클래스 외부에서 액세스 할 수있는 클래스에서 선언 된 메소드며 모든 일반 클래스는 기본적으로 Public 클래스다.

>>> ## 원 넓이 클래스
>>> class Circle:
    def __init__(self, radius, name):
        self.radius = radius
        self.name = name

    def get_name(self):
        return self.name

    def get_area(self):
        return (3.14 * self.radius) ** 2

>>> circle = Circle(5, '500원 동전')
>>> print(circle.get_name(), circle.get_area())
500 동전 246.49000000000004

>>> circle.radius = 3
>>> circle.name = '50원 동전'
>>> print(circle.get_name(), circle.get_area())
50 동전 88.7364

Protected

Protected Member는 prefix로 ‘_‘를 사용하며, Sub Class에서 사용하겠다는 의미이다. 그러나 강제성은 없으므로 Public과 거의 동일하게 외부 접근가능하다.

>>> ## 학생 정보 클래스 2
>>> class Student1:
    # 생성자
    def __init__(self, name, roll, branch):
        self._name = name
        self._roll = roll
        self._branch = branch
    
    # Roll, Branch 출력 메소드 (Protected)
    def _roll_branch(self):
        # Protected data 출력
        print("Roll: ", self._roll)
        print("Branch: ", self._branch)

>>> ## 학생 정보 클래스 2 (Student1 상속)
>>> class Student2(Student1):
    # 생성자
    def __init__(self, name, roll, branch):
        Student1.__init__(self, name, roll, branch)

    # Name, Roll, Branch 출력 메소드
    def details(self):
        # Student1의 Protected 변수, 메소드
        print("Name: ", self._name)
        self._roll_branch()

>>> info = Student2("Eliot", "학부생", "바이오메디컬학과")
>>> info._branch = "컴퓨터과학과"
>>> info.details()
Name: Eliot
Roll: 학부생
Branch: 컴퓨터과학과

Private

Private Member의 경우 변수와 함수는 클래스 내에서만 액세스 할 수 있다. 클래스를 Private으로 선언하기 위해 prefix로 “__“를 사용한다. 기본적으로 자기 클래스 외부에서 Private Member에 대한 접근은 금지된다.

>>> ## 계좌 관리 클래스
>>> class Account:
        # 생성자 // 초기 금액 0 (Private)
        def __init__(self, seed = 0):
            self.__seed = seed              

        # 출금 메소드
        def get_money(self, money):         
            self.__seed -= money
            return self.__get_balance()

        # 입금 메소드
        def save_money(self, money):
            self.__seed += money
            return self.__get_balance()

        # 잔고 확인 메소드(Praivate)
        def __get_balance(self):
            return self.__seed
        
        # 잔고 확인 메소드(Public) // 이름 명명 시 뒤에 __가 붙게 되면 Public Member로 간주
        def __get_status__(self):
            return self._seed               

>>> a = Account()
>>> a.__seed = 2000                           # Private Member이므로 반영 X

>>> print(a.save_money(5000))
5000

>>> print(a.get_money(1000))
4000

>>> print(a.__get_status__())
4000

>>> print(a.__get_balance())                  # Private Member이므로 ERROR!
AttributeError: 'Account' object has no attribute '__get_balance'

Private Member는 Python의 Name Mangling 기능에 따라 ‘_class__name’형태로 변환된다. 즉, Sub Class에서 이름 충돌을 피하기 위한 조치로 추후 확장 시 충돌이 발생하지 않도록 사전에 방지한다. 다음과 같이 dir() 명령어를 통해 객체의 속성을 확인하면 Private Member인 ‘__get_balance’는 Name Mangling에 의해 ‘_Account__get_balance’로 변경됐다.

>>> print(dir(a))
['_Account__get_balance', '_Account__seed', '__class__', '__dict__' ... 'get_money', 'save_money']

>>> a._Account__seed = 0                      # name mangled variable
>>> print(a._Account__get_balance)            # name mangled function
0

그러나 해당 속성을 통해 값을 수정하거나 호출해도 동작하는 것을 확인할 수 있다. 즉, 파이썬의 접근 제어자는 완벽한 Private 기능은 아니며, Sub Class에서 변수가 재정이되지 않도록 방지하는 성격의 기능으로 이해하는 것이 좋겠다.

참고

태그:

카테고리:

업데이트:

댓글 남기기