The Story That Explains Properties 🏦Properties समझाने वाली कहानी 🏦一个讲清属性(property)的故事 🏦
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.
दरवाज़ा 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。
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。外面语法干净,里面完全可控。
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 运行。
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)
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 的代码。类的外观不变,内部却有了规则。
@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
Every c.area lookup quietly runs the getter and returns what it computes.
हर c.area चुपचाप getter चलाता है और जो निकले वह लौटाता है।
每次访问 c.area 都会悄悄运行 getter,并返回它计算的结果。
@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
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 个结果都是真实的。
isinstance(value, int)0 <= value <= 150self._age = valueNever 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 记录。
@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)
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。
Python sends each kind of access — read, write, delete — to the matching method automatically.
Python हर तरह की पहुँच — पढ़ना, लिखना, delete — अपने आप सही method तक भेजता है।
Python 自动把每种访问 — 读取、写入、删除 — 交给对应的方法。
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。三种常见模式:
@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 等不该改变的值。fahrenheit from celsius, full_name from first + last, age from a birth year.celsius से fahrenheit, first + last से full_name, जन्म-वर्ष से age।celsius 得 fahrenheit,由名 + 姓得 full_name,由出生年份得 age。fahrenheit = 100 → the stored _celsius updates.fahrenheit = 100 set करें → रखा हुआ _celsius बदल जाता है।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)
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。今年结果正确,但真实程序应该读取当前年份,而不是写死。
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")
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 一致。
celsiuscelsiuscelsiusfahrenheitfahrenheitkelvinkelvinPractical 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)
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 没有可返回的值。
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 拒绝的值。
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 → RecursionError | Use 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 写成 @setter | There is no decorator called setter → NameErrorsetter नाम का कोई decorator नहीं → NameError不存在名为 setter 的装饰器 → NameError | Always 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 नहीं它变成另一个属性;原来的属性仍然没有 setter | The setter must have the same name as the getterSetter का नाम getter जैसा ही होsetter 必须与 getter 同名 |
| Defining the getter again after the setterSetter के बाद getter फिर से define करना在 setter 之后再次定义 getter | The 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 的代码,右边是修正后的代码,改动的行已标出。
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 = value | Underscore name — the common conventionUnderscore वाला नाम — आम परंपरा带下划线的名字 — 通用约定 |
| Read from outsideबाहर से पढ़ना从外部读取 | obj.x | Same as a plain attributeसादे attribute जैसा与普通属性相同 |
| Trigger the setterSetter चलाना触发 setter | obj.x = value | Just an assignment — the setter runs by itselfबस एक assignment — setter अपने आप चलता है只是一次赋值 — setter 自动运行 |
| Trigger the deleterDeleter चलाना触发 deleter | del obj.x | Your deleter decides what happensक्या होगा, आपका deleter तय करता है由你的 deleter 决定发生什么 |
| Read-only propertyसिर्फ़ पढ़ने वाली property只读属性 | Only @property, no setterसिर्फ़ @property, setter नहीं只有 @property,没有 setter | Assignment 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_property | Runs once, keeps the result — covered in an upcoming tutorialएक बार चलता है, नतीजा रखता है — आने वाले tutorial में只运行一次并保存结果 — 将在后续教程讲解 |
Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆
obj.x — no parentheses. Inside, you decide what that means.obj.x लिखते हैं — parentheses नहीं। अंदर आप तय करते हैं कि इसका मतलब क्या है।obj.x — 不加括号。里面是什么含义由你决定。@property — without breaking the class's API.@property में बदलें — class की API तोड़े बिना।@property — 不破坏类的 API。self._x, not self.x. Writing self.x inside the setter calls the setter again → RecursionError.self._x में रखें, self.x में नहीं। Setter के अंदर self.x लिखने से setter फिर बुलाया जाता है → RecursionError।self._x 中,而不是 self.x。在 setter 里写 self.x 会再次调用 setter → RecursionError。@x.setter. Different names mean two unrelated properties.@x.setter है। अलग नाम का मतलब दो असंबंधित properties।@x.setter。名字不同就成了两个无关的属性。AttributeError. Use this on purpose for values that must not change.AttributeError आता है। न बदलने वाली values के लिए इसे सोच-समझकर इस्तेमाल करें।AttributeError。对不应改变的值有意使用它。self.x = x in __init__ runs the same check.__init__ में self.x = x वही जाँच चलाता है।__init__ 里的 self.x = x 也会运行同样的检查。full_name, fahrenheit) are recalculated on every read. That is usually fine; if it is slow, make it a method or cache it.full_name, fahrenheit) हर बार पढ़ने पर फिर से निकाली जाती हैं। आमतौर पर यह ठीक है; धीमा हो तो method बनाएँ या cache करें।full_name、fahrenheit)每次读取都会重新计算。通常没问题;如果慢,就改成方法或加缓存。del obj.x needs clean-up — reset to a default, close something, log out.del obj.x पर साफ़-सफ़ाई चाहिए — default पर reset, कुछ बंद करना, log out।del obj.x 需要清理时才添加 — 重置为默认值、关闭资源、注销登录。@property over Java-style get_x() / set_x() methods. Callers get cleaner code, and you keep the freedom to change the inside later.get_x() / set_x() methods की जगह @property चुनें। Callers को साफ़ code मिलता है, और आपको बाद में अंदर बदलने की आज़ादी रहती है।@property,而不是 Java 风格的 get_x() / set_x() 方法。调用方代码更简洁,你也保留了日后修改内部实现的自由。Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题
@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 中计算。