Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Oppiskele classmethod and staticmethod as Descriptors | Descriptors in the Wild
Python Descriptors Explained

classmethod and staticmethod as Descriptors

Pyyhkäise näyttääksesi valikon

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

question mark

What does classmethod.__get__ pass as the first argument when the method is called?

Valitse oikea vastaus

Oliko kaikki selvää?

Miten voimme parantaa sitä?

Kiitos palautteestasi!

Osio 2. Luku 2

Kysy tekoälyä

expand

Kysy tekoälyä

ChatGPT

Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme

Osio 2. Luku 2
some-alt