본문 바로가기

Computer/Python

Public, Private Attributes

반응형

파이썬에서 클래스의 attribute에 대한 가시성은 공개 (public), 비공개 (private) 두 가지를 제공합니다. Attribute 이름 앞에 밑줄 두 개 (__)를 붙이면 비공개 필드가 되고 비공개 필드를 포함하는 클래스 안에 잇는 메소드에서는 해당 필드에 직접 접근할 수 있습니다. 하지만 클래스 외부에서 비공개 필드에 점 연산자 (.) 를 통해 직접적으로 접근하면 예외가 발생합니다.

class MyObject:
    def __init__(self):
        self.public_field = 5
        self.__private_field = 10

    def get_private_field(self):
        return self.__private_field

foo = MyObject()
assert foo.public_field == 5
assert foo.get_private_field() == 10
assert foo.__private_field == 10

 

마찬가지로 하위 클래스는 부모 클래스의 비공개 필드에 접근할 수 없습니다.

class MyObject:
    def __init__(self):
        self.__private_field = 10

class MyChildObject(MyObject):
    def get_private_field(self):
        return self.__private_field

foo = MyChildObject()
foo.get_private_field()

 

Private attribute 동작은 attribute 이름을 바꾸는 단순한 방식으로 구현됩니다. MyChildObject.get_private_field 같이 메서드 내부에서 private attribute에 접근하는 코드가 있으면, 파이썬 컴파일러는 __private_field라는 attribute 접근 코드를 _MyChildObject__private_field 라는 이름으로 바꿔줍니다. 위의 코드에서는 MyObject.__init__ 안에만 __private_field 정의가 들어있으므로 이 필드의 이름은 컴파일러 상에서 _MyObject__private_field 이 되는 것이죠. 이러한 상황에서 MyChildObject 하위 클래스에서 __private_field에 접근하면 변경한 attribute 이름이 _MyChildObject__private_field가 되어 attribute가 존재하지 않는다는 에러가 발생하는 것입니다. 따라서 다음과 같이 _MyObject__private_field 이름으로 접근할 때 private attribute 값을 얻을 수 있습니다.

class MyObject:
    def __init__(self):
        self.__private_field = 10

class MyChildObject(MyObject):
    def get_private_field(self):
        return self.__private_field

foo = MyChildObject()
foo._MyObject__private_field

결론적으로 파이썬 클래스에서는 private attribute를 선언하더라도 무조건적인 접근 차단을 하지 않습니다. 또한, __getattr__ / __getattribute__ / __setattr__ 등의 함수를 제공하므로 객체 내부에 자유롭게 접근 가능합니다. 그리고 private attribute를 사용하면 하위 클래스로의 확장이나 오버라이드가 매우 귀찮아집니다. 따라서 내부에 접근함으로써 생길 수 있는 피해를 줄이고자 파이썬 프로그래밍 스타일 가이드에서는 밑줄 한개로 정의한 보호 필드를 주로 사용합니다.

비공개 attribute를 사용할지 고민해야하는 경우는 하위 클래스의 필드와 이름이 충돌할 수 있는 경우 뿐으로 자식 클래스가 실수로 부모 클래스가 이미 정의한 attribute를 정의하면 충돌이 생길 수 있습니다. 보통 공개 API를 외부에 제공하는 경우에 하위 클래스 작성이 우리의 제어 밖에서 일어나고 attribute 이름이 흔할 때 (value, name 등) 충돌이 자주 발생할 수 있습니다. 이런 문제가 발생할 위험성을 줄이려면 부모 클래스 쪽에서 자식 클래스의 attribute이름이 자신의 attribute 이름과 겹치는 일을 방지하기 위해 비공개 attribute를 사용합니다.

class APIClass:
    def __init__(self):
        self.__value = 5
    
    def get(self):
        return self.__value

class Child(APIClass):
    def __init__(self):
        super().__init__()
        self._value = 'hello'

a = Child()
print(a.__dict__)
print(f'{a.get()}, {a._value}')

반응형

'Computer > Python' 카테고리의 다른 글

concurrent.futures를 이용한 병렬화  (0) 2021.06.27
데코레이터와 functools.wrap  (0) 2021.06.27
변수 영역과 클로저  (0) 2021.06.27
스레드 세이프 (Thread-safe)  (0) 2021.06.20
GIL (Global Interpreter Lock), Multi-Threading  (0) 2021.06.20