본문 바로가기
코딩 연습/코딩배우기

[python] 파이썬의 정보은닉(Information Hiding)과 __dict__ 메서드에 대해

by good4me 2020. 9. 13.

goodthings4me.tistory.com

 

■ 파이썬의 객체 외부에서 객체 내에 있는 변수(속성)나 메서드를 수정할 수 없게 하기 위한 코딩 규칙은 attribute, method 앞에 밑줄('_', undercore) 하나를 붙이는 것이다. 

그리고, __dict__ 메서드로 변수(속성)정보를 확인하고, 속성값을 수정할 수 있지만, 이를 제한하고 싶을 경우 밑줄  두 개를 붙이는 방법이 있다.

 

# 변수(속성)값을 수정할 수 있다. (잘못된 접근과 값의 수정)

class Person:
    def __init__(self, n, a):
        self.name = n
        self.age = a
    
    def __str__(self):
        return '{0}: {1}'.format(self.name, self.age)
    

def main():
    p = Person('James', 22)
    print(p)
    print(dir(p))
    p.age -= 1
    print(p)
    print(p.__dict__)  # {'name': 'James', 'age': 21}


main()

※ __dict__ 는 해당 객체 내에 있는 변수(속성) 정보를 담고 있는 딕셔너리로 파이썬 각 객체마다 가지고 있는 속성임

▷print(dir(p)) 결과 

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age''name']

 

# __dict__ 를 통해 변수의 값을 수정할 수 있다.

class Simple:
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return '이름은 %s 입니다.' % self.name


def main():
    sp = Simple('Kim')
    print(sp)
    sp.__dict__['name'] = 'Park'
    print(sp)
    

main()



[실행 결과]

이름은 Kim 입니다.
이름은 Park 입니다.

good4me.co.kr

 

 

■ 파이썬의 정보은닉(Infomation Hiding) 

파이썬에는 접근지정자라는 개념이 없지만, 객체(인스턴스) 변수(또는 메소드) 앞에 밑줄('_', underscore) 한 개를 붙여 private 표현을 한다. 즉, 개발자들 간의 약속, "밑줄(_) 한 개가 붙으면 private 속성이니 접근하지 말라는 명시적 표현"이라는 의미이다. 

# 참고로, 밑줄 두 개를 붙이면 외부 접근이 불가한 private 효과를 만들 수는 있지만, 실제로 private은 아니다.

# 이는 Name Mangling(내부적인 어떤 규칙을 통해 함수의 이름을  바꾸는 것)으로 불리는 것으로, 클래스의 확장 시 메서드의 충돌없는 오버라이드를 위해 만들어진 것이라고 한다.

# 이를 private 효과를 본다고 생각하면 안되며, 일부 오류(부작용)를 초래할 수 있기 때문에 파이썬에서는 객체의 인터페이스 용도(접근 용도)가 아닌 속성(또는 메서드)에는 밑줄(_) 두 개가 아닌 하나를 붙여주는 것이 좋다.

 

▶ 밑줄(_) 한 개와 두 개의 차이, 그리고 접근 여부와 방법 테스트 #1

class InfoHiding:
    def __init__(self):
        self.public_value = 1
        self._protected_value = 2
        self.__private_value = 3
        
    def _protected(self):
        print('_protected 입니다.')
    
    def __private(self):
        print('__private 입니다.')


h1 = InfoHiding()

print(h1.__dict__)
# {'public_value': 1, '_protected_value': 2, '_InfoHiding__private_value': 3}
# "__private_value" 속성명은 "_InfoHiding__private_value"로 변경됨(파이썬 내부적으로 사용)

h1.public_value += 1

h1._protected_value += 1

#h1.__private_value += 1  # 오류 발생
# AttributeError: 'InfoHiding' object has no attribute '__private_value'
# 변수 앞에 "__"가 있는 것은 외부에서 접근 불가

print(h1.public_value)  # 2

print(h1._protected_value)  # 3

#print(h1.__private_value)
# AttributeError: 'InfoHiding' object has no attribute '__private_value'

print(h1.__dict__)
# {'public_value': 2, '_protected_value': 3, '_InfoHiding__private_value': 3}
# "__private_value" 제외한 2개의 속성(변수)는 접근 가능하여 값이 수정됨

h1.public_value = 10
h1._protected_value = 20
h1.__private_value = 30

print(h1.__dict__)
# {'public_value': 10, '_protected_value': 20, '_InfoHiding__private_value': 3, '__private_value': 30}
# 2개의 속성(변수)값이 수정되었고, "__private_value" 속성이 추가됨 (정보은닉된 속성 아님)

print(h1.public_value)  # 10
print(h1._protected_value)  # 20
print(h1.__private_value)  # 30

print(h1._InfoHiding__private_value)  # 3

h1._protected()  # _protected 입니다.

#h1.__private()  # 오류 발생
# AttributeError: 'InfoHiding' object has no attribute '__private'

h1._InfoHiding__private()  # __private 입니다.

 

 

▶ 밑줄(_) 한 개와 두 개의 차이, 그리고 접근 여부와 방법 테스트 #2

# 객체 내에 있는 attribute, method 앞에 밑줄 두 개('__' double underscore)를 붙이면 객체 외부에서 해당 이름으로 접근할 수 없게 된다.

class Person2:
    def __init__(self, n, a):
        self.__name = n
        self.__age = a
    
    def __str__(self):
        return '{0}: {1}'.format(self.__name, self.__age)
    

def main():
    p = Person2('James', 22)
    print(p)  # James: 22  --> __str__ 호출 결과
    #p.__age += 1  # AttributeError: 'Person2' object has no attribute '__age'
    #print(p)
    
    print(dir(p))
    print(p.__dict__)
    # {'_Person2__name': 'James', '_Person2__age': 22}
    
    p.__dict__['__name'] = 'Choi'  # __name 속성값 수정 시도
    # _Person2__name이 아닌 해당 객체의 변수로 추가됨(주어진 값 할당됨)
    
    print(p.__dict__)
    # {'_Person2__name': 'James', '_Person2__age': 22, '__name': 'Choi'}
    
    print(p)  # James: 22
    print(p.__name)  # Choi  --> p 객체의 변수값
    print(dir(p))


main()

※ attribute, method 앞에 __(double underscore)를 붙이면, 해당 이름이 "_Classname__속성명 또는 메소드명"으로 변경됨
※ __(double underscore)가 붙은 변수(속성)값 수정하려고 할 경우, 본래 목적이 아닌 해당 객체의 변수로 추가됨

 

 

▶ print(dir(p))  --> p.__dict__['__name'] = 'Choi'  하기 전

['_Person2__age', '_Person2__name', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', __weakref__']

 

 print(dir(p))  --> p.__dict__['__name'] = 'Choi'  한 후

['_Person2__age', '_Person2__name', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__name', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

 

 

[참고 자료] 윤성우의 열혈파이썬 중급편

 

댓글