스쿼드

[스쿼드] 캡슐화

kinggoddino 2024. 8. 6.

1. 퍼블릭 변수 (Public Variable) : variable

변수 맘대로 쓰라는 뜻.

클래스 외부에서 자유롭게 접근할 수 있는 변수.

다른 클래스나 객체에서 직접 읽거나 쓸 수 있다.

 

2. 프로텍티드 변수 (Protected Variable) : _variable

변수 안바꾸길 권장한다는 뜻.

동일 패키지 내의 다른 클래스와 상속 관계에 있는 하위 클래스에서 접근 가능한 변수.

클래스 외부에서는 직접 접근할 수 없다.

 

3. 프라이빗 변수 (Private Variable) : __variable

변수 바꾸지 마

해당 클래스 내부에서만 접근할 수 있는 변수.

다른 클래스나 객체에서는 직접 접근할 수 없다.

접근을 위해서는 클래스 내부에 접근자 메소드와 설정자 메소드를 제공해야함.

class Private_Var:         # Private_Var 클래스 만들기
    def __init__(self):    # 초기화메서드
        self.x = 1         # 변수x 는 public
        self._y = 2        # 변수y 는 protected
        self.__z = 3       # 변수z 는 private
        self.__c = 8       # 변수c 는 private


a = Private_Var()                   # Private_Var 클래스로 a 객체 만들기

print(f'public 변수 > {a.x}')       # 1 (public 변수라서 그냥 출력됨)

# print(f'private 변수 > {a.__c}')    # AttributeError: 'Private_Var' object has no attribute '__c'
                                      # (private 변수는 출력시 예외 처리되어있음)

print(dir(a))
# 네임스페이스(객체 속성 공간들) 에서 변수만 확인해보기
# ['_Private_Var__c', '_Private_Var__z', '_y', 'x']
# private 처리한 애들은 '_클래스명__변수명' 으로 이름이 변경되어 있음!!

 

getter 메서드

접근자 메서드 : private 변수 값을 반환한다

 

setter 메서드

설정자 메서드 : private 변수 값을 재설정한다

class Private_Var_2:        # Private_Var_2 클래스 만들기
    def __init__(self):     # 초기화메서드
        self.x = 1          # 변수x 는 public
        self._y = 2         # 변수y 는 protected
        self.__z = 3        # 변수z 는 private
        self.__c = 8        # 변수c 는 private

    def get_c(self):            # getter 메서드
        return self.__c         # __c의 값을 반환한다.(클래스 내부라서 접근할 수 있다)

    def set_c(self, value):     # setter 메서드
        self.__c = value        # __c의 값을 value로 재선언한다.(클래스 내부라서 접근할 수 있다)


a = Private_Var_2()     # Private_Var_2 클래스로 a 객체 만들기
print(a.get_c())        # 8     # getter 메서드를 통해 __c 값 출력

a.set_c(88)             # setter 메서드를 통해 __c 값을 88로 변경
print(a.get_c())        # 88    # 변경된 c 값 출력해보기

 

@property 데코레이터

메서드를 객체의 속성처럼 사용할 수 있게 해줌

메서드를 호출하지 않고도 속성에 접근하듯이 값을 가져올 수 있음

 

@setter 데코레이터

특성 속성에 대한 값을 설정하는 메서드를 정의

속성에 값을 할당할 때 자동으로 이 메서드가 호출됨

class Private_Var_3:        # Private_Var_3 클래스 만들기
    discount_rate = 0.9     # 클래스 변수

    def __init__(self):     # 초기화메서드
        self.x = 1          # 변수x 는 public
        self._y = 2         # 변수y 는 protected
        self.__z = 3        # 변수z 는 private
        self.__c = 8        # 변수c 는 private

    @property
    # @property 데코레이터
    # 메서드를 객체의 속성처럼 사용할 수 있게 해줌. 
    # 메서드를 호출하지 않고도 속성에 접근하듯이 값을 가져올 수 있음.
    def c(self):
        return self.__c * Private_Var_3.discount_rate
        # __c에 discaount_rate를 적용한 값을 반환하는 property
        # 클래스변수이므로 변수명 앞에 .클래스명 붙여주기 

    @c.setter
    def c(self, value):
    # @property_name.setter 데코레이터
    # 특정 속성에 대한 값을 설정하는 메서드를 정의
    # 이 속성에 값을 할당할 때 자동으로 이 메서드가 호출됨
        if value < 0:
            raise ValueError('불편하네요')
        self.__c = value
        # __c의 값을 설정하는 setter. 값이 0보다 작다면 예외 처리
        # ValueError '불편하네요' 가 발생한다.


a = Private_Var_3()     # Private_Var 클래스로 a 객체 만들기

print(a.c)              # 7.2           # property를 통해 __c에 discount 적용한값 출력

a.c = 888888888         # setter를 통해 __c의 값을 888888888로 설정
print(a.c)              # 799999999.2   # 변경된 __c의 값에 할인율 적용한값 출력

a.c = 8                 # setter로 __C를 8로 설정
print(a.c)              # 7.2           # 변경된 __c의 값에 할인율 적용한값 출력

a.c = -8                # ValueError: 불편하네요
                        # setter를 통해 __c를 변경하려 했지만 0보다 작아서 예외 발생

 

 

4. 캡슐화 (Encapsulation)

 

이렇게 protected, private 변수를 사용해서 데이터를 보호하는 것을 캡슐화 라고 함.

클래스 내부 상태를 보호하고, 객체 간 상호작용을 명확하게 정의할 수 있다.

데이터 변경을 통제함으로써 코드의 유지보수도 용이함!

객체 지향 프로그래밍 개념중 하나.

 

 

# 숙제 - 전부 다 주석 다세요

students = [                                                   # students 변수 만들기
    {'name': 'won', 'age': 4444, 'grade': 'A'},                # 각 학생의 정보(이름,나이,등급)를
    {'name': 'babo', 'age': 444, 'grade': 'B'},                # 딕셔너리 키:밸류 형태로
    {'name': 'baboo', 'age': 44, 'grade': 'C'},                # students 리스트에 저장해준다
    {'name': 'baboo', 'age': 44, 'grade': 'C'},                # 세번째 네번째는 아예 똑같네 귀찮으셨나
]

def get_student_grade(student):                                # 등급반환 함수정의! 변수를 받아서
    return student['grade']                                    # 그 변수의 'grade'키에 해당하는 밸류를 반환한다.

print(get_student_grade(students[0]))                          # student의 0번째 인덱스를 인자로 넣어서 함수 호출
# A                                                            # 0번째 딕셔너리의 grade의 밸류는 "A"


class Student:                                                 # Student 클래스 만들기
    """
    Student Class                                              # 목적과 사용법을 설명하는 글 작성
    Date: 2044.04.44                                           # class 나 class안의 메서드 만들 때 이렇게 설명서를 적어두면
    44444444444444444                                          # 나중에 사람들이 사용할 때 __doc__ 를 통해 글을 읽을 수 있다
    하기 싫어                                                   # 오픈소스에 참여하는 멋진 개발자들은 다들 이렇게 살고있다
    """
    def __init__(self, name, age, grade):                      # 초기화 메서드(생성자) : 인스턴스 생성할 때 호출됨. 이름, 나이, 성적을 초기화함.
        self.name = name                                       # 인스턴수 변수 name 에 각자의 name 저장
        self.age = age                                         # 인스턴수 변수 age 에 각자의 age 저장
        self.grade = grade                                     # 인스턴수 변수 grade 에 각자의 grade 저장

    def __str__(self):                                         # 문자열표현 메서드! string 약자. 객체를 출력할 때 호출된다. 60번째 line
        return f'called __str__ >>> {self.name}, {self.age}'   # 비공식적이고 사용자 친화적으로 객체의 정보를 전달할 때 사용함. 

    def __repr__(self):                                        # 문자열표현 메서드! representation 약자.
        return f'called __repr__ >>> {self.name}, {self.age}'  # 공식적이고 개발자가 객체를 이해할 수 있는 상세한 정보 제공할 때 사용함.

    # instance method                                          # 위에 __ 붙은 메서드는 매직메서드고 얘는 새로 정의하는
    def get_grade(self):                                       # 인스턴스 메서드!
        print(f'called get_grade >>> {id(self)}')              # 함수 호출 시 인스턴스의 id를 출력한 후에
        return f'grade >>> {self.grade}'                       # 인스턴스의 grade를 반환함

student_1 = Student('won', 4444, 'A')                          # Student 클래스의 인스턴스 생성하기
student_2 = Student('babo', 444, 'B')                          # init 함수에 name, age, grade 있으니깐
student_3 = Student('baboo', 44, 'C')                          # 순서대로 인자를 적어서 생성해준다

# id
print(id(student_1))                          # 출력 : 2278781762912      # id() : 객체의 고유한 식별자를 반환하는 함수
print(id(student_2))                          # 출력 : 2278781762816      # 3개 객체가 서로 개별적이어서 id가 모두 다르다.
print(id(student_3))                          # 출력 : 2278781762720      # 만약 두 변수가 동일한 객체를 참조하고 있다면 id가 같음. 

print(f'dir func >>> {dir(student_1)}')       # dir() : 객체가 가진 모든 속성과 메서드를 보여준다. 이 객체에 어떤 함수를 쓸 수 있는지 알 수 있다.
print(f'dir func >>> {dir(student_2)}')       # 원래있는 매직메서드, 새로정의한 인스턴스 메서드(get_grade), 객체가 가진 변수(age, grade, name)까지 다 알려준다.
print('-' * 50)                               # 매직메서드로 dict, doc, dir, class 도 확인 되었으니 사용할 수 있다!
print()                                     

print(f'__dict__ >>> {student_1.__dict__}')   # 출력 : {'name': 'won', 'age': 4444, 'grade': 'A'}
print('-' * 50)                               # __dict__ : 객체가 가진 속성과 속성값을 딕셔너리 형태로 보여준다.
print()

print(student_1)                              # 출력 : called __str__ >>> won, 4444 
print('-' * 50)                               # 객체를 출력하면서, 클래스에서 정의했던 __str__ 메서드가 호출된다. student_1의 name, age 변수 출력됨
print()                                       # 만약 __str__, __repr__ 정의를 안했다면 클래스 이름과 메모리 주소를 나타내는 값이 출력됨.
                                              # 이렇게 출력됨 : <__main__.Student object at 0x000001F7F9BF3D60>

# docstring
print(student_1.__doc__)                      # __doc__ : 객체의 문서화 문자열(Document string)을 보여준디
print(student_2.__doc__)                      # student_1 과 student_2 객체는 둘 다 크래스 Student 의 인스턴스다. 
                                              # 그래서 클래스 Student의 설명('''아까 주석으로 작성한부분''')을 보여준다. 두번보여줌

# docstring 사용하여야 오픈소스 개발잘엉나ㅣ러나ㅣㅇ러닌언아ㅣ
a_list = []                                   # a_list 변수 선언. 빈리스트
print(type(a_list))                           # type() : 이 객체의 자료유형을 보여준다. 출력 : <class 'list'>
print(dir(a_list))                            # list가 가진 모든 속성과 메서드 보여주기. append, remove, sort 등등 list에서 쓸 수 있는 함수를 확인할 수 있다.
print(a_list.__doc__)                         # list 클래스를 만든 사람(귀도반로섬인가)이 작성해둔 설명('''주석''')을 읽어볼 수 있다
print(a_list.append.__doc__)                  # list 클래스의 메서드인 append 에 대한 설명(귀도반로섬이 적어놓은거)을 볼 수 있다
                                              # Append object to the end of the list.
print(a_list.remove.__doc__)                  # list 클래스의 메서드인 remove 에 대한 설명(귀도반로섬이 적어놓은거)을 볼 수 있다
                                              # Remove first occurrence of value. Raises ValueError if the value is not present.
                                              # 아하 그래서 remove 함수를 쓰면 리스트 중복요소가 있어도 젤 첫번째 값만 삭제되는거였다!!
                                              # 그리고 없으면 ValueError 가 뜨는거였음 오...
print('-' * 50)
print()

# self
print(f'method call _1 >>> {student_1.get_grade()}')    # called get_grade >>> 1598651055456 / method call _1 >>> grade >>> A
                                                        # Student 클래스의 get_grade 함수 호출. 호출할 때 student_1 객체를 self로 넣어준다.
print(f'student_1 ID >>> {id(student_1)}')              # student_1 ID >>> 1598651055456
                                                        # id 함수로 확인해본 결과 위에랑 똑같음. 한 객체의 id 고유값은 변하지 않는다.
print()
print(f'method call _2 >>> {student_2.get_grade()}')    # called get_grade >>> 1598651055360 / method call _2 >>> grade >>> B
                                                        # 이번에는 student_2 객체를 self로 넣어서 함수 호출.
print(f'student_2 ID >>> {id(student_2)}')              # student_2 ID >>> 1598651055360
                                                        # 마찬가지로 위에랑 id 똑같다
print('-' * 50)
print()

# __class__
print(student_1.__class__)                              # sudent_1 의 클래스를 알려줌. 출력 : <class '__main__.Student'>
print(student_2.__class__)                              # sudent_2 의 클래스를 알려줌. 출력 : <class '__main__.Student'>
print(f'id >>> {id(student_1.__class__)}, {id(student_2.__class__)}')   # id >>> 1598617687808, 1598617687808
                                                                        # 두 객체의 클래스의 id를 각각 출력해본 결과 동일한 것을 확인!
print(f'Class ID - Student >>> {id(Student)}')          # Class ID - Student >>> 1598617687808 : 클래스 id를 직접 출력해도 동일한 것을 확인!
print('-' * 50)
print()


#------------------------------------------------------------------------------------------


class Student:
    
    # Class var
    student_total_count = 0                         # 클래스 변수!! : 생성된 인스턴스의 총 개수를 세기 위한 변수

    def __init__(self, name, age, grade):
        self.name = name                            # 맘대로 볼 수 있는 변수
        self.age = age
        self._grade = grade                         # Protected 변수 : 접근할 수 잇긴한데 되도록 안하길 권장.
        self.__grade = grade                        # Private 변수 : 외부에서 접근하지 못하도록 캡슐화 하는거.

        Student.student_total_count += 1            # 인스턴스 생성할 때마다 호출되는 init 메서드에서 숫자를 하나씩 늘려준다.
                                                    # 근데 지역변수가 아니라 클래스변수로 지정했던 변수니까 앞에 Student. 붙여주기!!!
                                                    # 안붙였을 때 : UnboundLocalError: local variable 'student_total_count' referenced before assignmen
student_1 = Student('won', 4444, 'A')
student_2 = Student('babo', 444, 'B')
student_2 = Student('baboo', 44, 'C')
student_2 = Student('baboo', 44, 'C')
student_2 = Student('ba', 44, 'C')                  # Student 클래스의 인스턴스 9개 생성해보기
student_2 = Student('bo', 44, 'C')
student_2 = Student('babooo', 44, 'C')
student_2 = Student('baboooo', 44, 'C')
student_2 = Student('baboooooo', 44, 'C')

print(f'Class var-count student_1 >>> {student_1.student_total_count}')    # Class var-count student_1 >>> 9
print(f'Class var-count student_2 >>> {student_2.student_total_count}')    # Class var-count student_2 >>> 9
                                                                           # student_total_count 는 클래스 변수이기 때문에 다른 객체에서도 똑같은 값이 나온다.
print()
print(f'instance var student_1 {student_1.name}')                          # instance var student_1 won
print(f'instance var student_2 {student_2.name}')                          # instance var student_2 baboooooo
                                                                           # name 은 인스턴스 변수이기 때문에 객체마다 각각 본인의 값이 나온다.
print(student_2.name)                                                      # baboooooo       # student_2의 name 출력


print(student_1._grade)                    # Protected변수      # A
# print(student_1.__grade)                 # Private변수        # AttributeError
print(student_1._Student__grade)           # Private변수        # A
                                           # 오 캡슐화하면 파이썬이 <변수명>을 <_클래스명__변수명> 으로 바꿔놓는다!

 


 

'스쿼드' 카테고리의 다른 글

포맷  (0) 2024.09.10
[스쿼드] Matrix 만들기  (0) 2024.08.05
[스쿼드] 계산기 만들기  (0) 2024.08.02
[Python] 파이썬 문법 기본문제  (0) 2024.07.31
[Python] 파이썬 문법 기본문제  (0) 2024.07.30