classmethod and staticmethod as Descriptors
メニューを表示するにはスワイプしてください
classmethod and staticmethod are built-in descriptors. They use __get__ to change what gets passed as the first argument when a method is called. Understanding them as descriptors explains exactly how they work – and why they behave differently from regular methods.
Regular Methods Are Non-Data Descriptors
Before looking at classmethod and staticmethod, it helps to know that regular functions stored as class attributes are themselves non-data descriptors. Their __get__ binds the instance as the first argument:
1234567891011# A regular function's __get__ binds the instance class Report: def generate(self): return f"Report from {self}" print(type(Report.__dict__["generate"])) # <class 'function'> print(hasattr(Report.__dict__["generate"], "__get__")) # True report = Report() bound_method = Report.__dict__["generate"].__get__(report, Report) print(bound_method()) # Same as report.generate()
classmethod — Binding the Class
classmethod wraps a function so that __get__ binds the class as the first argument instead of the instance. This happens whether the method is called on an instance or on the class directly:
123456789101112131415161718192021222324# classmethod binds the class, not the instance class Transaction: _currency = "USD" def __init__(self, transaction_id, amount): self.transaction_id = transaction_id self.amount = amount @classmethod def get_currency(cls): return cls._currency @classmethod def from_string(cls, raw_string): # Parsing "TX-001:4500.0" into a Transaction transaction_id, amount = raw_string.split(":") return cls(transaction_id, float(amount)) # Calling on the class and on an instance – both receive the class print(Transaction.get_currency()) # "USD" transaction = Transaction.from_string("TX-001:4500.0") print(transaction.transaction_id) # "TX-001" print(transaction.amount) # 4500.0
How classmethod.__get__ Works
1234567891011121314# Inspecting classmethod as a descriptor class Config: debug = False @classmethod def is_debug(cls): return cls.debug print(type(Config.__dict__["is_debug"])) # <class 'classmethod'> print(hasattr(Config.__dict__["is_debug"], "__get__")) # True # __get__ returns a bound method with the class pre-filled bound = Config.__dict__["is_debug"].__get__(None, Config) print(bound()) # False – same as Config.is_debug()
staticmethod — No Binding at All
staticmethod wraps a function so that __get__ returns the original function unchanged – no instance, no class is bound. It behaves like a plain function that happens to live in a class namespace:
123456789101112131415# staticmethod – no binding, just a plain function in class scope class CurrencyConverter: _rate = 1.08 # EUR to USD @staticmethod def format_amount(amount, symbol): return f"{symbol}{amount:,.2f}" @classmethod def convert_to_usd(cls, eur_amount): usd_amount = eur_amount * cls._rate return cls.format_amount(usd_amount, "$") print(CurrencyConverter.format_amount(4500.0, "€")) # €4,500.00 print(CurrencyConverter.convert_to_usd(4500.0)) # $4,860.00
classmethod vs staticmethod
フィードバックありがとうございます!
AIに質問する
AIに質問する
何でも質問するか、提案された質問の1つを試してチャットを始めてください