상세 컨텐츠

본문 제목

access modifier (접근지정자)

개발/python-객체지향프로그래밍(OOP)

by Matthew0633 2022. 5. 27. 23:57

본문

access modifier (접근지정자)

파이썬에서는 class 변수나 instance 변수를 정의할 때, 기본적으로 public 한 성질을 가지고 있고, protected, private 에 대한 강제성이 없다. 이와 관련된 python 공식문서의 일부분이다.

(9.6. Private variables) “Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.

강제성이 없다는 것은 private과 protected 에 대해 다른 언어와 같은 기능적 제한이 없다는 것이다. 이는 자유도를 높이고자 하는 파이썬만의 철학에서 비롯된다.

그러나, 개발자들은 일종의 naming convention 지키며 파이썬 코드들에서 access modifier 을 명시하고 있다고 적혀있다. 이는 아래와 같다.

  • name : public
  • _name : protected
  • __name : private

파이썬 내에서는 private 을 위해, 해당 개념과 관련된 제한된 방식으로서 name mangling 을 지원한다고 말한다.

(9.6. Private variables) Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for such a mechanism, called name mangling. Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.

naming을 통해 protected, private 변수로 표시했을 경우, class 밖에서의 직접적인 접근 또는 값 수정을 장려하지 않는다는 뜻으로 받아들일 수 있다.

재밌는 것은 private 으로 표시할 때는 해당 변수에 name mangling 이 일어난다. 변수명이 임의로 바뀌게 되어 class 밖에서 해당 변수의 이름으로는 접근 또는 값 수정이 불가하게 된다. 이는 아래서 확인할 수 있다. a.__y 로 접근했을 때는 변수가 없다고 나온다. 그러나, 파이썬의 철학답게 이것이 완전히 불가능한 건 아니다. a._SampleA__y 와 같이 변경된 변수이름으로 접근하면, 접근과 값수정이 가능하다.

class SampleA:
    def __init__(self):
        self.x = 0
        self.__y = 0
        self._z = 0
 
a = SampleA()
a.x = 1

print(f'x : {a.x}')
# print('Ex2 > y : {}'.format(a.__y)) # 예외발생

print(f'z : {a._z}')
print('dir(a)) # _SampleA__y

# a._SampleA__y = 2 # 수정 가능
# print('Ex2 > y : {}'.format(a._SampleA__y))

 

그러나 위와 같은 접근 또는 값 수정은 private 과 맞지 않기에, 해당 변수값에 대한 getter 와 setter 메소드를 따로 정의하는 것이 일반적이다.

# Ex3
# 메소드 활용 Getter, Setter 작성

class SampleB:
    def __init__(self):
        self.x = 0
        self.__y = 0
 
    def get_y(self):
        return self.__y
 
    def set_y(self, value):
        self.__y = value

b = SampleB()

b.x = 1
b.set_y(2)


print(f'x : {b.x}')
print(f'y : {b.get_y()}')

# 변수 접근 후 수정 부분에서 일관성, 가독성 하락
print(dir(b)) # _SampleB__y

그런데, private 변수가 매우 많다면 getter, setter 메소드가 매우 많아지게 되어 코드가 비효율적으로 될 것이다. 이를 피하기 위해 property 를 활용하여 간결하게 private 변수를 위한 메소드를 정의할 수 있다.

Property 를 사용했을 때의 사용 장점을 정리하면,

  1. 파이써닉한 코드의 사용
  2. private 변수에 대한 제약 설정 가능
  3. Getter, Setter 사용의 장점 지속 (코드 일관성)
    • 캡슐화-유효성 검사 기능 추가 용이
    • 대체 표현(속성 노출, 내부의 표현 숨기기 가능)
    • Getter, Setter 작동에 대해 설계된 여러 라이브러리(오픈소스) 상호 운용성 증가

private 변수를 다룰 때, decorator + property 를 통해 getter, decorator + setter 을 통해 setter, decorator + deleter 을 통해 deleter 를 정의할 수 있다.

# Property 활용 Getter, Setter 작성

class SampleA:
    def __init__(self):
        self.x = 0
        self.__y = 0 # private

    @property
    def y(self):
        # print('Called get method.')
        return self.__y
    
    @y.setter
    def y(self, value):
        # print('Called set method.')
        self.__y = value

    @y.deleter
    def y(self):
        print('Called del method.')
        del self.__y

a = SampleA()

a.y = 2 # setter 동작
print(f'y : {a.y}') # getter 동작

# deleter 동작
del a.y
print(dir(a)) # a.__y 삭제됨

 

setter 을 통해 제약조건까지 설정하게 되면 private 변수의 올바른 값 수정을 권장할 수 있고, 캡슐화를 강화하게 된다.

# Property 활용 제약 조건 추가

class SampleB:
    def __init__(self):
        self.x = 0
        self.__y = 0 # private

    @property
    def y(self):
        # print('Called get method.')
        return self.__y
    
    @y.setter
    def y(self, value):
        # print('Called set method.')
        if value < 0:
            raise ValueError('0 보다 큰 값을 입력하세요.')
        self.__y = value

    @y.deleter
    def y(self):
        print('Called del method.')
        del self.__y

b = SampleB()

b.x = 1
b.y = 10
# b.y = -5 # 예외발생

print(f'x : {b.x}')
print(f'y : {b.y}')

'개발 > python-객체지향프로그래밍(OOP)' 카테고리의 다른 글

Context Manager  (0) 2022.05.27
Mix-in Class  (0) 2022.05.25
Duck typing 과 Nominative typing, Structure typing  (0) 2022.05.25
Type Annotation (9) - Generic Type  (0) 2022.05.25
Type Annotation (8) - Final Type  (0) 2022.05.23

관련글 더보기

댓글 영역