파이썬 심화: 매직 메서드와 프로퍼티
안녕하세요! 여러분의 코딩 길잡이, 재준봇입니다!
자, 여러분. 여기까지 오시느라 정말 고생 많으셨습니다. 이제 우리는 파이썬의 기초를 넘어 소위 말하는 “고수”들의 영역으로 들어갑니다. 오늘 배울 내용은 ‘매직 메서드’와 ‘프로퍼티’입니다. 이름만 들으면 무슨 마법 주문 같고, 뭔가 어려울 것 같죠? 하지만 걱정 마세요. 저 재준봇이 아주 찰떡같은 비유로 머릿속에 때려 박아 드리겠습니다.
이거 모르면 파이썬을 쓴다고 하면서 정작 파이썬의 진정한 맛은 못 느끼는 셈이 됩니다. 진짜 신기하고 강력한 기능들이니 집중해서 따라오세요!
16강: 파이썬 심화: 매직 메서드와 프로퍼티
1. 매직 메서드(Magic Method): 객체에 ‘마법’을 부리는 방법
여러분, 혹시 게임 캐릭터 만드실 때 ‘패시브 스킬’이라는 거 들어보셨나요? 내가 직접 버튼을 누르지 않아도 특정 상황이 되면 자동으로 발동되는 스킬 말입니다. 파이썬의 매직 메서드가 바로 그 패시브 스킬과 같습니다.
우리가 보통 함수를 호출할 때는 obj.say_hello() 이렇게 이름을 불러서 실행하죠? 그런데 매직 메서드는 이름 앞뒤에 언더바 두 개(__)가 붙어 있어서 던더(Dunder, Double Underscore) 메서드라고도 부릅니다. 이 녀석들은 우리가 직접 호출하는 게 아니라, 파이썬 시스템이 “어? 이런 상황이네?” 하고 자동으로 호출해 주는 특수한 함수들입니다.
쉽게 말해: “파이썬아, 내가 만든 클래스 객체에
+기호를 쓰면 이 동작을 수행해 줘!”라고 미리 약속해두는 설정값 같은 것입니다.
[실습 1] 매직 메서드로 객체에 생명력 불어넣기
단순히 데이터를 담기만 하는 객체가 아니라, 파이썬 기본 타입(리스트, 정수 등)처럼 동작하게 만드는 3가지 대표적인 매직 메서드를 구현해 보겠습니다.
class SmartBook:
def __init__(self, title, pages):
# 책의 제목과 페이지 수를 초기화합니다.
self.title = title
self.pages = pages
# 1. __str__: print() 함수로 출력할 때 어떻게 보일지 결정하는 마법
def __str__(self):
# 객체를 그냥 출력하면 <__main__.SmartBook object...>라고 나오지만,
# 이 메서드를 정의하면 우리가 원하는 예쁜 문자열로 나옵니다.
return f"책 제목: {self.title}, 총 페이지: {self.pages}p"
# 2. __len__: len() 함수를 썼을 때 어떤 값을 반환할지 결정하는 마법
def __len__(self):
# 책의 페이지 수를 길이라고 정의하겠습니다.
return self.pages
# 3. __add__: + 연산자를 썼을 때 어떻게 동작할지 결정하는 마법
def __add__(self, other):
# 책 두 권을 더하면 전체 페이지 합계를 가진 새로운 책 객체를 반환하게 합니다.
total_pages = self.pages + other.pages
return SmartBook("합본집", total_pages)
# --- 실행 코드 ---
book1 = SmartBook("파이썬 정복기", 300)
book2 = SmartBook("코딩 마스터", 200)
# __str__ 발동! (print 함수가 내부적으로 __str__을 호출함)
print(book1)
# __len__ 발동! (len 함수가 내부적으로 __len__을 호출함)
print(f"이 책의 길이는 {len(book1)}페이지입니다.")
# __add__ 발동! ( + 기호가 내부적으로 __add__를 호출함)
combined_book = book1 + book2
print(f"두 권을 합친 결과: {combined_book}")
[코드 뜯어보기]
__str__(self): 이 메서드는 객체의 ‘명함’ 같은 겁니다.print()를 했을 때 사용자가 읽기 좋은 형태로 보여주기 위해 사용합니다.__len__(self): 파이썬의len()함수는 사실 내부적으로 이 메서드를 찾습니다. “너의 길이는 얼마니?”라고 물어보는 것이죠.__add__(self, other): 가장 소름 돋는 부분입니다. 숫자나 문자열에만 쓰던+연산자를 내가 만든 클래스에서도 쓸 수 있게 만들어 줍니다.other는+기호 오른쪽에 오는 객체를 의미합니다.
2. 프로퍼티(Property): 변수 접근의 ‘보디가드’
자, 이제 프로퍼티로 넘어가 보겠습니다. 여러분, 클래스를 만들 때 변수를 그냥 self.name = name 이렇게 공개해두면 편하긴 합니다. 하지만 실무에서는 이게 아주 위험합니다.
예를 들어, 사람의 나이를 저장하는 변수가 있는데 누군가 실수로 person.age = -100이라고 입력한다면? 프로그램은 돌아가겠지만 데이터는 엉망이 되겠죠. 그렇다고 매번 set_age() 같은 함수를 만들어서 체크하자니 코드가 너무 지저분해집니다.
이때 등장하는 것이 바로 @property 데코레이터입니다.
쉽게 말해: 변수처럼 보이지만 실제로는 함수로 동작하게 만드는 것입니다. 변수에 접근하려고 할 때 “잠깐! 너 정당한 권한이 있어? 입력값이 올바른가?”라고 검사하는 보디가드를 세우는 것과 같습니다.
[실습 2] 프로퍼티로 데이터 완벽 방어하기
프로퍼티의 3가지 핵심 요소인 Getter, Setter, Deleter를 모두 구현해 보겠습니다.
class Employee:
def __init__(self, name, salary):
self.name = name
# 내부 변수는 앞에 언더바(_)를 붙여 "건드리지 마세요"라고 표시합니다.
self._salary = salary
# 1. Getter: 변수 값을 가져올 때 실행되는 마법
@property
def salary(self):
# 값을 가져올 때 단순히 주는 게 아니라 가공해서 줄 수도 있습니다.
print("급여 정보를 조회합니다...")
return f"{self._salary}원"
# 2. Setter: 변수 값을 변경하려고 할 때 실행되는 마법 (검증 가능!)
@salary.setter
def salary(self, value):
if value < 0:
# 잘못된 값이 들어오면 여기서 컷트합니다!
print("경고: 급여는 음수가 될 수 없습니다!")
return
print(f"급여를 {value}원으로 수정합니다.")
self._salary = value
# 3. Deleter: 변수를 삭제하려고 할 때 실행되는 마법
@salary.deleter
def salary(self):
print("급여 정보를 삭제합니다. 주의하세요!")
del self._salary
# --- 실행 코드 ---
emp = Employee("재준", 5000000)
# Getter 작동: 함수처럼 ()를 붙이지 않고 변수처럼 접근합니다.
print(emp.salary)
# Setter 작동: 변수에 값을 대입하는 것처럼 보이지만, 사실은 setter 함수가 실행됩니다.
emp.salary = 6000000 # 정상 변경
emp.salary = -1000 # 잘못된 값 입력 -> 경고 발생!
print(emp.salary)
# Deleter 작동: del 키워드를 사용하면 실행됩니다.
del emp.salary
[코드 뜯어보기]
@property: 이 데코레이터가 붙은 메서드는 이제 변수처럼 읽을 수 있습니다.emp.salary()가 아니라emp.salary라고 씁니다.@salary.setter:emp.salary = 값이라고 쓰는 순간 이 함수가 실행됩니다. 여기서if문을 통해 데이터의 유효성을 검사할 수 있어 매우 안전합니다.@salary.deleter:del emp.salary를 했을 때 동작합니다. 단순히 지우는 것뿐만 아니라 삭제 전 로그를 남기는 등의 처리가 가능합니다.
🚩 초보자 폭풍 질문!
Q: 아니 재준봇님! 그냥 _salary라는 변수를 쓰고, 값을 바꿀 때 set_salary() 함수를 따로 만들어서 쓰면 되는 거 아닌가요? 왜 굳이 이렇게 복잡하게 프로퍼티를 쓰나요?
A: 오, 아주 날카로운 질문입니다! 그 이유는 바로 하위 호환성과 사용자 편의성 때문입니다.
만약 여러분이 이미 만든 라이브러리를 1,000명의 개발자가 사용하고 있다고 칩시다. 처음에는 self.salary라고 변수를 공개해서 썼는데, 나중에 “아! 검증 로직이 필요하네?”라고 깨달았습니다. 이때 set_salary()라는 함수를 새로 만들면, 그 라이브러리를 쓰던 1,000명의 코드를 전부 수정해야 합니다.
하지만 프로퍼티를 사용하면 사용자는 똑같이 emp.salary = 6000이라고 쓰는데, 내부적으로만 함수로 동작하게 바꿀 수 있습니다. 즉, 겉모습은 변수인데 속은 함수로 만들어 외부 코드를 하나도 안 고치고 기능을 추가할 수 있는 엄청난 장점이 있는 거죠!
⚠️ 실무 주의보
실무에서 프로퍼티를 사용할 때 가장 많이 하는 실수 중 하나가 바로 ‘무한 루프’입니다!
# [절대 금지 예제]
@property
def salary(self):
return self.salary # <--- 여기서 무한 루프 발생!
위 코드를 보시면 salary 프로퍼티 내부에서 다시 self.salary를 호출하고 있습니다. 그러면 파이썬은 “값을 가져오기 위해 salary를 호출 -> 다시 salary를 호출 -> 다시 salary를 호출…” 하면서 결국 프로그램이 뻗어버립니다(RecursionError).
해결책: 반드시 내부 저장용 변수(보통 _를 앞에 붙인 _salary)를 따로 두고, 프로퍼티 메서드는 그 내부 변수를 제어하도록 설계해야 합니다. 잊지 마세요, 이름이 같으면 안 됩니다!
🏁 오늘 강의 마무리
오늘 우리는 파이썬의 꽃이라고 할 수 있는 매직 메서드와 프로퍼티를 배웠습니다.
- 매직 메서드는 파이썬의 내장 기능을 내 것처럼 활용하게 해주는 ‘패시브 스킬’이다. (
__str__,__len__,__add__등) - 프로퍼티는 변수의 겉모습을 유지하면서 내부적으로 데이터 검증과 제어를 가능하게 하는 ‘보디가드’이다. (
@property,@setter,@deleter)
이 두 가지만 제대로 다룰 줄 알아도 여러분의 코드는 훨씬 더 파이썬답고(Pythonic), 전문적인 모습으로 변할 것입니다. 처음에는 헷갈릴 수 있지만, 직접 코드를 쳐보며 “아, 이래서 쓰는구나!”라고 느끼는 게 중요합니다.
자, 이제 직접 여러분만의 ‘마법 객체’를 만들어 보세요. 다음 강의에서는 더 강력한 내용으로 돌아오겠습니다. 고생하셨습니다!
<hr>