본문 바로가기
python

파이썬 기초 - 클래스 속성 관리와 접근 제어

by kyeongseo.oh 2025. 3. 20.

명명 규칙에 의한 접근 제어

파이썬은 언어 차원에서 강제적인 접근 제어 기능을 제공하지 않지만, 명명 규칙을 통해 접근 제어 표현할 수 있다.

  • public: 클래스 내부 및 외부 어디서든 접근이 가능한 속성이나 메서드이다. 변수명에 별도의 접두사를 붙이지 않는다. (예: name)
  • protected: 클래스 내부 및 서브클래스(상속받은 클래스)에서만 사용해야 한다는 의미를 갖는다. 하지만 관례상일 뿐, 실제로 접근이 차단되는 것은 아니다. 단일 밑줄(_)을 접두사로 붙여 표시한다. (예: _name)
  • private: 클래스 내부에서만 접근 가능하도록 의도된 속성이다. 네임 맹글링이 적용되어 외부에서 직접 접근할 수 없도록 처리된다. 다만, 이는 보안 목적이 아니라 속성명이 내부적으로 충돌하는 것을 방지하기 위해 사용된다. (예: __name)
class Person:
    def __init__(self, name, age):
        self.name = name          # 공개 속성
        self._age = age           # 보호 속성 (관례상 직접 접근 지양)
        self.__secret = "비밀"     # 비공개 속성 (이름 맹글링 발생)
    
    def reveal_secret(self):
        return self.__secret

person = Person("홍길동", 30)
print(person.name)            # 출력: 홍길동
print(person._age)            # 출력: 30 (접근 가능하지만 관례상 외부에서 직접 접근하지 않음)
# print(person.__secret)      # AttributeError 발생
print(person.reveal_secret()) # 출력: 비밀
print(person._Person__secret) # 출력: 비밀 (이름 맹글링으로 인해 이런 식으로 접근 가능)

Name Mangling

네임 맹글링은 파이썬에서 __(더블 언더스코어)로 시작하는 변수나 메서드의 이름을 내부적으로 변경하는 기능이다. 네임 맹글링을 사용하면 부모 클래스의 속성을 보호하여 자식 클래스에서 실수로 덮어쓰는 문제를 막을 수 있다.

__변수명 에서 _클래스명__변수명 으로 자동으로 변환된다.

Name Mangling을 사용하지 않은 예제

Parent의 age가 오버라이드된다.

class Parent:
    def __init__(self):
        self.age = 60  # 중요한 설정 값

    def get_parent_age(self):
        return self.age  # 부모의 설정 값을 반환

class Child(Parent):
    def __init__(self):
        super().__init__()
        self.age = 20  # 자식 클래스에서 같은 변수 사용 (부모 값 덮어씀)

c = Child()
print(c.get_parent_age())  # return : 20

Name Mangling을 사용한 예제

Parent의 age가 오버라이드되는 것을 방지한다.

class Parent:
    def __init__(self):
        self.__age = 60  # private 변수 (네임 맹글링 발생)

    def get_parent_age(self):
        return self.__age  # 부모의 설정 값을 반환

class Child(Parent):
    def __init__(self):
        super().__init__()
        self.__age = 20  # 자식 클래스의 독립적인 변수

c = Child()
print(c.get_parent_age())  # return : 60
print(c._Parent__age)      # return : 60
print(c._Child__age)       # return : 20

 

getter와 setter 구현

게터(getter)와 세터(setter)는 객체 속성에 접근하고 수정하는 메서드로 속성 보호 및 유효성 검사 등에 사용된다.

1. 전통적인 방식

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age
    
    def get_name(self):
        return self._name
    
    def set_name(self, name):
        if not name:
            raise ValueError("이름은 비어있을 수 없습니다.")
        self._name = name
    
    def get_age(self):
        return self._age
    
    def set_age(self, age):
        if age < 0:
            raise ValueError("나이는 음수일 수 없습니다.")
        self._age = age

person = Person("홍길동", 30)
print(person.get_name())  # 홍길동
person.set_age(35)
print(person.get_age())   # 35

2. @property 데코레이터 사용

@property 데코레이터는 메소드를 속성처럼 접근할 수 있게 해주는 기능이다. 

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age
    
    @property
    def name(self):
        return self._name
    
    @name.setter
    def name(self, name):
        if not name:
            raise ValueError("이름은 비어있을 수 없습니다.")
        self._name = name
    
    @property
    def age(self):
        return self._age
    
    @age.setter
    def age(self, age):
        if age < 0:
            raise ValueError("나이는 음수일 수 없습니다.")
        self._age = age

person = Person("홍길동", 30)
print(person.name)  # 홍길동
person.age = 35
print(person.age)   # 35

getter만 구현하면 읽기 전용 속성으로 사용할 수 있다.

class Person:
    def __init__(self, first_name, last_name):
        self._first_name = first_name
        self._last_name = last_name
    
    @property
    def first_name(self):
        return self._first_name
    
    @property
    def last_name(self):
        return self._last_name
    
    @property
    def full_name(self):  # 읽기 전용 속성
        return f"{self._first_name} {self._last_name}"

person = Person("홍", "길동")
print(person.full_name)  # 홍 길동
person.full_name = "김 철수"  # AttributeError: can't set attribute

Computed Properties

계산된 속성은 다른 속성들을 기반으로 값이 계산되는 속성으로 매번 접근할 때마다 실시간으로 계산되며, @property 데코레이터를 사용해 구현할 수 있다.

class Circle:
    def __init__(self, radius):
        self.radius = radius  # 반지름
    
    @property
    def area(self):
        return 3.14159 * self.radius ** 2  # 면적 계산 (computed property)
    
circle = Circle(5)
print(circle.area)  # 78.53975 (반지름이 5일 때 면적)

circle.radius = 10
print(circle.area)  # 314.159 (반지름이 10일 때 면적)

 

 

 

댓글