프로그래밍소프트웨어 엔지니어

파이썬에서 클래스 속성(class properties)은 어떻게 작동합니까? property와 일반 속성의 차이점은 무엇이며, 계산된 속성을 어떻게 구현합니까? getter/setter 감시 기능을 주의 깊게 살펴보는 것이 중요한 이유는 무엇입니까?

Hintsage AI 어시스턴트로 면접 통과

답변.

파이썬의 속성(property)은 다른 언어(Java/C++)의 getter 및 setter와 유사하게 속성의 캡슐화를 우아하게 구현하는 방법으로 등장했지만, 메서드를 명시적으로 호출할 필요는 없습니다. property가 없던 시절에는 변수를 접근하기 위해 명시적인 메서드 get/set을 구현해야 했기 때문에 클래스 인터페이스가 복잡해졌습니다.

문제는 클래스 사용자가 속성과의 작업을 투명하게 인터페이스할 수 있도록(dot 표기법 obj.x를 사용하여) 하면서 내부적으로는 계산, 검증, 캐싱 또는 값의 유효성을 제어할 수 있어야 한다는 것입니다. property가 없다면 내부 구현이 변경될 때 모든 호출을 변경해야 하므로 대량의 오류가 발생하고 확장성이 떨어집니다.

해결책은 @property 데코레이터를 사용하여 메서드를 속성으로 변환하고 계산, 검증 또는 지연 로딩의 로직을 메서드 안에 숨기는 것입니다. getter는 읽기 전용으로 구현되고, .setter를 사용하여 쓰기를 관리하며, .deleter를 사용하여 삭제를 처리합니다.

코드 예제:

class Rectangle: def __init__(self, width, height): self._width = width self._height = height @property def area(self): # 'area'는 이제 속성처럼 읽을 수 있음 return self._width * self._height @property def width(self): return self._width @width.setter def width(self, value): # rect.width = ... 할 때 자동으로 호출됨 assert value > 0, "Width must be positive" self._width = value rect = Rectangle(3, 4) print(rect.area) # 12 rect.width = 10 print(rect.area) # 40

주요 특징:

  • 속성은 일반 속성 접근을 통해 계산이나 검증을 숨깁니다.
  • property는 getter, setter, deleter를 지원합니다.
  • property 사용은 구현이 변경될 때 사용자 코드 변경을 요구하지 않습니다.

함정 질문.

property가 클래스 메서드나 정적 메서드와 함께 작동할 수 있습니까?

아니요. property는 인스턴스 수준(instance-level)의 속성만을 처리합니다. 클래스 수준(class-level)에서는 특별한 기술자(descriptor)나 @classmethod-property를 사용해야 합니다(수동으로 구현됨).

setter 없이 getter만 있는 property를 선언하면 속성에 값을 쓸 수 있습니까?

아니요. 그런 property는 읽기 전용(read-only)입니다; 할당 시도는 AttributeError를 발생시킵니다.

setter 내부에서 예외가 발생하면(예: assert)?

예외가 외부로 전파되고, 할당은 이루어지지 않으며, 속성의 값은 이전 상태를 유지합니다. 이는 검증을 위해 자주 사용됩니다.

일반적인 오류 및 안티 패턴

  • property의 백엔드에서 보호된(_attr) 또는 비공개(__attr) 속성을 사용하지 않기 — self.attr에 대한 재귀적 참조가 발생해 순환 참조를 초래할 수 있습니다.
  • 필요 없는 곳에서 property 사용하기 (계산이나 제어가 필요 없는 곳에서).
  • setter에서 검증이 없는 경우, 클래스의 불변(invariant)이 무너질 수 있습니다.

실제 사례

부정적인 사례: 클래스의 공개 속성을 제어 없이 변경.
장점: 빠르고 간단함.
단점: 클라이언트 코드를 다시 작성하지 않고 내부 로직을 변경할 수 없으며, 일관되지 않은 상태가 발생할 수 있습니다.

긍정적인 사례: property를 통한 캡슐화, getter/setter의 로직, 엄격한 검증.
장점: 유연성, 제어, 투명성, 안전한 API 진화.
단점: 코드가 약간 더 많고, 디버깅이 다소 복잡해질 수 있습니다.