2.1.7 · D1OOP Fundamentals

Foundations — Properties — `@property`, `@setter`, `@deleter` for controlled access

2,635 words12 min readBack to topic

Before you can appreciate properties, you must be fluent in the little pieces of vocabulary the parent note quietly assumes. Below we build every one from absolute zero — plain words, then a picture, then why the topic needs it.


0. What is an object and an attribute?

Figure — Properties — `@property`, `@setter`, `@deleter` for controlled access

Read Figure 1: the blue box is the object t; the two lines inside it are attributes it holds. The orange label t.celsius on the left, with the arrow diving into the box, is the picture of the dot: it says "start at box t, then walk in to find the thing named celsius." Keep this arrow-into-a-box image in your head — everything below is just what that arrow does on the way in.

Why the topic needs it: properties exist only to change what the dot does. If you don't picture the dot as "go find a thing named X on this box", nothing later makes sense.


1. The self word and the _ (underscore) convention

Figure — Properties — `@property`, `@setter`, `@deleter` for controlled access

Read Figure 2: the blue box on the left is the public name celsius (the guard); the green box on the right is the private name _celsius (where the number actually lives). The gray arrow "stores into" shows the guard handing the value to the private box. The red panel at the bottom is the trap: if the guard tries to store into itself (self.celsius = value) the arrow would loop back to the guard forever — that is the RecursionError the parent warns about.


2. What is a method, and why is it "just a function on a class"?

Why the topic needs it: a property is built out of methods. @property takes the celsius method and repackages it so you can drop the () and just write t.celsius.


3. The @decorator symbol — and the three slots fget / fset / fdel

Figure — Properties — `@property`, `@setter`, `@deleter` for controlled access

Read Figure 3: on the left is your plain function celsius(self); the orange middle box property( ) is the wrapping machine; the blue box on the right is the finished property object. The two arrows show the flow: function → wrapper → property. The big orange @property label reminds you that this whole three-step flow is what that one line triggers. The finished object is what holds the fget/fset/fdel slots from the definition above.

Why the @x.setter reuses the name x: @property first makes an object named x with fget filled. Then x.setter(func) returns a copy of that object with the fset slot now filled too, and the name x is rebound to the copy. @x.deleter does the same for fdel. All three slots accumulate into one object. (Compare Getters and Setters in Java vs Python — Java needs no decorator because it never had the plain-attribute syntax to protect.)


4. What is a descriptor? (the engine under properties)

Why the topic needs it: the parent's whole "properties beat instance attributes" guarantee is just this one fact — data descriptors win.


5. __dict__, __mro__, and why the lookup order is what it is

Figure — Properties — `@property`, `@setter`, `@deleter` for controlled access

Read Figure 4: the five stacked boxes are the priority ladder, checked top to bottom; the arrows show Python falling through to the next rung only when the current one has no match. The green top rung is where a property sits (data descriptor) — above the blue instance-dict rung, which is the visual reason the setter always runs. The bottom red rung is the "nothing matched" outcome, AttributeError.


6. Edge cases: del, an empty fset, and how deleting touches storage


Prerequisite map

Object and attribute dot

self and underscore names

Method is a function on a class

Decorator wraps a function

property object with fget fset fdel

Descriptor get set delete

Data vs non-data descriptor

obj dict and mro lookup order

Property wins over instance dict

Invariant a rule that must hold

Controlled access via property


7. Invariant — the reason all this exists


Equipment checklist

Test yourself — cover the right side.

The dot in t.celsius means
"the thing named celsius belonging to object t" — go find attribute celsius on t.
Inside a method, self refers to
the exact object the method was called on (e.g. t in t.celsius).
Why store the value in self._celsius not self.celsius
assigning to celsius would re-trigger the property setter → infinite recursion; _celsius is a separate backing field.
@property above def celsius is shorthand for
celsius = property(celsius) — the function is wrapped into a property object with fget filled.
What are fget, fset, fdel
the three slots of a property holding the functions run on get, set, and delete respectively.
A descriptor is an object that defines
at least one of __get__, __set__, __delete__.
A data descriptor differs from a non-data descriptor because
it defines __set__ or __delete__, so it wins over the instance's __dict__.
The full attribute-lookup priority order is
data descriptor > instance __dict__ > non-data descriptor > plain class attribute > AttributeError.
Why does a data descriptor beat the instance dict
it defines __set__, a promise to control writes; letting the instance shadow it would break that guarantee, so the class descriptor wins.
Why is a non-data descriptor deferred below the instance dict
it makes no claim over writes (only __get__), so a deliberately stored instance value is allowed to override it.
What does __mro__ decide vs what does the priority rule decide
__mro__ picks which class-level x is found; the priority rule decides whether it beats the instance's own copy.
What does del acc.balance do when only @balance.deleter is defined
it runs fdel (cleanup), it does not remove anything from acc.__dict__.
What happens on acc.balance = 5 when no setter is defined
AttributeError: can't set attribute — the empty fset actively blocks it.

See the parent: Properties (Hinglish).