classmethod and staticmethod as Descriptors
Stryg for at vise menuen
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
Tak for dine kommentarer!
Spørg AI
Spørg AI
Spørg om hvad som helst eller prøv et af de foreslåede spørgsmål for at starte vores chat
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
Tak for dine kommentarer!