Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Python  Module 9 – Object-oriented Python and the data model

Methods: instance, class, static and properties

Write alternative constructors with classmethod, helpers with staticmethod and checked attributes with property, and see what each kind receives.

  • Intermediate
  • 25 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 9 of 25 minutes free
Free: Each kind of method receives something different · A class method builds objects of the class it is called on · A static method is a plain function kept in the class · 2 more sections. With a pass: 2 in-depth parts and 3 interview answers, about 16 minutes.

What you will learn

  • Explain what an instance method, a class method, a static method and a property each receive
  • Write alternative constructors with @classmethod that also build subclasses
  • Decide when a @staticmethod or a module-level function fits better
  • Validate attributes with a @property setter that the constructor uses too
  • Choose between a property and functools.cached_property for a computed value

Before you start

What you’ll learn · Before you start

What you will learn

  • Explain what an instance method, a class method, a static method and a property each receive
  • Write alternative constructors with @classmethod that also build subclasses
  • Decide when a @staticmethod or a module-level function fits better
  • Validate attributes with a @property setter that the constructor uses too
  • Choose between a property and functools.cached_property for a computed value

Before you start

On this page

A Day class that can only be built from three numbers makes every caller with the text "2026-08-15" split and convert it first, and a Temperature whose celsius is a plain attribute accepts -500 without a word. Python’s four kinds of method solve both, and what tells them apart is what each one receives. An instance method, the default, receives the instance it was called on as self. A class method: A method marked with @classmethod. It receives the class the call went through as its first argument, cls, whether it is called through the class or through an instance., marked with @classmethod, receives the class as cls, which makes it the natural home of alternative constructor: A class method that builds an instance from another form of the data, such as a string or a row, by returning cls(...). date.fromisoformat() and dict.fromkeys() are alternative constructors. such as Day.from_iso(text); because cls is whichever class the call went through, the same constructor also builds subclasses.

A static method: A function kept in a class with @staticmethod. A call through the class or through an instance passes it no instance and no class, only the arguments written., marked with @staticmethod, receives nothing extra: it is a plain function kept in the class. A property: An attribute whose reads and assignments run methods: a getter and, optionally, a setter. @property turns a method into the getter, and callers keep writing obj.name., marked with @property, runs a method when code reads or assigns an attribute, so t.celsius can be computed or checked while callers keep the plain attribute syntax. Below you will compare the four and watch a class method and a property at work.

Each kind of method receives something different

Every method is written the same way, with def in the class body; the decorator above it decides what a call passes in:

An instance method receives the instance, a class method the class, a static method nothing extra, and a property runs its getter or setter.Instance method@classmethod@staticmethod@propertyt.show(5)show(t, 5)Day.from_iso(s)from_iso(Day, s)Day.is_valid(y, m, d)is_valid(y, m, d)t.celsius = 30setter(t, 30)self = tcls = Daynothing addeda method runs

What a call passes to each kind of method

Text description of the diagram

The diagram has four boxes, one for each kind, and each box shows a call at the top and, below it, what the method actually receives.

  • Instance method: the call t.show(5) runs show(t, 5); the instance t arrives as self.
  • Class method: the call Day.from_iso(s) runs from_iso(Day, s); the class arrives as cls. Called through an instance, it receives the instance's class.
  • Static method: the call Day.is_valid(y, m, d) runs is_valid(y, m, d); nothing is added to the arguments.
  • Property: the assignment t.celsius = 30 runs the property's setter as setter(t, 30); reading t.celsius runs its getter with t.

The decorators work because the class stores a different object for each kind, and the attribute lookup of the previous lesson asks that object what to hand back. A function comes back bound to the instance, a class method comes back bound to the class, a static method comes back as the bare function, and a property calls its getter. The table puts the four side by side:

Kind Receives Use it for
Instance method the instance, as self work on one object’s data
Class method the class, as cls alternative constructors
Static method only its own arguments a helper that belongs with the class
Property the instance, as self, when the attribute is read or assigned computed or checked attributes

Start with an instance method

Default to an instance method; use a class method to build objects, a static method for a helper that needs neither the object nor the class, and a property to keep the obj.name syntax.

Checkpoint

Check: What each kind of method receives

  1. One question Which kind of method receives the class as its first argument, whether it is called through the class or through an instance?

    Choose one answer.

    Show the answer: One question

    Answer: A class method

    The decorator decides what a call passes in: the instance for an instance method and a property, the class for a class method, and nothing extra for a static method.

A class method builds objects of the class it is called on

__init__ gives a class one way to be built. A class method adds more, each one a recipe that turns another form of the data, such as a string, a dictionary or a row of a file, into the arguments of the normal constructor:

A from_iso constructor and an is_valid helper Python · day_constructors.py
import calendar


class Day:
    def __init__(self, year, month, day):
        if not self.is_valid(year, month, day):
            raise ValueError(f"not a day of the calendar: {year}-{month}-{day}")
        self.year, self.month, self.day = year, month, day

    @classmethod
    def from_iso(cls, text):
        """Build a Day, or an instance of a subclass, from 'YYYY-MM-DD'."""
        year, month, day = (int(part) for part in text.split("-"))
        return cls(year, month, day)

    @staticmethod
    def is_valid(year, month, day):
        return 1 <= month <= 12 and 1 <= day <= calendar.monthrange(year, month)[1]

    def iso(self):
        return f"{self.year:04}-{self.month:02}-{self.day:02}"


class Holiday(Day):  # a subclass: it inherits everything above
    pass


d = Day.from_iso("2026-03-15")
h = Holiday.from_iso("2026-08-15")
print(type(d).__name__, d.iso())
print(type(h).__name__, h.iso())  # cls was Holiday, so a Holiday came back
print(Day.is_valid(2026, 2, 29), d.is_valid(2028, 2, 29))
try:
    Day.from_iso("2026-02-30")
except ValueError as error:
    print("ValueError:", error)

Output

Day 2026-03-15
Holiday 2026-08-15
False True
ValueError: not a day of the calendar: 2026-2-30

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 day_constructors.py

  • from_iso splits the text, converts the three parts and returns cls(year, month, day), so __init__ still checks every new object, including the impossible day in the last line.
  • Called on the subclass, Holiday.from_iso(...) received Holiday as cls and returned a Holiday. Subclasses get the constructor without writing anything; the lesson on inheritance shows how they extend it.
  • The standard library uses the same pattern: dict.fromkeys(), int.from_bytes() and datetime.date.fromisoformat() are all class methods.

Step through the call on the subclass and watch what cls is:

Interactive · message by message What Holiday.from_iso(text) does Step through the call on the subclass. Each arrow is a call with its arguments, or the object a call returns; watch what cls is.
What Holiday.from_iso(text) does, step 6 of 6. Messages so far: Your code to from_iso(cls, text): Holiday.from_iso(text); from_iso(cls, text) to __init__: Holiday(2026, 8, 15); __init__ to Your code: a Holiday.Your codecalls the subclassfrom_iso(cls, text)cls = Holiday; 2026, 8, 15__init__is_valid: TrueHoliday.from_iso(text)Holiday(2026, 8, 15)a Holiday

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. The call. Holiday has no from_iso of its own, so the lookup finds the class method that Day defines, as it finds any inherited method.
  2. cls is Holiday. A class method receives the class the call went through, so cls is Holiday, not Day, although the method is written in Day.
  3. Text to numbers. The method splits the text at each hyphen and converts the three parts with int(), giving the year 2026, the month 8 and the day 15.
  4. cls(2026, 8, 15). return cls(year, month, day) calls Holiday(2026, 8, 15), the normal constructor, so __init__ runs on a new Holiday.
  5. The check passes. __init__ asks is_valid(2026, 8, 15), which is True because August has 31 days, and stores the three numbers on the new object.
  6. A Holiday comes back. The caller receives a Holiday, as the second line of output shows. The last call takes the same path with a day that its month does not have, and there the check raises ValueError.

Checkpoint

Check: Class methods as constructors

  1. Question 1 of 2 Order.from_row(row) is a class method that ends with return cls(*row). RushOrder is a subclass of Order that defines nothing of its own. What does RushOrder.from_row(row) return?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: A RushOrder, because cls is RushOrder

    RushOrder.from_row(row) finds the class method in Order and passes RushOrder as cls, so return cls(*row) builds a RushOrder. That is why alternative constructors return cls(...): subclasses get them without writing anything.

  2. Question 2 of 2 d is a Holiday, a subclass of Day. What does the class method from_iso receive as cls in the call d.from_iso(text)?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Holiday, the class of d

    A class method always receives a class. Through the class it is that class; through an instance it is the instance's class, so d.from_iso(...) passes Holiday and returns a new Holiday.

A static method is a plain function kept in the class

is_valid is a static method: it needs neither an instance nor the class, only three numbers, and calendar.monthrange() supplies the length of the month, leap years included. It can be called through the class or through an instance, as the third line of output shows: Day.is_valid(2026, 2, 29) gives False, because 2026 is not a leap year, and d.is_valid(2028, 2, 29) gives True. Step through both calls and watch what each one receives:

Interactive · message by message Two calls of the static method is_valid Step through the third line of day_constructors.py. Each arrow is a call with the arguments it passes, or what a call returns; the middle column shows what is_valid received.
Two calls of the static method is_valid, step 6 of 6. Messages so far: Your code to is_valid: is_valid(2026, 2, 29); is_valid to calendar.monthrange: monthrange(2026, 2); is_valid to Your code: False; Your code to is_valid: d.is_valid(2028, 2, 29); is_valid to calendar.monthrange: monthrange(2028, 2); is_valid to Your code: True.Your codethe third print()is_validgot 2028, 2, 29calendar.monthrangedays in a monthis_valid(2026, 2, 29)monthrange(2026, 2)Falsed.is_valid(2028, 2, 29)monthrange(2028, 2)True

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. Through the class. Day.is_valid(2026, 2, 29) calls the function with exactly the three numbers written. A static method is given no instance and no class.
  2. How long is the month?. is_valid asks calendar.monthrange(2026, 2) how many days month 2 of the year 2026 has. 2026 is not a leap year, so the answer is 28.
  3. 29 is too many. Day 29 is past the 28 days of the month, so is_valid returns False, the first value of the third line of output.
  4. Through an instance. d.is_valid(2028, 2, 29) is called through the Day object d, yet the function again receives only the three numbers. Python puts nothing in front of them, neither d nor its class.
  5. A leap year. 2028 is a leap year, so monthrange(2028, 2) gives 29 days and day 29 exists.
  6. True through the instance. is_valid returns True, the second value of the third line. Both calls ran the same plain function on the arguments written, nothing more.

Neither call added an argument. A static method is just a function in the class’s namespace, so a module-level function is often as clear. Keep it in the class when it is part of what the class offers, such as a check that callers may want to run before they build a day.

Checkpoint

Check: Static methods

  1. One question Day.is_valid is a static method with the parameters (year, month, day), and d is a Day. What does the call d.is_valid(2028, 2, 29) pass to it?

    Choose one answer.

    Show the answer: One question

    Answer: Only 2028, 2 and 29

    A static method is a plain function kept in the class: nothing is added to the arguments, so is_valid receives 2028, 2 and 29 and returns True, because 2028 is a leap year.

A property runs a method when an attribute is read or assigned

A property lets a class check or compute a value while its users keep writing t.celsius, as if it were a plain attribute:

A temperature that refuses values below absolute zero Python · temperature.py
class Temperature:
    ABSOLUTE_ZERO = -273.15

    def __init__(self, celsius):
        self.celsius = celsius  # an assignment, so it runs the setter below

    @property
    def celsius(self):
        return self._celsius

    @celsius.setter
    def celsius(self, value):
        if value < Temperature.ABSOLUTE_ZERO:
            raise ValueError(f"{value} °C is below absolute zero")
        self._celsius = float(value)

    @property
    def fahrenheit(self):  # no setter: read-only, computed on every read
        return self._celsius * 9 / 5 + 32


t = Temperature(25)
print(t.celsius, t.fahrenheit)
t.celsius = -40
print(t.celsius, t.fahrenheit, vars(t))

for attempt in (lambda: setattr(t, "celsius", -300), lambda: Temperature(-500)):
    try:
        attempt()
    except ValueError as error:
        print("ValueError:", error)
try:
    t.fahrenheit = 0
except AttributeError as error:
    print("AttributeError:", error)
print(t.celsius)  # the refused values changed nothing

Output

25.0 77.0
-40.0 -40.0 {'_celsius': -40.0}
ValueError: -300 °C is below absolute zero
ValueError: -500 °C is below absolute zero
AttributeError: property 'fahrenheit' of 'Temperature' object has no setter
-40.0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 temperature.py

  • @property makes the first celsius method the getter: reading t.celsius calls it. @celsius.setter adds the setter: The method of a property that runs when the attribute is assigned, as in t.celsius = 30. It can check the value before storing it, usually under another name such as _celsius., which t.celsius = -40 calls with the new value. The setter stores the value under another name, _celsius, because the name celsius belongs to the property.
  • __init__ assigns self.celsius, not self._celsius, so the constructor goes through the same check: Temperature(-500) was refused like the assignment of -300, and neither changed the stored value.
  • fahrenheit has no setter, so it is read-only and computed from _celsius on every read; assigning it raises AttributeError.

Step through the two assignments and the constructor call of the program:

Interactive · message by message Two assignments and a constructor through one setter Step through temperature.py. Each arrow is an assignment or what the setter does with it; the right column is what t stores.
Two assignments and a constructor through one setter, step 6 of 6. Messages so far: Your code to property celsius: t.celsius = -40; property celsius to t's dictionary: _celsius = -40.0; Your code to property celsius: t.celsius = -300; property celsius to Your code: ValueError; Your code to property celsius: celsius = -500.Your codeassigns t.celsiusproperty celsiusgetter and settert's dictionary_celsius -40.0t.celsius = -40_celsius = -40.0t.celsius = -300ValueErrorcelsius = -500

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. An assignment. t.celsius = -40 stores nothing by itself. Because celsius is a property, the assignment calls its setter with the value -40.
  2. The check passes. The setter compares -40 with absolute zero, -273.15. The value is above it, so the setter goes on.
  3. Stored under _celsius. The setter stores float(value) under another name, _celsius, because the name celsius belongs to the property. That is the {'_celsius': -40.0} that vars(t) printed.
  4. An impossible value. t.celsius = -300 calls the same setter with -300.
  5. Refused. -300 is below -273.15, so the setter raises ValueError before it stores anything. The dictionary still holds -40.0, which the last line of output shows.
  6. The constructor too. __init__ assigns self.celsius, not self._celsius, so building Temperature(-500) goes through the same setter and is refused in the same way.

The biggest benefit is one that callers never see. A class can start with a plain celsius attribute and turn it into a property on the day it needs a check, and every line that reads or assigns t.celsius keeps working unchanged. That is why Python classes do not write get_celsius() and set_celsius() methods in advance. For a value that is expensive to compute and does not change, functools.cached_property computes it on the first read and keeps the result in the instance.

Checkpoint

Check: Properties, getters and setters

  1. Question 1 of 2 With the Temperature class of the example above, what does this print?

    Read the code, then choose one answer.

    t = Temperature(25)
    try:
        t.celsius = -300
    except ValueError:
        pass
    print(t.celsius)
    Show the answer: Question 1 of 2

    Answer: 25.0

    Temperature(25) runs the setter, which stores 25.0 under _celsius. The assignment of -300 runs the same setter, which raises ValueError before it stores anything, so reading t.celsius afterwards still gives 25.0.

  2. Question 2 of 2 fahrenheit is a property with a getter and no setter. What does t.fahrenheit = 0 do?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It raises AttributeError: property 'fahrenheit' of 'Temperature' object has no setter

    A property without a setter is read-only. Reading fahrenheit computes it from _celsius; assigning it raises AttributeError, which is what the example printed.

Check yourself

Practice that adapts to you

Practise this lesson

11 questions about 9 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 11 What does temperature.py print?

    What does this program print? Choose one answer.

    class Temperature:
        ABSOLUTE_ZERO = -273.15
    
        def __init__(self, celsius):
            self.celsius = celsius  # an assignment, so it runs the setter below
    
        @property
        def celsius(self):
            return self._celsius
    
        @celsius.setter
        def celsius(self, value):
            if value < Temperature.ABSOLUTE_ZERO:
                raise ValueError(f"{value} °C is below absolute zero")
            self._celsius = float(value)
    
        @property
        def fahrenheit(self):  # no setter: read-only, computed on every read
            return self._celsius * 9 / 5 + 32
    
    
    t = Temperature(25)
    print(t.celsius, t.fahrenheit)
    t.celsius = -40
    print(t.celsius, t.fahrenheit, vars(t))
    
    for attempt in (lambda: setattr(t, "celsius", -300), lambda: Temperature(-500)):
        try:
            attempt()
        except ValueError as error:
            print("ValueError:", error)
    try:
        t.fahrenheit = 0
    except AttributeError as error:
        print("AttributeError:", error)
    print(t.celsius)  # the refused values changed nothing
    Show the answer: Question 1 of 11

    Answer: it prints

    25.0 77.0
    -40.0 -40.0 {'_celsius': -40.0}
    ValueError: -300 °C is below absolute zero
    ValueError: -500 °C is below absolute zero
    AttributeError: property 'fahrenheit' of 'Temperature' object has no setter
    -40.0

    The setter stores the value under _celsius, because the name celsius belongs to the property, so that is what vars(t) shows. __init__ assigns through the property, so Temperature(-500) is refused just like the assignment of -300, and a refused value changes nothing: the last line is still -40.0.

  2. Question 2 of 11 What does this print?

    Read the code, then choose one answer.

    class Shape:
        @classmethod
        def make(cls):
            return cls()
    
    class Circle(Shape):
        pass
    
    print(type(Circle.make()).__name__, type(Circle().make()).__name__)
    Show the answer: Question 2 of 11

    Answer: Circle Circle

    A class method receives the class the call went through. Circle.make() passes Circle, and a call through a Circle instance passes that instance's class, also Circle, so both calls build a Circle.

  3. Question 3 of 11 A Postcode class needs a helper that checks whether a string has the right shape. It reads no attribute of an instance and does not need the class either. Which fits best?

    Choose one answer.

    Show the answer: Question 3 of 11

    Answer: A static method, or a function at module level

    A static method receives nothing extra, which is exactly what a helper that uses only its arguments needs. A class method would receive a class it never uses, and a property is read like an attribute, without arguments. A module-level function is just as good when the helper is not part of what the class offers.

  4. Question 4 of 11 What does this print?

    Read the code, then choose one answer.

    class Box:
        def __init__(self, side):
            self._side = side
    
        @property
        def volume(self):
            return self._side ** 3
    
    b = Box(2)
    first = b.volume
    b._side = 3
    print(first, b.volume)
    Show the answer: Question 4 of 11

    Answer: 8 27

    A property stores nothing itself: its getter runs on every read. The first read computed 2 cubed, and after the side changed, the next read computed 3 cubed. Assigning _side is allowed; only the property volume has no setter.

  5. Question 5 of 11 Which statements about a property celsius are true? Choose every right answer.

    Choose every answer that is right.

    Show the answer: Question 5 of 11

    Answer:

    • Reading t.celsius calls the property's getter
    • Without a setter, t.celsius = 30 raises AttributeError

    Reading and assigning go through the getter and the setter, and without a setter an assignment fails. Callers keep the attribute syntax, which is the point of a property. The property itself stores nothing: the getter and setter decide where the value lives, usually in a storage attribute such as _celsius.

  6. Question 6 of 11 A method in a class has no decorator above it. What does it receive as its first argument in the call obj.method()?

    Choose one answer.

    Show the answer: Question 6 of 11

    Answer: obj, the instance it was called on

    An instance method is the default: no decorator, and every call through an instance passes that instance as the first argument, self.

  7. Question 7 of 11 A Duration class needs Duration.from_minutes(90) to build an object, d.hours to read like an attribute, and a check Duration.valid_unit('h') that needs neither an object nor the class. Which kinds fit, in that order?

    Choose one answer.

    Show the answer: Question 7 of 11

    Answer: A class method, a property, a static method

    Choose by what the method needs: a constructor needs the class, an attribute-like value needs the instance and the attribute syntax, and a helper that needs neither is a static method or a plain function.

  8. Question 8 of 11 What is an alternative constructor?

    Choose one answer.

    Show the answer: Question 8 of 11

    Answer: A class method that turns another form of the data into the arguments of the normal constructor and returns cls(...)

    Day.from_iso(text) is an alternative constructor: it turns the text into year, month and day and returns cls(year, month, day). dict.fromkeys() and date.fromisoformat() are others in the standard library.

  9. Question 9 of 11 dict.fromkeys() is a class method of dict. What does this print?

    Read the code, then choose one answer.

    class Tally(dict):
        pass
    
    t = Tally.fromkeys(["a", "b"], 0)
    print(type(t).__name__, t)
    Show the answer: Question 9 of 11

    Answer: Tally {'a': 0, 'b': 0}

    The standard library's alternative constructors follow the same rule as Day.from_iso: called on a subclass, they build the subclass. Tally.fromkeys(...) returns a Tally whose keys all have the value 0.

  10. Question 10 of 11 What does a static method receive besides the arguments written in the call?

    Choose one answer.

    Show the answer: Question 10 of 11

    Answer: Nothing

    A static method is a plain function kept in the class's namespace. A call through the class or through an instance passes only the arguments written.

  11. Question 11 of 11 A class has a plain attribute price that 40 places in a program read and assign. Prices below 0 must now be refused. Which change keeps all 40 places working?

    Choose one answer.

    Show the answer: Question 11 of 11

    Answer: Make price a property whose setter checks the value and stores it under _price, and let __init__ assign self.price

    A property can replace a plain attribute without changing a single caller: reads go to the getter and assignments to the setter. Assigning the public name in __init__ makes construction go through the same check.

How each kind works inside, with three bugs reproduced Pro: opens with a Pro, Premium or Ultimate pass

Each kind of method is a different object stored in the class, and the attribute lookup asks that object what to hand back. This part opens the class’s dictionary, binds each kind by hand, and reproduces the three bugs that these mechanics cause most often.

In this part:

  • What the class stores, and what each lookup returns
  • Three bugs reproduced: the wrong type, a skipped check and endless recursion

1 diagram · 2 runnable examples (Python) · about 7 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

A property or a cached_property: the work counted, a stale value and a race Pro: opens with a Pro, Premium or Ultimate pass

A property runs its getter on every read, while a cached property runs it once and keeps the answer. That trade decides both the speed and the correctness of a computed attribute, so this part counts the work each one does, makes a cache go stale, reproduces a race between two threads, and ends with an exercise.

In this part:

  • Counting the work: 1,000 reads, 1,000 runs against one
  • A race reproduced: two threads, one instance, two runs of the getter
  • Which computed attribute to choose, with an exercise
    • A rectangle with checked sides and an alternative constructor
  • Five rules for methods and computed attributes

1 diagram · 4 runnable examples (Python) · 1 exercise with sample tests · about 14 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Interview questions

  1. Fresher · Warm-up

    What is the difference between @classmethod and @staticmethod?

    A class method receives the class as its first argument, usually named cls, whether it is called through the class or through an instance. A static method receives no implicit argument at all: it is a plain function kept in the class's namespace. Class methods are the tool for alternative constructors such as Day.from_iso(text): they return cls(...), so called on a subclass they build the subclass. Static methods suit helpers that belong with the class but need neither an instance nor the class, such as a check of three numbers before a day is built; a function at module level is often just as clear. Both can be called through the class or through an instance. A method without either decorator is an instance method and receives the instance as self.

  2. Fresher to mid

    PriorityOrder.from_csv_row(row) returns plain Order objects, so every priority order goes to the slow queue, and nothing raised an error. The constructor is a class method of Order. What is the likely bug, how do you prove it, and how do you prevent it in other constructors?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A widely used Product class has a public attribute price that about 40 places read and assign. You now have to reject negative prices and store prices as whole numbers of the smallest currency unit. How do you change the class without breaking its callers?

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    A Dashboard object computes an expensive summary with functools.cached_property. After the nightly data refresh, users see old numbers, and under load the summary query sometimes runs twice for one dashboard. Explain both problems and design a fix.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • An instance method receives the instance, a class method the class, a static method nothing extra, and a property runs a method when its attribute is read or assigned.
  • Write alternative constructors as class methods that return cls(...), so that subclasses get instances of themselves.
  • A static method is a plain function in the class’s namespace: keep it for helpers that belong to the class’s interface.
  • A property’s setter can refuse bad values; assign the public name in __init__ so that construction is checked too.
  • A property can replace a plain attribute later without changing a single caller.

Terms in this lesson

alternative constructor
A class method that builds an instance from another form of the data, such as a string or a row, by returning cls(...). date.fromisoformat() and dict.fromkeys() are alternative constructors.
class method
A method marked with @classmethod. It receives the class the call went through as its first argument, cls, whether it is called through the class or through an instance.
property
An attribute whose reads and assignments run methods: a getter and, optionally, a setter. @property turns a method into the getter, and callers keep writing obj.name.
setter
The method of a property that runs when the attribute is assigned, as in t.celsius = 30. It can check the value before storing it, usually under another name such as _celsius.
static method
A function kept in a class with @staticmethod. A call through the class or through an instance passes it no instance and no class, only the arguments written.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.