skip to content

How must @abc.abstractmethod be stacked with @property or @classmethod, and why?

level: middleimportance: should knowfreq 40%

answer

  1. Decorators apply bottom-up
  2. One is writable, the other is not
  3. abstractmethod goes closest to def
  4. Wrong order fails at class definition
  5. property computes __isabstractmethod__ read-only

basics

~10 s

Put @abstractmethod innermost, directly above the def, with @property, @classmethod or @staticmethod outside it. The reverse order raises AttributeError while the class body runs, because those wrapper objects expose a read-only isabstractmethod.

solid answer

~40 s

Decorators apply bottom-up, so `@abstractmethod` must sit closest to the `def`: it flags the plain function, and `@property`, `@classmethod` or `@staticmethod` then wraps something already marked. Those three wrapper types compute `__isabstractmethod__` from whatever they wrap and expose it **read-only**, so putting `@abstractmethod` on the outside fails immediately with `AttributeError: attribute '__isabstractmethod__' of 'property' objects is not writable` — an error at class-definition time, which is a good outcome compared with silently producing a class that is not abstract. With the correct order `ABCMeta` sees the name normally and adds it to `__abstractmethods__`. A subclass satisfies an abstract property with a concrete `@property` or, because the check is name presence only, with a plain class attribute. `abc.abstractproperty` and its siblings still import but have been deprecated since 3.3.

code

python · 20 lines
python
from abc import ABC, abstractmethod

class CatalogueSource(ABC):
    @property
    @abstractmethod
    def record_count(self): ...

    @classmethod
    @abstractmethod
    def from_manifest(cls, manifest): ...

print(sorted(CatalogueSource.__abstractmethods__))

try:
    class Broken(ABC):
        @abstractmethod
        @property
        def record_count(self): ...
except AttributeError as exc:
    print(exc)

go deeper

for a junior

Recall the shape rather than the theory: @property on top, @abstractmethod underneath, def at the bottom. If you see the opposite order in a snippet, say it will fail as the class is created.

for a middle

Explain the cause, not just the rule. abstractmethod assigns isabstractmethod, which is writable on a function and read-only on property, classmethod and staticmethod objects, and those wrappers derive their own abstractness from what they wrap.

for a senior

Be the person who notices this in review, and add what the check does not buy you: a subclass can satisfy an abstract property with a bare class attribute, so treat the ABC as a naming contract and rely on tests or static checking for the shape of the value.

for a principal

Decide how much of your public interface should be expressed as abstract properties at all. Read-only-by-declaration contracts that the runtime cannot enforce are a documentation choice, and consistency across a code base matters more than any single class getting it right.

### The rule `@abc.abstractmethod` goes **innermost** — directly above the `def` — and `@property`, `@classmethod` or `@staticmethod` goes outside it. Decorators apply bottom-up, so this order means `abstractmethod` receives the plain function, flags it, and hands it to the wrapper, which then wraps a function that is already marked. ```python class CatalogueSource(ABC): @property @abstractmethod def record_count(self): ... ``` ### Why the other order is an error, not a style choice `abc.abstractmethod` works by assigning `func.__isabstractmethod__ = True`. That works on a plain function, whose `__dict__` accepts arbitrary attributes. It does not work on a `property`, `classmethod` or `staticmethod` object: each of those types exposes `__isabstractmethod__` as a **read-only** computed attribute that reports `True` when any callable it wraps is abstract — the getter, setter and deleter for a `property`, the underlying function for the other two. Trying to assign it raises immediately, while the class body is still executing: ``` AttributeError: attribute '__isabstractmethod__' of 'property' objects is not writable ``` That is a genuinely nice failure: the wrong order blows up at import time rather than producing a class that silently is not abstract. Contrast that with the classic `@staticmethod` ordering bug of years past, where the wrong order simply produced something that never got flagged. ### What ABCMeta sees Because the wrapper reports abstractness for whatever it wraps, `ABCMeta` picks the name up normally: `sorted(CatalogueSource.__abstractmethods__)` is `['from_manifest', 'record_count']`, and instantiating `CatalogueSource` or any subclass that has not supplied both names raises `TypeError`. Nothing about the abstract-method mechanism changes; only the descriptor sitting on the class differs. A `property` is abstract if *any* of its three functions is abstract. In practice you mark the getter, and the whole descriptor is abstract: ```python class Source(ABC): @property @abstractmethod def record_count(self): ... @record_count.setter @abstractmethod def record_count(self, value): ... ``` Marking both is only worth doing when you actually want the setter documented as required; the abstract-name check cannot tell a subclass "you implemented the getter but not the setter", because it works on the name, not on the descriptor's parts. ### What a subclass has to provide Anything bound to that name. The natural implementation is a concrete `@property`, but because the check is presence-only, a plain class attribute satisfies it too: ```python class FileSource(CatalogueSource): record_count = 17 # satisfies the abstract property @classmethod def from_manifest(cls, manifest): return cls() ``` Both forms clear `__abstractmethods__`. This surprises people who expect the ABC to insist on a descriptor; it is the same "names, not contracts" property that ABCs have everywhere. If callers must be able to *set* the value, that is a review question, not something the machinery checks. For an abstract `classmethod`, the subclass supplies a real `classmethod`; for an abstract `staticmethod`, a real `staticmethod`. The ordering rule is identical in both cases and the same read-only `__isabstractmethod__` is what makes it so. ### The legacy helpers `abc.abstractproperty`, `abc.abstractclassmethod` and `abc.abstractstaticmethod` still exist and still import on 3.14, but they have been **deprecated since Python 3.3**, when stacking was made to work. They are single decorators that combine both roles. Seeing them in a code base is a dating clue rather than a bug, but writing them in new code is a small red flag: they do not compose, and the stacked form is what the documentation and every modern example use. ### The interview shape of this The question is usually asked as "what is wrong with this class?" over a snippet with `@abstractmethod` on top of `@property`. The complete answer names the failure (an `AttributeError` at class-definition time), the cause (`__isabstractmethod__` is writable on a function and read-only on the wrapper types), the fix (`abstractmethod` innermost), and the corollary that the wrapper computes its own abstractness from what it wraps — which is precisely why the correct order needs no special support from `ABCMeta` at all.

  • What must a subclass provide to satisfy an abstract property?
    Any binding of that name. The idiomatic answer is a concrete `@property`, but a plain class attribute such as `record_count = 17` clears `__abstractmethods__` just as well, because ABCMeta checks name presence rather than the kind of descriptor. Nothing verifies that a setter exists either — if callers must assign to the attribute, that is a code-review concern, not something the ABC machinery enforces.
  • Why do abc.abstractproperty and abc.abstractclassmethod still exist?
    They predate decorator stacking working correctly and are kept for old code; they have been deprecated since Python 3.3 and still import on 3.14. They combine both roles in one decorator, which means they do not compose with anything else. Seeing them dates a code base; writing them today is a small red flag in review.
  • Does an abstract property need both its getter and setter marked?
    No. A `property` reports itself abstract if any of its getter, setter or deleter is abstract, so marking the getter is enough to put the name in `__abstractmethods__`. Marking the setter too is documentation for humans; the machinery cannot tell a subclass that it implemented the getter but forgot the setter, because it only looks at the name.

saying these in an interview costs you the question

  • Puts @abstractmethod above @property and expects it to work
  • Says decorator order here is cosmetic
  • Uses abc.abstractproperty in new code as the current API
  • Thinks the ordering error surfaces at instantiation
  • Believes an abstract property requires a descriptor in the subclass

context