Python/Advanced

[Python] Class 이해 - 언더바 정의, @property, @staticmethod, setter/getter

yubi5050 2023. 7. 2. 18:27

언더바 ( _ ) 1개, 2개 차이

Python 클래스 내부 변수와, 메소드에 대한 캡슐화 정도를 조절하기 위해 언더바(_)를 사용하는데, 언더바 1개와 2개는 그 정도가 다르다.

 

👉 하나의 언더바 (_) 사용

  • _variable, _method()
  • 비교적 약한 캡슐화 정도를 가짐
  • 일반적으로 내부적으로 사용되는 변수, 메소드를 나타냄

 

👉 두개의 언더바 (__) 사용

  • __variable, __method()
  • 이중 언더바를 사용하면 이름이 자동으로 변환되어 상속시 충돌을 방지 (_클래스명__<변수, 함수명>)
  • 강한 캡슐화 정도를 가짐 (외부에서 직접 접근 불가)
  • 클래스 내부에서만 접근 가능

 

👉 예시 코드

아래 예시에서 볼 수 있 듯, 두개의 언더바(__)를 사용한 변수/메소드는 직접적으로 접근하지 못하고, 해당 클래스를 직접적으로 통해야 접근이 가능하다. (example._Example_c, example._Example_mc())

class Example:
    a = "var a"
    _b = "var _b"
    __c = "var __c"

    def ma(self):
        return "method a"

    def _mb(self):
        return "method b"

    def __mc(self):
        return "method c"


example = Example()
print(dir(example))  # [ 'a', '_b', '_Example__c', 'ma', '_mb', '_Example__mc'] 
print(example.a) # var a
print(example._b) # var _b
# print(sample.__c) <- error
print(example.ma()) # method a
print(example._mb()) # method b
# print(sample.__mc()) <- error
print(example._Example__c) # var __c
print(example._Example__mc()) # method c

 

@staticmethod 데코레이터 란

클래스 객체를 생성하지 않더라도 외부에서 사용 가능하게 하는 것

 

일반적으로 클래스는 캡슐화의 원칙을 지켜야 하는게 일반적인데, staticmethod를 사용하는 이유는?

  • 정적 메서드는 클래스, 인스턴스와 관계가 없는 경우에 사용
  • 외부 호출 시 Class명.함수명 의 방식으로 읽기 쉬운 로직의 코드 생성 가능
  • 객체의 상태에 의존하지 않는 동작 구현 목적 (클래스 변수, 인스턴스 변수에 독립적인 코드)
  • 모듈 수준의 기능 제공과, 재사용성을 위해 
  • Service Layer 분리할 때 사용하기도 함

 

그래도 일반적으로는 캡슐화를 지키며 사용하는 것이 조금 더 올바른 방법

 

 

@property 데코레이터 란

다른 필드로부터 읽기 전용 필드(가공)에 대한 소요가 있을때 정의 하여 사용

해당 속성을 호출시 매번 접근하여 값을 (계산하여) 반환

캡슐화를 목적으로 주로 사용된다.

 

👉 예시 코드

class Profile:
    def __init__(self, first_name:str, last_name:str):
        self.first_name = first_name
        self.last_name = last_name

    @property
    def full_name(self):
        return self.first_name + self.last_name

profile = Profile("홍","길동")
print(profile.first_name) # "홍"
print(profile.last_name) # "길동"
print(profile.full_name) # "홍길동"

 

👉 추가 ) cached_property

property와 똑같이 읽기 전용 목적으로 사용 . 

하지만 해당 계산 결과에 대해 캐시에 저장하여, 재사용됨

단 단일 프로세스 내 인스턴스 메모리 한정 공유됨

 

 

@property 를 이용한 getter / setter

일반적으로 클래스에서 변수들을 캡슐화 하여 관리하기 위해선, __변수명 으로 캡슐화 하여 사용하는데, 생성한 객체에서 캡슐화 된 private 변수를 직접 접근시에는 AttributeError 가 발생한다.

하지만 접근이 필요한 경우 @property를 이용해 getter/setter를 구현하여 접근 및 값을 수정 할 수 있다.

class Human:
    def __init__(self, age: int, name: str):
        self.__age = age
        self.__name = name

    # 일반 property 의 역할
    @property
    def profile(self):
        return str(self.__age) + self.__name

    # getter의 역할 - 캡슐화 된 객체 변수에 접근
    @property
    def age(self):
        return self.__age

    # setter의 역할 - 캡슐화 된 객체 변수에 접근
    @age.setter
    def age(self, age: int):
        self.__age = age


human = Human(10, "홍길동")
print(human.__name) # AttributeError: 'Human' object has no attribute '__name'
print(human.profile) # 10홍길동
human.age = 20
print(human.profile) # 20홍길동

이렇게 하면 

굳이 애써 __age와 같은 private 변수로 선언한 의의가 퇴색되고, 외부 접근이 age로 쉽게 가능해 지긴 하지만, setter에서 값에 대한 추가적인 validation 로직을 추가해 보완 할 수 있다.

 

즉 정리하면

기본적으로 캡슐화를 지향 하되, 외부에서 값의 read가 필요할 경우 property를 통해 getter로서 접근을 허용해주되,

수정이 필요할 경우 setter로 접근을 허용해주되, 값에 대한 validation을 추가로 작성

참고