본문 바로가기
C.W.K.
Stream
Lesson 05 of 05 · published

property·cached_property·staticmethod·classmethod

~22 min · property, cached_property, staticmethod, classmethod, builtin

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

property는 메서드에 속성 문법을 입혀

@property는 읽을 때 계산하거나 검증할 값을 obj.value처럼 노출해. setter와 deleter를 붙일 수 있지만, 단순 공개 필드를 이유 없이 감싸지는 마.

cached_property는 첫 결과를 인스턴스에 저장해

비싼 계산이 같은 인스턴스에서 변하지 않을 때 한 번만 계산하고 이후에는 저장된 값을 돌려줘. 입력 상태가 바뀌면 캐시를 지우거나 쓰지 않아야 해.

수신자가 필요한지로 메서드를 나눠

staticmethod는 self나 cls가 필요 없는 클래스 관련 함수고, classmethod는 cls를 받아 대안 생성자와 상속 가능한 팩토리에 알맞아. 인스턴스 상태를 쓰면 평범한 메서드가 맞아.

Code

property의 읽기·쓰기·삭제·python
class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value <= 0:
            raise ValueError("radius 양수여야")
        self._radius = value

    @property
    def area(self):                     # 계산, setter 불필요
        return 3.14159 * self._radius ** 2

c = Circle(5)
print(c.radius)              # 5     — 속성처럼 보임
print(c.area)                # 78.54 — 접근 시 계산
c.radius = 10                # setter 사용
print(c.area)                # 314.16

try:
    c.radius = -1            # setter 가 검증
except ValueError as e:
    print(e)
한 번 계산하는 cached_property·python
from functools import cached_property
import time

class Report:
    def __init__(self, data):
        self.data = data

    @cached_property
    def summary(self):
        print("summary 계산 중...")
        time.sleep(0.5)              # 비싼 작업이라 가정
        return f"sum={sum(self.data)}, avg={sum(self.data)/len(self.data)}"

r = Report([10, 20, 30, 40, 50])
print(r.summary)             # 'summary 계산 중...' 후 결과
print(r.summary)             # 재계산하지 않고 캐시된 값을 반환
print(r.summary)             # 재계산하지 않음

# 강제 재계산 — 캐시된 속성 삭제
del r.summary
print(r.summary)             # 'summary 계산 중...' 또
staticmethod와 classmethod로 생성 경로 나누기·python
from datetime import date

class User:
    def __init__(self, name, joined):
        self.name = name
        self.joined = joined

    @classmethod
    def from_dict(cls, d):
        # 서브클래스에 맞는 클래스 반환
        return cls(name=d["name"], joined=d.get("joined", date.today()))

    @classmethod
    def from_csv_row(cls, row):
        name, joined = row.split(",")
        return cls(name.strip(), date.fromisoformat(joined.strip()))

    @staticmethod
    def is_valid_name(name):
        # self/cls 안 만짐 — 단지 유틸리티
        return isinstance(name, str) and 1 <= len(name) <= 50

u = User.from_dict({"name": "Pippa"})
print(u.name, u.joined)
print(User.is_valid_name("x"))   # True
print(User.is_valid_name(""))    # False

# 서브클래스 — classmethod 가 여전히 맞는 타입 반환
class Admin(User):
    pass

a = Admin.from_dict({"name": "Dad"})
print(type(a).__name__)          # 'Admin'  — 'User' 아님!
직접 제어하는 property 캐시·python
class Stock:
    def __init__(self, ticker, price):
        self.ticker = ticker
        self.price = price
        self._fetched = None

    @property
    def cached_data(self):
        if self._fetched is None:
            print("fetching...")
            self._fetched = {"ticker": self.ticker, "price": self.price}
        return self._fetched

    def refresh(self):
        self._fetched = None             # 캐시 무효화

s = Stock("AAPL", 180)
print(s.cached_data)         # 'fetching...' 후 dict
print(s.cached_data)         # fetching을 다시 출력하지 않음
s.refresh()
print(s.cached_data)         # 'fetching...' 또 — refresh 작동

# cached_property 가 흔한 경우엔 더 짧음
# 수동 @property 는 무효화 로직 필요할 때

External links

Exercise

내부에는 Kelvin으로 저장하면서 kelvin, celsius, fahrenheit를 읽고 쓸 수 있는 Temperature 클래스를 만들어. 섭씨 25도를 넣으면 298.15K가 되고 절대영도보다 낮으면 ValueError가 나야 해. description은 cached_property로 만들고 모든 경로를 시험해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.