Intermediate Python 📂 Class and Object · 6 of 10 216 min read

Python OOP — Properties, Getter, Setter & Deleter Explained

Master Python's @property decorator — the clean way to expose attributes with validated reads, controlled writes, and custom cleanup on delete. Learn @property (getter), @x.setter, @x.deleter, read-only and computed properties through a bank-teller analogy, three animated SVG diagrams (property vs attribute, setter validation flow, full lifecycle), Temperature and BankAccount practical examples, and 10 golden rules.

🌐
SectionSection第 01 节

The Story That Explains Properties 🏦Properties समझाने वाली कहानी 🏦一个讲清属性(property)的故事 🏦

The Bank Teller — Not the Vault, Not the Form, the Friendly FaceBank Teller — न तिजोरी, न form, बस एक मददगार चेहरा银行柜员 — 不是金库,不是表格,而是友善的面孔
You walk into a bank and want to know your balance. Three doors face you.

Door 1 — walk into the vault yourself. You could grab any file and change any number. That is a public attribute — acc.balance = -999999 would work, no questions asked.

Door 2 — fill out request forms. Form A for "get balance", Form B for "deposit". Every read and write means calling acc.get_balance() or acc.set_balance(500). Safe, but wordy.

Door 3 — the friendly teller. You just ask "what's my balance?" (acc.balance). Behind the counter, the teller checks the records, applies the rules and gives you the answer. You filled out no form — but the checks still happened. Want to change your balance? Say acc.balance = 5000, and the teller checks it before writing.

The teller is Python's @property. From outside it looks and behaves like a plain attribute. Inside, it runs a method — a getter for reads, a setter for writes and a deleter for clean-up.
आप bank जाते हैं और अपना balance जानना चाहते हैं। सामने तीन दरवाज़े हैं।

दरवाज़ा 1 — ख़ुद तिजोरी में चले जाएँ। आप कोई भी file उठा सकते हैं, कोई भी नंबर बदल सकते हैं। यह public attribute है — acc.balance = -999999 बिना सवाल चल जाएगा।

दरवाज़ा 2 — request forms भरें। "Balance जानें" के लिए Form A, "जमा करें" के लिए Form B। हर पढ़ने-लिखने के लिए acc.get_balance() या acc.set_balance(500) बुलाना पड़ता है। सुरक्षित, पर लंबा-चौड़ा।

दरवाज़ा 3 — मददगार teller। आप बस पूछते हैं "मेरा balance कितना है?" (acc.balance)। Counter के पीछे teller records जाँचता है, नियम लगाता है और जवाब देता है। आपने कोई form नहीं भरा — फिर भी जाँच हुई। Balance बदलना है? acc.balance = 5000 कहिए, और teller लिखने से पहले जाँचता है।

यह teller Python का @property है। बाहर से यह सादे attribute जैसा दिखता और चलता है। अंदर यह एक method चलाता है — पढ़ने के लिए getter, लिखने के लिए setter और साफ़-सफ़ाई के लिए deleter।
你走进银行,想知道自己的余额。面前有三扇门。

第 1 扇门 — 自己走进金库。 你可以拿任何文件、改任何数字。这就是公有属性 — acc.balance = -999999 直接生效,没人过问。

第 2 扇门 — 填写申请表。 A 表"查询余额",B 表"存款"。每次读写都要调用 acc.get_balance() 或 acc.set_balance(500)。安全,但很啰嗦。

第 3 扇门 — 友善的柜员。 你只需问一句"我的余额是多少?"(acc.balance)。柜台后面,柜员查记录、按规则办事,再告诉你答案。你没填任何表 — 但检查照样做了。想改余额?说 acc.balance = 5000,柜员会在写入 之前 先检查。

这个柜员就是 Python 的 @property。从外部看,它像普通属性一样使用;在内部,它运行方法 — 读取时用 getter,写入时用 setter,清理时用 deleter。
🔑
The Core Insightमूल बात核心要点

A property is a special attribute whose read, write and delete are secretly method calls. Callers write obj.balance — but Python runs your @property method for reads, your @balance.setter for writes and your @balance.deleter for del obj.balance. Clean syntax outside, full control inside.

Property एक ख़ास attribute है जिसका पढ़ना, लिखना और delete करना असल में छिपी हुई method calls हैं। Callers obj.balance लिखते हैं — पर Python पढ़ने पर आपका @property method, लिखने पर @balance.setter और del obj.balance पर @balance.deleter चलाता है। बाहर साफ़ syntax, अंदर पूरा नियंत्रण।

属性(property) 是一种特殊的属性,它的读取、写入和删除背后其实都是方法调用。调用方写 obj.balance — 但 Python 在读取时运行你的 @property 方法,写入时运行 @balance.setter,del obj.balance 时运行 @balance.deleter。外面语法干净,里面完全可控。

▶️
This Tutorial Has Five Simulators and a Quizइस Tutorial में पाँच Simulators और एक Quiz है本教程有五个模拟器和一个小测验

Look for the interactive boxes: the Setter Gate and Setter Step by Step (Section 04), the Temperature Router (08), Account Tiers (09) and the Bug Doctor (10). Every result comes from real Python runs.

Interactive डिब्बे खोजें: Setter Gate और Setter क़दम-दर-क़दम (Section 04), Temperature Router (08), Account Tiers (09) और Bug Doctor (10)। हर नतीजा असली Python runs से है।

请留意这些互动框:Setter 关卡 和 Setter 逐步演示(第 04 节)、温度路由器(08)、账户等级(09)和 Bug 医生(10)。每个结果都来自真实的 Python 运行。


SectionSection第 02 节

The Problem Properties Solve 🧩Properties कौन-सी समस्या हल करती हैं 🧩属性要解决的问题 🧩

There are three ways to let callers reach data in a class. Two of them have serious downsides. Properties are the third — and the reason they exist.

Callers को class के data तक पहुँचने देने के तीन तरीक़े हैं। दो में गंभीर कमियाँ हैं। Properties तीसरा तरीक़ा हैं — और इसी वजह से वे बनीं।

让调用方访问类中数据有三种方式。其中两种有严重缺点。属性是第三种 — 也是它存在的原因。

# -- Option 1: public attribute - unsafe --------------------
class BankAccount:
    def __init__(self, balance):
        self.balance = balance

acc = BankAccount(1000)
acc.balance = -999999              # nothing stops this - the class contract is gone
print("Option 1:", acc.balance)


# -- Option 2: Java-style getter/setter methods - wordy -------
class BankAccount:
    def __init__(self, balance):
        self._balance = balance

    def get_balance(self):
        return self._balance

    def set_balance(self, value):
        if value < 0:
            raise ValueError("Balance cannot be negative")
        self._balance = value

acc = BankAccount(1000)
print("Option 2:", acc.get_balance())   # works, but every read is a method call
acc.set_balance(1500)                   # works - but the API is wordy


# -- Option 3: @property - safe AND clean ---------------------
class BankAccount:
    def __init__(self, balance):
        self.balance = balance         # goes through the setter - automatic validation

    @property
    def balance(self):                 # getter
        return self._balance

    @balance.setter
    def balance(self, value):          # setter
        if value < 0:
            raise ValueError("Balance cannot be negative")
        self._balance = value

acc = BankAccount(1000)
print("Option 3:", acc.balance)        # looks like a plain attribute...
acc.balance = 1500                     # ...but the setter enforces the rules
try:
    acc.balance = -999999
except ValueError as e:
    print("Option 3 blocked:", e)
OUTPUT
Option 1: -999999 Option 2: 1000 Option 3: 1000 Option 3 blocked: Balance cannot be negative
🏆
Why This Mattersयह क्यों ज़रूरी है为什么这很重要

With properties you can start with a plain public attribute (self.balance = ...) and later upgrade it to a checked property — without breaking any code that already uses acc.balance. Your class looks the same from outside, while the inside gains rules.

Properties के साथ आप सादे public attribute (self.balance = ...) से शुरू कर सकते हैं और बाद में उसे जाँच वाली property में बदल सकते हैं — acc.balance इस्तेमाल करने वाले किसी code को तोड़े बिना। Class बाहर से वैसी ही दिखती है, जबकि अंदर नियम जुड़ जाते हैं।

有了属性,你可以先 从 普通公有属性(self.balance = ...)开始,以后再把它 升级 为带检查的属性 — 而不会破坏任何已经在用 acc.balance 的代码。类的外观不变,内部却有了规则。


SectionSection第 03 节

@property — The Getter 🟩@property — Getter 🟩@property — Getter(读取器)🟩

The @property decorator turns a method into a read-only attribute. Callers write obj.area, and Python quietly calls the method. A computed property like area stores nothing — its value is whatever the method returns.

@property decorator एक method को सिर्फ़ पढ़ने वाले attribute में बदल देता है। Callers obj.area लिखते हैं, और Python चुपचाप method बुलाता है। area जैसी computed property कुछ नहीं रखती — उसकी value वही है जो method लौटाता है।

@property 装饰器把一个方法变成 只读属性。调用方写 obj.area,Python 悄悄调用该方法。像 area 这样的计算属性什么都不存 — 它的值就是方法返回的结果。

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @property
    def area(self):
        # Computed on the fly - no stored attribute
        return 3.14159 * self._radius ** 2

    @property
    def circumference(self):
        return 2 * 3.14159 * self._radius

c = Circle(5)
print(c.radius)          # looks like an attribute
print(c.area)            # same syntax, but computed
print(c.circumference)

# Trying to write to a property that has no setter
try:
    c.area = 100
except AttributeError as e:
    print("Blocked:", e)

print(c.__dict__)        # only _radius is stored; area is computed each time
OUTPUT
5 78.53975 31.4159 Blocked: property 'area' of 'Circle' object has no setter {'_radius': 5}
@property — Attribute Syntax, Method Behaviour@property — Attribute जैसा Syntax, Method जैसा व्यवहार@property — 属性的语法,方法的行为
A property looks like an attributeOUTSIDE CODEबाहर का CODE外部代码c.areareads like an attributeattribute जैसा पढ़ा像属性一样读取class Circle — insideclass Circle — अंदरclass Circle — 内部@propertyGETTER METHODGETTER METHODGETTER 方法def area(self): return 3.14159 * self._radius ** 2runs every time you write c.areaहर बार c.area लिखने पर चलता है每次写 c.area 时都会运行RESULTनतीजा结果78.53975

Every c.area lookup quietly runs the getter and returns what it computes.

हर c.area चुपचाप getter चलाता है और जो निकले वह लौटाता है।

每次访问 c.area 都会悄悄运行 getter,并返回它计算的结果。


SectionSection第 04 节

@x.setter — Checked Writes 🟧@x.setter — जाँच के साथ लिखना 🟧@x.setter — 带检查的写入 🟧

A property with only @property is read-only. To allow writes, add a matching @name.setter method. This is where validation lives: the setter catches every assignment and can reject a bad value before it touches the object.

सिर्फ़ @property वाली property सिर्फ़ पढ़ने के लिए होती है। लिखने देने के लिए मिलता-जुलता @name.setter method जोड़ें। जाँच यहीं रहती है: setter हर assignment पकड़ता है और ग़लत value को object तक पहुँचने से पहले ठुकरा सकता है।

只有 @property 的属性是 只读 的。要允许写入,就加一个对应的 @name.setter 方法。校验就放在这里:setter 拦截每一次赋值,可以在坏值碰到对象之前把它拒绝。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age  = age                   # goes THROUGH the setter

    @property
    def age(self):                        # GETTER
        return self._age

    @age.setter
    def age(self, value):                 # SETTER - same method name!
        if not isinstance(value, int):
            raise TypeError("age must be an int")
        if value < 0 or value > 150:
            raise ValueError("age must be 0..150")
        self._age = value

alice = Person("Alice", 30)
print(alice.age)               # getter runs

alice.age = 31                 # setter runs, passes validation
print(alice.age)

# -- Bad values are rejected AT THE SETTER, before touching the object --
try:
    alice.age = -5
except ValueError as e:
    print("Rejected:", e)

try:
    alice.age = "forty"
except TypeError as e:
    print("Rejected:", e)

print(alice.age)               # still 31 - the bad values never got in
OUTPUT
30 31 Rejected: age must be 0..150 Rejected: age must be an int 31
@x.setter — Check Before You Store@x.setter — रखने से पहले जाँचें@x.setter — 先检查再存储
Setter validation flowGOOD WRITEसही WRITE正确写入alice.age = 31BAD WRITEग़लत WRITE错误写入alice.age = -5@age.setterdef age(self, value): if not isinstance(value, int): raise TypeError(...) if value < 0 or value > 150: raise ValueError(...) self._age = valueSTOREDरखा गया已存储_age = 31REJECTEDठुकराया已拒绝ValueErrorBad values never reach _age. The setter is your gate.ग़लत values _age तक कभी नहीं पहुँचतीं। Setter आपका दरवाज़ा है।坏值永远到不了 _age。setter 就是你的关卡。

Good values pass through to storage. Bad values raise an exception and the object stays as it was.

सही values storage तक पहुँचती हैं। ग़लत values exception देती हैं और object जैसा था वैसा रहता है।

好值通过并被存储。坏值抛出异常,对象保持原样。

🚧 Simulator: The Setter Gate🚧 Simulator: Setter Gate🚧 模拟器:Setter 关卡

Pick a value for alice.age = … (she starts at 30). Watch it pass — or fail — each check in the setter. Try True for a surprise. All 8 results are real.

alice.age = … के लिए कोई value चुनें (शुरू में 30 है)। देखें कि वह setter की हर जाँच में pass होती है — या fail। हैरानी के लिए True आज़माएँ। सभी 8 नतीजे असली हैं।

为 alice.age = … 选一个值(初始为 30)。看它通过 — 或没通过 — setter 中的每一项检查。试试 True,会有惊喜。全部 8 个结果都是真实的。

The Setter GateSetter GateSetter 关卡every write passes the checksहर write जाँच से गुज़रती है每次写入都要过检查
1isinstance(value, int)
20 <= value <= 150
✓self._age = value
🛠️
Use a Different Name Underneathअंदर एक अलग नाम इस्तेमाल करें底层要用不同的名字

Never store the value under the property's own name (self.age inside the setter). That would call the setter again and again — RecursionError. The common convention: a property named x stores its value in _x. That is why the setter writes self._age, not self.age.

Value को property के अपने नाम में कभी न रखें (setter के अंदर self.age)। इससे setter बार-बार बुलाया जाएगा — RecursionError। आम परंपरा: x नाम की property अपनी value _x में रखती है। इसीलिए setter self._age लिखता है, self.age नहीं।

永远不要用属性自己的名字存值(在 setter 里写 self.age)。那会一次又一次地调用 setter — RecursionError。通用约定:名为 x 的属性把值存在 _x 中。所以 setter 写的是 self._age,而不是 self.age。

🎬 Simulator: The Setter Step by Step🎬 Simulator: Setter क़दम-दर-क़दम🎬 模拟器:Setter 逐步演示

Step through a real run. Notice that self.age = age inside __init__ opens the setter on the call stack, and print(alice.age) opens the getter. Switch to -5 to watch the setter reject the value. Recorded with sys.settrace.

एक असली run को step करें। ध्यान दें कि __init__ के अंदर self.age = age call stack पर setter खोलता है, और print(alice.age) getter खोलता है। -5 चुनें और setter को value ठुकराते देखें। sys.settrace से रिकॉर्ड।

逐步运行一次真实程序。注意 __init__ 里的 self.age = age 会在调用栈上打开 setter,print(alice.age) 会打开 getter。切换到 -5,看 setter 拒绝这个值。由 sys.settrace 记录。

The Setter Step by StepSetter क़दम-दर-क़दमSetter 逐步演示recorded with sys.settracesys.settrace से रिकॉर्ड由 sys.settrace 记录
Assign:Assign:赋值:
CodeCode代码
Call stack (newest at the bottom)Call stack (सबसे नया नीचे)调用栈(最新的在最下面)
The objectObject对象
OutputOutput输出

SectionSection第 05 节

@x.deleter — Custom Clean-Up on del obj.x 🟥@x.deleter — del obj.x पर अपनी साफ़-सफ़ाई 🟥@x.deleter — del obj.x 时的自定义清理 🟥

The third and least-used member of the trio. A deleter runs when a caller writes del obj.x. Use it when deleting should do more than just remove a value — reset to a default, close a connection, log out.

तिकड़ी का तीसरा और सबसे कम इस्तेमाल होने वाला सदस्य। Deleter तब चलता है जब caller del obj.x लिखता है। इसे तब इस्तेमाल करें जब delete करने पर value हटाने से ज़्यादा कुछ करना हो — default पर reset, connection बंद करना, log out।

三件套中第三个、也是最少用的成员。当调用方写 del obj.x 时,deleter 就会运行。当删除不只是移除一个值时使用它 — 重置为默认值、关闭连接、注销登录。

class Session:
    def __init__(self, token):
        self._token = token

    @property
    def token(self):
        return self._token

    @token.setter
    def token(self, value):
        if not isinstance(value, str) or len(value) < 8:
            raise ValueError("token must be a string of length ≥ 8")
        self._token = value

    @token.deleter
    def token(self):
        # Custom cleanup - logout, revoke, wipe
        print(f"Logging out session for token '{self._token[:4]}...'")
        self._token = None

s = Session("abc-secret-token")
print(s.token)

del s.token           # the deleter runs
print(s.token)
OUTPUT
abc-secret-token Logging out session for token 'abc-...' None

SectionSection第 06 节

The Full Property Lifecycle — Get · Set · Delete 🔄पूरा Property Lifecycle — Get · Set · Delete 🔄属性的完整生命周期 — 读 · 写 · 删 🔄

One property name, three operations, three methods behind the scenes. Read (x = acc.balance) → the getter runs. Write (acc.balance = 100) → the setter runs. Delete (del acc.balance) → the deleter runs.

एक property नाम, तीन operations, पर्दे के पीछे तीन methods। पढ़ना (x = acc.balance) → getter चलता है। लिखना (acc.balance = 100) → setter चलता है। Delete (del acc.balance) → deleter चलता है।

一个属性名,三种操作,背后三个方法。读取(x = acc.balance)→ 运行 getter。写入(acc.balance = 100)→ 运行 setter。删除(del acc.balance)→ 运行 deleter。

Get · Set · Delete — One Name, Three MethodsGet · Set · Delete — एक नाम, तीन Methods读 · 写 · 删 — 一个名字,三个方法
Property lifecycleREADपढ़ना读取x = acc.balance@property · GETTERdef balance(self): return self._balanceWRITEलिखना写入acc.balance = 100@balance.setter · SETTERdef balance(self, v): if v < 0: raise ValueError self._balance = vDELETEDELETE删除del acc.balance@balance.deleter · DELETERdef balance(self): print("closing"); del self._balance

Python sends each kind of access — read, write, delete — to the matching method automatically.

Python हर तरह की पहुँच — पढ़ना, लिखना, delete — अपने आप सही method तक भेजता है।

Python 自动把每种访问 — 读取、写入、删除 — 交给对应的方法。


SectionSection第 07 节

Special Cases — Read-Only and Computed Properties 🔒ख़ास मामले — सिर्फ़ पढ़ने वाली और Computed Properties 🔒特殊情况 — 只读属性与计算属性 🔒

Not every property needs a getter, a setter and a deleter. Three common patterns:

हर property को getter, setter और deleter तीनों की ज़रूरत नहीं होती। तीन आम patterns:

并不是每个属性都需要 getter、setter 和 deleter。三种常见模式:

🔒
Read-Only Propertyसिर्फ़ पढ़ने वाली Property只读属性
only @property, no setterसिर्फ़ @property, setter नहीं只有 @property,没有 setter
Define only @property. Any write raises AttributeError: … has no setter. Good for IDs and values that must not change.
सिर्फ़ @property define करें। हर write पर AttributeError: … has no setter। IDs और न बदलने वाली values के लिए अच्छा।
只定义 @property。任何写入都会抛出 AttributeError: … has no setter。适合 ID 等不该改变的值。
🧮
Computed PropertyComputed Property计算属性
derived from other attributesदूसरे attributes से निकली由其他属性推导
No stored value — the getter computes on every read: fahrenheit from celsius, full_name from first + last, age from a birth year.
कोई रखी हुई value नहीं — getter हर बार पढ़ने पर निकालता है: celsius से fahrenheit, first + last से full_name, जन्म-वर्ष से age।
不存储值 — 每次读取时由 getter 计算:由 celsius 得 fahrenheit,由名 + 姓得 full_name,由出生年份得 age。
🔁
Two-Way Syncदोनों तरफ़ का Sync双向同步
setter updates the stored valuesetter रखी value बदलता हैsetter 更新存储的值
A property with a getter and a setter that translates between two views of the same data. Set fahrenheit = 100 → the stored _celsius updates.
Getter और setter वाली property जो एक ही data के दो views के बीच बदलाव करती है। fahrenheit = 100 set करें → रखा हुआ _celsius बदल जाता है।
同时有 getter 和 setter 的属性,在同一数据的两种视图之间转换。设置 fahrenheit = 100 → 存储的 _celsius 随之更新。
class Person:
    def __init__(self, first, last, birth_year):
        self.first       = first
        self.last        = last
        self._birth_year = birth_year

    # -- COMPUTED read-only property --
    @property
    def full_name(self):
        return f"{self.first} {self.last}"

    # -- READ-ONLY property (no setter) --
    @property
    def birth_year(self):
        return self._birth_year

    # -- COMPUTED from the year - no storage --
    @property
    def age(self, _year=2026):
        return _year - self._birth_year

alice = Person("Alice", "Roy", 1995)
print(alice.full_name)     # computed
print(alice.age)           # computed
print(alice.birth_year)

# Change one part -> the computed value updates automatically
alice.first = "Alicia"
print(alice.full_name)     # recomputed on the fly

# Read-only - trying to write fails
try:
    alice.birth_year = 1990
except AttributeError as e:
    print("Blocked:", e)
OUTPUT
Alice Roy 31 1995 Alicia Roy Blocked: property 'birth_year' of 'Person' object has no setter

A note on age: Python calls a getter with self only, so _year always uses its default, 2026. The answer is right this year, but a real program would read the current year instead of hard-coding it.

age पर एक नोट: Python getter को सिर्फ़ self के साथ बुलाता है, इसलिए _year हमेशा अपना default, 2026, लेता है। इस साल जवाब सही है, पर असली program में साल hard-code करने की जगह मौजूदा साल पढ़ा जाना चाहिए।

关于 age 的说明:Python 调用 getter 时只传 self,所以 _year 总是使用默认值 2026。今年结果正确,但真实程序应该读取当前年份,而不是写死。


SectionSection第 08 节

Practical Example 1 — Temperature With Two-Way Conversion 🌡️व्यावहारिक उदाहरण 1 — दोनों तरफ़ बदलने वाला Temperature 🌡️实例 1 — 双向换算的温度 🌡️

A classic use of properties: one stored value (celsius) and several views of it (fahrenheit, kelvin). Set fahrenheit and the stored celsius updates. Every write is checked against absolute zero.

Properties का एक क्लासिक इस्तेमाल: एक रखी हुई value (celsius) और उसके कई views (fahrenheit, kelvin)। Fahrenheit set करें और रखा हुआ celsius बदल जाता है। हर write absolute zero से जाँची जाती है।

属性的经典用法:一个存储的值(摄氏)和它的多种 视图(华氏、开尔文)。设置华氏,存储的摄氏随之更新。每次写入都会对照绝对零度检查。

class Temperature:
    def __init__(self, celsius=0):
        self.celsius = celsius        # goes through the setter - validated at creation

    # == CELSIUS: stored, with validation ==
    @property
    def celsius(self):
        return self._celsius

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

    @celsius.deleter
    def celsius(self):
        print("Resetting temperature to 0°C")
        self._celsius = 0

    # == FAHRENHEIT: two-way view of the underlying celsius ==
    @property
    def fahrenheit(self):
        return self._celsius * 9 / 5 + 32

    @fahrenheit.setter
    def fahrenheit(self, value):
        # Convert to celsius and reuse the celsius setter - validation reused!
        self.celsius = (value - 32) * 5 / 9

    # == KELVIN: read-only view of the underlying celsius ==
    @property
    def kelvin(self):
        return self._celsius + 273.15


t = Temperature(25)
print(f"{t.celsius}°C = {t.fahrenheit}°F = {t.kelvin}K")

# Write to fahrenheit -> celsius updates too (two-way sync)
t.fahrenheit = 100
print(f"{t.celsius:.2f}°C = {t.fahrenheit}°F = {t.kelvin:.2f}K")

# Validation is enforced through EITHER path
try:
    t.celsius = -300
except ValueError as e:
    print("Blocked:", e)

try:
    t.fahrenheit = -500            # reuses the celsius validation
except ValueError as e:
    print("Blocked:", e)

# Read-only kelvin cannot be assigned
try:
    t.kelvin = 400
except AttributeError as e:
    print("Blocked:", e)

del t.celsius                     # the deleter runs -> resets to 0
print(f"After reset: {t.celsius}°C")
OUTPUT
25°C = 77.0°F = 298.15K 37.78°C = 100.0°F = 310.93K Blocked: -300°C is below absolute zero Blocked: -295.55555555555554°C is below absolute zero Blocked: property 'kelvin' of 'Temperature' object has no setter Resetting temperature to 0°C After reset: 0°C
One Stored Value, Three Viewsएक रखी Value, तीन Views一个存储值,三种视图
Two-way temperature syncSTORED · one numberरखा हुआ · एक नंबर存储 · 一个数字_celsiust.fahrenheitgetter + settert.kelvingetter only · read-onlyसिर्फ़ getter · read-only只有 getter · 只读t.celsiusgetter + setter + deleterfahrenheit setter → celsius setter → checkfahrenheit setter → celsius setter → जाँचfahrenheit setter → celsius setter → 检查

Only _celsius is stored. Fahrenheit and kelvin are computed on every read, and the fahrenheit setter reuses the celsius check.

सिर्फ़ _celsius रखा जाता है। Fahrenheit और kelvin हर बार पढ़ने पर निकाले जाते हैं, और fahrenheit setter celsius वाली जाँच दोबारा इस्तेमाल करता है।

只有 _celsius 被存储。华氏和开尔文在每次读取时计算,华氏的 setter 复用摄氏的检查。

🌡️ Simulator: The Temperature Router🌡️ Simulator: Temperature Router🌡️ 模拟器:温度路由器

Pick an operation and a value, then press Run. The method that fires lights up — getter, setter or deleter — and the thermometer shows the one stored number. Try writing −500 °F or assigning kelvin. Every output matched real Python on 300 random sequences.

कोई operation और value चुनें, फिर Run दबाएँ। जो method चलता है — getter, setter या deleter — वह चमकता है, और thermometer वह एक रखा हुआ नंबर दिखाता है। −500 °F लिखकर या kelvin assign करके देखें। हर output 300 random sequences पर असली Python से मिला।

选择操作和数值,然后按 Run。被触发的方法 — getter、setter 或 deleter — 会亮起,温度计显示那唯一存储的数字。试试写入 −500 °F,或给 kelvin 赋值。每个输出都在 300 个随机序列上与真实 Python 一致。

The Temperature RouterTemperature Router温度路由器which method fires?कौन-सा method चलता है?哪个方法被触发?
Operation:Operation:操作:
Value:Value:数值:
This line will runयह line चलेगी将要运行的代码
gettercelsius
settercelsius
deletercelsius
getterfahrenheit
setterfahrenheit
getterkelvin
no settersetter नहीं无 setterkelvin
ConsoleConsole控制台

SectionSection第 09 节

Practical Example 2 — BankAccount Balance With a Property 💰व्यावहारिक उदाहरण 2 — Property वाला BankAccount Balance 💰实例 2 — 用属性管理 BankAccount 余额 💰

class BankAccount:
    def __init__(self, holder, opening_balance):
        self.holder = holder
        self.balance = opening_balance    # goes THROUGH the setter
        self._log = []

    # -- Getter --
    @property
    def balance(self):
        return self._balance

    # -- Setter with full validation --
    @balance.setter
    def balance(self, value):
        if not isinstance(value, (int, float)):
            raise TypeError("balance must be numeric")
        if value < 0:
            raise ValueError("balance cannot be negative")
        self._balance = float(value)

    # -- Deleter closes the account --
    @balance.deleter
    def balance(self):
        print(f"Closing {self.holder}'s account, final balance {self._balance}")
        del self._balance

    # -- Read-only COMPUTED property --
    @property
    def is_rich(self):
        return self._balance > 100_000

    # -- Read-only COMPUTED property --
    @property
    def tier(self):
        if self._balance >= 1_000_000: return "Platinum"
        if self._balance >= 100_000:   return "Gold"
        if self._balance >= 10_000:    return "Silver"
        return "Standard"


acc = BankAccount("Alice", 5000)
print(acc.balance, acc.tier, acc.is_rich)

acc.balance = 150_000                     # the setter validates, then stores
print(acc.balance, acc.tier, acc.is_rich)

# Validation catches junk
try: acc.balance = -100
except ValueError as e: print("Blocked:", e)

try: acc.balance = "a lot"
except TypeError as e: print("Blocked:", e)

# Computed properties are read-only
try: acc.tier = "Platinum"
except AttributeError as e: print("Blocked:", e)

# The deleter runs custom cleanup
del acc.balance
try: print(acc.balance)
except AttributeError as e: print("After delete:", e)
OUTPUT
5000.0 Standard False 150000.0 Gold True Blocked: balance cannot be negative Blocked: balance must be numeric Blocked: property 'tier' of 'BankAccount' object has no setter Closing Alice's account, final balance 150000.0 After delete: 'BankAccount' object has no attribute '_balance'

The last line shows what the deleter left behind: _balance is gone, so the getter has nothing to return.

आख़िरी line दिखाती है कि deleter के बाद क्या बचा: _balance हट गया, इसलिए getter के पास लौटाने को कुछ नहीं।

最后一行显示了 deleter 之后的状态:_balance 已被删除,所以 getter 没有可返回的值。

🏆
Why Callers Never Notice the MachineryCallers को अंदर की मशीनरी क्यों नहीं दिखती为什么调用方察觉不到背后的机制

Every line above uses acc.balance or acc.tier like a plain attribute — no get_balance(), no set_balance(). Yet checks, computing and clean-up all run automatically. This is the uniform access principle: to the caller, a stored value, a computed value and a checked value all look the same — obj.attr.

ऊपर की हर line acc.balance या acc.tier को सादे attribute की तरह इस्तेमाल करती है — न get_balance(), न set_balance()। फिर भी जाँच, गणना और साफ़-सफ़ाई अपने आप चलती हैं। यह uniform access principle है: caller के लिए रखी हुई value, निकाली गई value और जाँची गई value — तीनों एक जैसी दिखती हैं — obj.attr।

上面每一行都把 acc.balance 或 acc.tier 当作普通属性使用 — 没有 get_balance(),也没有 set_balance()。但检查、计算和清理都自动进行。这就是 统一访问原则:对调用方来说,存储的值、计算的值和经过校验的值看起来都一样 — obj.attr。

💰 Simulator: Account Tiers💰 Simulator: Account Tiers💰 模拟器:账户等级

Drag the balance or pick a preset. Each move is an assignment that runs the setter, and tier and is_rich are recomputed every time you read them. Try exactly 100,000. The red buttons try values the setter rejects.

Balance खिसकाएँ या कोई preset चुनें। हर बदलाव एक assignment है जो setter चलाता है, और tier व is_rich हर बार पढ़ने पर फिर से निकाले जाते हैं। ठीक 100,000 आज़माएँ। लाल buttons वे values आज़माते हैं जिन्हें setter ठुकराता है।

拖动余额或选择预设。每次变动都是一次赋值,会运行 setter;tier 和 is_rich 每次读取时都会重新计算。试试恰好 100,000。红色按钮尝试会被 setter 拒绝的值。

Account TiersAccount Tiers账户等级computed on every readहर बार पढ़ने पर निकाला गया每次读取时计算
acc.balance
presets:presets:预设:
bad values:ग़लत values:坏值:
Standard< 10,000
Silver≥ 10,000
Gold≥ 100,000
Platinum≥ 1,000,000

SectionSection第 10 节

Common Mistakes (and Fixes) 🩹आम ग़लतियाँ (और उनके हल) 🩹常见错误(及修正)🩹

Mistakeग़लती错误What goes wrongक्या बिगड़ता है会出什么问题Fixहल修正
Storing under the same name as the propertyProperty के ही नाम में रखना用与属性相同的名字存储self.age = value inside the setter calls the setter again → RecursionErrorSetter के अंदर self.age = value setter को फिर बुलाता है → RecursionErrorsetter 里的 self.age = value 会再次调用 setter → RecursionErrorUse a different name: self._age = valueअलग नाम रखें: self._age = value换个名字:self._age = value
Forgetting to store the valueValue रखना भूल जाना忘记存储值The setter runs and the check passes — but nothing is savedSetter चलता है, जाँच pass होती है — पर कुछ save नहीं होताsetter 运行了,检查也通过了 — 但什么都没保存End the setter with self._x = valueSetter के आख़िर में self._x = value लिखें在 setter 末尾写 self._x = value
Writing @setter instead of @name.setter@name.setter की जगह @setter लिखना把 @name.setter 写成 @setterThere is no decorator called setter → NameErrorsetter नाम का कोई decorator नहीं → NameError不存在名为 setter 的装饰器 → NameErrorAlways use the property's name: @balance.setterहमेशा property का नाम लें: @balance.setter始终用属性名:@balance.setter
Giving the setter a different name from the getterSetter का नाम getter से अलग रखनाsetter 与 getter 名字不同It becomes a separate property; the original one still has no setterयह अलग property बन जाती है; असली में अब भी setter नहीं它变成另一个属性;原来的属性仍然没有 setterThe setter must have the same name as the getterSetter का नाम getter जैसा ही होsetter 必须与 getter 同名
Defining the getter again after the setterSetter के बाद getter फिर से define करना在 setter 之后再次定义 getterThe second @property replaces the whole property — the setter is silently lostदूसरा @property पूरी property बदल देता है — setter चुपचाप खो जाता है第二个 @property 替换整个属性 — setter 悄悄丢失Keep getter, setter and deleter together, defined once eachGetter, setter और deleter साथ रखें, हर एक एक बार把 getter、setter、deleter 放在一起,各定义一次
Doing slow work in a getterGetter में धीमा काम करना在 getter 里做耗时工作An innocent obj.total runs a database query on every readमासूम-सा obj.total हर बार पढ़ने पर database query चलाता है看似无害的 obj.total 每次读取都查询数据库Make it a method, or cache the result (functools.cached_property — a later tutorial)इसे method बनाएँ, या नतीजा cache करें (functools.cached_property — आगे के tutorial में)改成方法,或缓存结果(functools.cached_property — 后续教程)
Using a property when a method would be clearerजहाँ method साफ़ हो वहाँ property इस्तेमाल करना本该用方法却用了属性Callers expect no side effects; a hidden API call surprises themCallers side effects की उम्मीद नहीं करते; छिपी API call उन्हें चौंका देती है调用方以为没有副作用;隐藏的 API 调用让人意外If it has side effects or needs arguments, use a normal methodअगर side effects हों या arguments चाहिए, तो सामान्य method लें有副作用或需要参数时,用普通方法

🩺 Simulator: Bug Doctor🩺 Simulator: Bug Doctor🩺 模拟器:Bug 医生

Five of these mistakes, run for real. Buggy code on the left, fixed code on the right, changed lines marked.

इनमें से पाँच ग़लतियाँ, असली run में। बाईं ओर buggy code, दाईं ओर ठीक किया हुआ, बदली lines चिह्नित।

其中五个错误的真实运行。左边是有 bug 的代码,右边是修正后的代码,改动的行已标出。

Bug DoctorBug DoctorBug 医生5 bugs · real outputs5 bugs · असली outputs5 个 bug · 真实输出
❌ BuggyBuggy有 bug
✅ Fixedठीक किया已修正

SectionSection第 11 节

Quick Reference 📋Quick Reference 📋速查表 📋

Taskकाम任务SyntaxSyntax语法Notesनोट说明
Define a getterGetter define करना定义 getter@property + def x(self):Read-only unless a setter followsSetter न हो तो सिर्फ़ पढ़ने के लिए没有 setter 时为只读
Define a setterSetter define करना定义 setter@x.setter + def x(self, value):Must match the getter's nameGetter के नाम से मिलना चाहिए必须与 getter 同名
Define a deleterDeleter define करना定义 deleter@x.deleter + def x(self):Runs on del obj.xdel obj.x पर चलता है在 del obj.x 时运行
Store the valueValue रखना存储值self._x = valueUnderscore name — the common conventionUnderscore वाला नाम — आम परंपरा带下划线的名字 — 通用约定
Read from outsideबाहर से पढ़ना从外部读取obj.xSame as a plain attributeसादे attribute जैसा与普通属性相同
Trigger the setterSetter चलाना触发 setterobj.x = valueJust an assignment — the setter runs by itselfबस एक assignment — setter अपने आप चलता है只是一次赋值 — setter 自动运行
Trigger the deleterDeleter चलाना触发 deleterdel obj.xYour deleter decides what happensक्या होगा, आपका deleter तय करता है由你的 deleter 决定发生什么
Read-only propertyसिर्फ़ पढ़ने वाली property只读属性Only @property, no setterसिर्फ़ @property, setter नहीं只有 @property,没有 setterAssignment raises AttributeErrorAssignment पर AttributeError赋值会抛出 AttributeError
Computed propertyComputed property计算属性The getter returns a value derived from other attributesGetter दूसरे attributes से निकली value लौटाता हैgetter 返回由其他属性推导出的值No stored value neededकोई रखी value नहीं चाहिए无需存储值
Expensive computed valueमहँगी computed value开销大的计算值@functools.cached_propertyRuns once, keeps the result — covered in an upcoming tutorialएक बार चलता है, नतीजा रखता है — आने वाले tutorial में只运行一次并保存结果 — 将在后续教程讲解

SectionSection第 12 节

Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆

🏦 Properties, Getter, Setter and Deleter — Rules to RememberProperties, Getter, Setter और Deleter — याद रखने वाले नियम属性、Getter、Setter 与 Deleter — 必须记住的规则
1
A property is an attribute that runs a method. Callers use obj.x — no parentheses. Inside, you decide what that means.
Property एक attribute है जो method चलाता है। Callers obj.x लिखते हैं — parentheses नहीं। अंदर आप तय करते हैं कि इसका मतलब क्या है।
属性 是会运行方法的属性。调用方写 obj.x — 不加括号。里面是什么含义由你决定。
2
Start with a plain attribute. When you need checks, logging or computing, upgrade to @property — without breaking the class's API.
सादे attribute से शुरू करें। जब जाँच, logging या गणना चाहिए, तो @property में बदलें — class की API तोड़े बिना।
从普通属性开始。需要检查、日志或计算时,再升级为 @property — 不破坏类的 API。
3
Store the value in self._x, not self.x. Writing self.x inside the setter calls the setter again → RecursionError.
Value self._x में रखें, self.x में नहीं। Setter के अंदर self.x लिखने से setter फिर बुलाया जाता है → RecursionError।
把值存在 self._x 中,而不是 self.x。在 setter 里写 self.x 会再次调用 setter → RecursionError。
4
The setter's name must exactly match the getter's, and its decorator is @x.setter. Different names mean two unrelated properties.
Setter का नाम getter से हूबहू मिलना चाहिए, और उसका decorator @x.setter है। अलग नाम का मतलब दो असंबंधित properties।
setter 的名字必须与 getter 完全相同,装饰器是 @x.setter。名字不同就成了两个无关的属性。
5
A property with only a getter is read-only. Writing to it raises AttributeError. Use this on purpose for values that must not change.
सिर्फ़ getter वाली property सिर्फ़ पढ़ने के लिए है। उसमें लिखने पर AttributeError आता है। न बदलने वाली values के लिए इसे सोच-समझकर इस्तेमाल करें।
只有 getter 的属性是只读的。写入会抛出 AttributeError。对不应改变的值有意使用它。
6
Check values inside the setter. Raising an exception there guarantees bad data never reaches the stored attribute — and self.x = x in __init__ runs the same check.
Values setter के अंदर जाँचें। वहाँ exception देने से ग़लत data रखे हुए attribute तक कभी नहीं पहुँचता — और __init__ में self.x = x वही जाँच चलाता है।
在 setter 里检查值。在那里抛出异常可以保证坏数据永远到不了存储的属性 — 而 __init__ 里的 self.x = x 也会运行同样的检查。
7
Computed properties (full_name, fahrenheit) are recalculated on every read. That is usually fine; if it is slow, make it a method or cache it.
Computed properties (full_name, fahrenheit) हर बार पढ़ने पर फिर से निकाली जाती हैं। आमतौर पर यह ठीक है; धीमा हो तो method बनाएँ या cache करें।
计算属性(full_name、fahrenheit)每次读取都会重新计算。通常没问题;如果慢,就改成方法或加缓存。
8
Use a property when the work is cheap and has no side effects. If it needs arguments, does I/O or surprises anyone, make it a normal method.
Property तब लें जब काम सस्ता हो और side effects न हों। अगर arguments चाहिए, I/O हो या किसी को चौंकाए, तो सामान्य method बनाएँ।
当操作 开销小且无副作用 时才用属性。需要参数、涉及 I/O 或会让人意外时,就用普通方法。
9
The deleter is optional and rare. Add it only when del obj.x needs clean-up — reset to a default, close something, log out.
Deleter वैकल्पिक और कम इस्तेमाल होने वाला है। इसे तभी जोड़ें जब del obj.x पर साफ़-सफ़ाई चाहिए — default पर reset, कुछ बंद करना, log out।
deleter 是可选的,也很少用。只有当 del obj.x 需要清理时才添加 — 重置为默认值、关闭资源、注销登录。
10
Prefer @property over Java-style get_x() / set_x() methods. Callers get cleaner code, and you keep the freedom to change the inside later.
Java-जैसे get_x() / set_x() methods की जगह @property चुनें। Callers को साफ़ code मिलता है, और आपको बाद में अंदर बदलने की आज़ादी रहती है।
优先使用 @property,而不是 Java 风格的 get_x() / set_x() 方法。调用方代码更简洁,你也保留了日后修改内部实现的自由。

Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题

🏆 QuizPick an answer to see why.जवाब चुनें और कारण देखें।选一个答案,看看原因。
🎯
Key Takeawayमुख्य सीख关键要点

@property makes a method look like an attribute: reading obj.x runs the getter, obj.x = v runs @x.setter, and del obj.x runs @x.deleter. Store the real value in _x, put your checks in the setter (even __init__ goes through it), leave out the setter for read-only values, and compute derived values in getters.

@property एक method को attribute जैसा बना देता है: obj.x पढ़ने पर getter, obj.x = v पर @x.setter, और del obj.x पर @x.deleter चलता है। असली value _x में रखें, जाँच setter में रखें (__init__ भी उसी से गुज़रता है), सिर्फ़ पढ़ने वाली values के लिए setter न लिखें, और निकाली जाने वाली values getters में बनाएँ।

@property 让方法看起来像属性:读取 obj.x 运行 getter,obj.x = v 运行 @x.setter,del obj.x 运行 @x.deleter。把真实值存在 _x 中,把检查写在 setter 里(连 __init__ 也要经过它),只读值就不写 setter,推导值在 getter 中计算。

🐍 Try Python Yourself

Write, run and visualize Python step by step. No install needed.