본문 바로가기
Python

[Python] 캡슐화

by xangmin 2022. 4. 8.
반응형

캡슐화

 - 객체의 일부 구현 내용을 외부 접근으로부터 막기 위함

 - 객체의 속성과 행동을 하나로 묶기 위함

 

class Citizen:
    """주민 클래스"""
    drinking_age = 19

    def __init__(self, name, age, resident_id):
        """이름, 나이, 주민등록번호"""
        self.name = name
        self.age = age
        self.resident_id = resident_id

    def authenticate(self, id_field):
        """본인이 맞는지 확인하는 메소드"""
        return self.resident_id == id_field

    def able_to_drink(self):
        """음주 가능 나이인지 확인하는 메소드"""
        return self.age >= Citizen.drinking_age

    def __str__(self):
        """주민 정보를 문자열로 리턴하는 메소드"""
        return f"{self.name}님은 {str(self.age)}살입니다."


taki = Citizen("타키탸키", 26, "123456")
fire = Citizen("파이리", 12, "654321")

위의 클래스는 주민을 나타내기 위한 클래스이다. Citizen 클래스는 주민 이름과 나이 주민번호를 인스턴스 변수로 가진다. 

 

메소드로는 주민번호를 이용해서 본인 인증을 하는 authenticate 메소드와 음주가 가능한 나이인지 알려주는 able_to_drink 메소드, 그리고 주민 정보를 문자열로 리턴하는 던더 str 메소드를 가지고 있다. 그런데 이 프로그램에는 몇 가지 문제점이 있다.

 

print(taki.resident_id)
taki.age = -26

 

주민의 resident_id를 출력하면 주민번호가 유출될 수 있고 age 변수의 값을 마음대로 지정할 수 있기 때문에 음수 값이 들어갈 수도 있다. 나이 값을 마음대로 정할 수 있다는 것은 또 다른 문제가 있다. 이 프로그램에는 음주가 가능한 나이인지를 확인하는 메소드가 있는데 12살 파이리 어린이의 나이를 바꾸면 음주 가능 나이로 판별이 된다.

 

이러한 문제를 해결하려면 Citizen 클래스에 캡슐화를 적용해야 한다. 캡슐화에는 두 가지 정의가 있다.

 

1. 객체의 일부 구현 내용에 대한 외부로부터 직접적인 액세스를 차단하는 것

2. 객체의 속성과그것을 사용하는 행동을 하나로 묶는 것

 

객체 내부 숨기기

클래스 외부에서 어떤 변수가 메소드에 직접 접근하는 것을 막는다. 숨기고자 하는 변수 이름 앞에 언더바 두개를 붙이면 된다.

class Citizen:
    """주민 클래스"""
    drinking_age = 19
    
    def __init__(self, name, age, resident_id):
        """이름, 나이, 주민등록번호"""
        self.name = name
        self.__age = age
        self.__resident_id = resident_id
    
    def authenticate(self, id_field):
        """본인이 맞는지 확인하는 메소드"""
        return self.__resident_id == id_field
        
    def able_to_drink(self):
        """음주 가능 나이인지 확인하는 메소드"""
        return self.__age >= Citizen.drinking_age
        
    def __str__(self):
        """주민 정보를 문자열로 리턴하는 메소드"""
        return f"{self.name}님은 {str(self.__age)}살입니다."

 

taki의 주민번호를 출력해보자.

print(taki.__resident_id)

 

다음과 같은 에러 메세지가 나온다. 이 에러 메세지는 taki 인스턴스에는 __resident_id라는 변수가 없다고 나온다.

AttributeError: 'Citizen' object has no attribute '__resident_id'

 

이번에는 메소드 앞에 언더바 두 개를 붙여보자.

def __authenticate(self, id_field):
        """본인이 맞는지 확인하는 메소드"""
        return self.__resident_id == id_field

 

__authenticate 메소드를 호출하면

taki.__authenticate("123456")

같은 에러 메세지가 뜬다. 이 말은 곧, __authenticate 메소드를 클래스 밖에서 호출할 수 없단느 것이다.

 

메소드를 통해 변수에 접근하기

이제 캡슐화가 적용된 __resident_id__age는 클래스 외부에서 값을 읽을 수도, 설정할 수도 없다. 문제는 만약 주민의 나이를 변경하고 싶어도 __age에 접근할 수가 없기 때문에 수정이 불가능하다는 것이다. 이와 같은 상황을 해결하기 위해서는 변수에 접근할 수 있는 메소드를 따로 만들어야 한다.

 

def get_age(self):
    return self.__age

 

이렇게 하면 Citizen 클래스 밖에서도 __age의 값을 읽을 수 있다. 이런 식으로 숨겨진 변수에 접근 가능한 메소드가 있으면 클래스 밖에서도 해당 변수에 바로 접근할 수 있다.

 

def set_age(self, value):
    self.__age = value

 

이렇게 하면 이제 __age 변수는 클래스 밖에서 읽을 수도 있고 설정할 수도 있다.

print(taki.get_age())
taki.set_age(12)
print(taki.get_age())
26
12

 

정리하자면, Citizen 클래스의 __age 변수는 캡슐화가 적용되었기 때문에 외부에서 바로 접근할 수는 없다. 하지만 해당 변수를 활용하는 메소드를 정의하면 접근이 가능하다.

이렇게 접근할 수 있는 메소드를 만드는 것이 바로 캡슐화의 두 번째 정의에 해당된다. 두 번째 정의에서 '하나로 묶는 것'이라는 표현이 있었는데 이 말은 '변수에 접근하는 통로를 메서드로 제한한다'는 것을 의미합니다.

 

출처 : https://velog.io/@tataki26/%EC%BA%A1%EC%8A%90%ED%99%94

반응형

댓글