The Story That Explains Private Members 🏠Private Members समझाने वाली कहानी 🏠一个讲清私有成员的故事 🏠
The bulletin board in the hallway — notes about the rubbish schedule, the WiFi password, the address. Anyone can read them and update them. These are public attributes.
Your bedroom door with a "please knock" sign — the door is not locked. Your flatmates could walk in, but by convention they don't. If they enter anyway, it is their responsibility if something breaks. These are protected attributes (one underscore).
Your locked safe inside the bedroom — passport, bank cards, diary. The house rules actively rename the safe so nobody finds it by accident. You can technically still open it, but you have clearly gone out of your way. These are private attributes (two underscores, name-mangled).
Python has no hard
private keyword. It uses naming conventions and one small piece of magic (name mangling) to show intent: "public — help yourself", "protected — don't unless you know what you're doing", and "private — really, this is not for you".गलियारे का notice board — कूड़े का schedule, WiFi password, पता। कोई भी इन्हें पढ़ और बदल सकता है। ये public attributes हैं।
"कृपया दस्तक दें" लिखा आपका bedroom का दरवाज़ा — दरवाज़ा बंद नहीं है। साथी अंदर आ सकते हैं, पर परंपरा से नहीं आते। फिर भी आएँ, तो कुछ टूटने की ज़िम्मेदारी उनकी। ये protected attributes हैं (एक underscore)।
Bedroom के अंदर आपकी बंद तिजोरी — passport, bank cards, diary। घर के नियम तिजोरी का नाम ही बदल देते हैं ताकि कोई ग़लती से न पाए। तकनीकी रूप से आप फिर भी उसे खोल सकते हैं, पर साफ़ है कि आपने ख़ास कोशिश की। ये private attributes हैं (दो underscores, name-mangled)।
Python में कोई सख़्त
private keyword नहीं है। वह इरादा दिखाने के लिए naming conventions और एक छोटा-सा जादू (name mangling) इस्तेमाल करता है: "public — ले लीजिए", "protected — जब तक पता न हो, न छुएँ", और "private — सच में, यह आपके लिए नहीं"।走廊的公告板 — 倒垃圾时间表、WiFi 密码、地址。谁都可以看,也可以改。这些是 公有属性。
挂着"请敲门"牌子的卧室门 — 门没锁。室友 可以 进来,但按约定他们不会。硬要进来,弄坏了东西就是他们的责任。这些是 受保护属性(一个下划线)。
卧室里上锁的保险箱 — 护照、银行卡、日记。房规会主动给保险箱改个名,让人不会无意中找到。技术上 你仍然能打开它,但显然是特意为之。这些是 私有属性(两个下划线,名字改编)。
Python 没有严格的
private 关键字。它用 命名约定 和一点小魔法(名字改编)来表达意图:"公有 — 随便用","受保护 — 不懂就别碰","私有 — 真的,这不是给你的"。Python does not enforce privacy the way Java or C++ does — it signals it. One underscore _x is a polite request. Two underscores __x trigger name mangling, which renames the attribute inside the class so accidental access becomes very unlikely. Together they let you build classes with a clear, safe public API and an inside that outsiders cannot casually touch.
Python privacy को Java या C++ की तरह लागू नहीं करता — वह उसका संकेत देता है। एक underscore _x एक विनम्र अनुरोध है। दो underscores __x name mangling चालू करते हैं, जो class के अंदर attribute का नाम बदल देता है ताकि ग़लती से पहुँचना लगभग नामुमकिन हो जाए। दोनों मिलकर आपको साफ़, सुरक्षित public API वाली classes बनाने देते हैं, जिनके अंदरूनी हिस्से को बाहर वाले यूँ ही नहीं छू सकते।
Python 不像 Java 或 C++ 那样 强制 私有 — 它只是 发出信号。一个下划线 _x 是礼貌的请求。两个下划线 __x 会触发 名字改编(name mangling),在类内部给属性改名,让无意的访问几乎不可能。两者结合,让你写出公有 API 清晰安全、内部不被外人随手触碰的类。
Look for the interactive boxes: the Access Tester (Section 05), the Name Mangler (06), Private Helpers Step by Step (07), the Bank Vault (08) and the Bug Doctor (10). Every result comes from real Python runs.
Interactive डिब्बे खोजें: Access Tester (Section 05), Name Mangler (06), Private Helpers क़दम-दर-क़दम (07), Bank Vault (08) और Bug Doctor (10)। हर नतीजा असली Python runs से है।
请留意这些互动框:访问测试器(第 05 节)、名字改编器(06)、私有辅助方法逐步演示(07)、银行金库(08)和 Bug 医生(10)。每个结果都来自真实的 Python 运行。
The Problem — Python Makes Everything Public by Default 🚪समस्या — Python में डिफ़ॉल्ट रूप से सब Public है 🚪问题 — Python 默认一切都是公有的 🚪
When you write self.balance = 1000 in __init__, any caller can reach into the object and set acc.balance = -99999999. Nothing stops them. That is fine for a quick script, but for a real class it is a disaster.
जब आप __init__ में self.balance = 1000 लिखते हैं, तो कोई भी caller object में पहुँचकर acc.balance = -99999999 कर सकता है। कुछ नहीं रोकता। छोटी script के लिए ठीक है, पर असली class के लिए यह आफ़त है।
当你在 __init__ 里写 self.balance = 1000,任何调用方都能伸手进对象,设置 acc.balance = -99999999。没有东西能阻止。对小脚本无所谓,对真正的类却是灾难。
class BankAccount:
def __init__(self, holder, balance):
self.holder = holder
self.balance = balance # public - anyone can touch it
acc = BankAccount("Alice", 1000)
# Nothing stops this - the whole class contract is gone
acc.balance = -99999999
print(acc.balance) # no validation, no history, no protection
Python's philosophy is "we are all consenting adults". It gives you tools to signal what is part of your public API and what is not. Callers who ignore those signals do so at their own risk. That is usually enough, because in real teams code review catches anyone who reaches into a class's private parts.
Python का दर्शन है "हम सब समझदार वयस्क हैं"। वह आपको यह संकेत देने के औज़ार देता है कि क्या आपकी public API का हिस्सा है और क्या नहीं। जो callers इन संकेतों को अनदेखा करते हैं, वे अपने जोखिम पर करते हैं। आमतौर पर इतना काफ़ी है, क्योंकि असली teams में code review हर उस व्यक्ति को पकड़ लेता है जो class के private हिस्सों में हाथ डालता है।
Python 的哲学是"我们都是成年人"。它给你工具来 标明 哪些属于公有 API、哪些不属于。无视这些信号的调用方后果自负。这通常就够了,因为在真实团队里,代码审查会抓住任何伸手进类私有部分的人。
One Underscore _protected — "Please Knock" 🚪एक Underscore _protected — "कृपया दस्तक दें" 🚪一个下划线 _protected — "请敲门" 🚪
A name that starts with one underscore (self._log, self._retries) tells every reader: "this is internal to the class — don't touch it from outside unless you know what you're doing." Python itself does nothing special: no renaming, no errors. It is a pure convention that the whole community respects.
एक underscore से शुरू होने वाला नाम (self._log, self._retries) हर पढ़ने वाले को बताता है: "यह class का अंदरूनी है — जब तक पता न हो, बाहर से न छुएँ।" Python ख़ुद कुछ ख़ास नहीं करता: न नाम बदलना, न errors। यह शुद्ध परंपरा है जिसे पूरा community मानता है।
以 一个下划线 开头的名字(self._log、self._retries)告诉每位读者:"这是类的内部细节 — 除非你清楚自己在做什么,否则别从外部碰它。"Python 本身不做任何特殊处理:不改名,不报错。这纯粹是整个社区都遵守的约定。
class BankAccount:
def __init__(self, holder, balance):
self.holder = holder # public - okay to read/write
self._log = [] # protected - internal, but subclasses may extend it
self._version = 1 # protected - internal bookkeeping
acc = BankAccount("Alice", 1000)
# You CAN still touch _log from outside - Python doesn't stop you...
print(acc._log)
print(acc.__dict__) # no renaming: the names are stored as written
# ...but every Python developer reads "_log" as "internal - don't touch."
Use _name for attributes and methods that are internal, but that subclasses may still need. Common examples: _log, _cache, _state, _normalize(). One underscore says "not part of my public API, but not sealed off from my family either".
_name उन attributes और methods के लिए इस्तेमाल करें जो अंदरूनी हैं, पर जिनकी subclasses को फिर भी ज़रूरत हो सकती है। आम उदाहरण: _log, _cache, _state, _normalize()। एक underscore कहता है "मेरी public API का हिस्सा नहीं, पर मेरे परिवार से बंद भी नहीं"।
_name 用于 内部 但 子类可能仍需要 的属性和方法。常见例子:_log、_cache、_state、_normalize()。单下划线的意思是"不属于我的公有 API,但也不对我的家族封闭"。
Two Underscores __private — Name Mangling Locks the Safe 🔒दो Underscores __private — Name Mangling तिजोरी बंद करता है 🔒两个下划线 __private — 名字改编锁上保险箱 🔒
A name that starts with two underscores (and does not end with two) triggers Python's name mangling. Inside the class, Python quietly renames self.__balance to self._BankAccount__balance. Anyone who tries the obvious acc.__balance from outside gets an AttributeError.
दो underscores से शुरू होने वाला नाम (जो दो पर ख़त्म न हो) Python की name mangling चालू करता है। Class के अंदर Python चुपचाप self.__balance का नाम self._BankAccount__balance कर देता है। बाहर से सीधा acc.__balance आज़माने वाले को AttributeError मिलता है।
以 两个下划线 开头(且不以两个下划线结尾)的名字会触发 Python 的 名字改编。在类内部,Python 悄悄把 self.__balance 改名为 self._BankAccount__balance。任何人从外部直接尝试 acc.__balance 都会得到 AttributeError。
class BankAccount:
def __init__(self, holder, balance):
self.holder = holder
self.__balance = balance # private - Python will rename it
self.__pin = None # private - this must never leak
def show(self):
# Inside the class, __balance works as expected
print(f"{self.holder}: ${self.__balance}")
acc = BankAccount("Alice", 1000)
acc.show()
# -- The obvious access from outside FAILS --
try:
print(acc.__balance)
except AttributeError as e:
print("Blocked:", e)
# -- What Python actually did: it renamed the attribute --
print(acc.__dict__)
# -- You COULD still reach it by the mangled name, but everyone knows you are cheating --
print(acc._BankAccount__balance)
Name mangling stops accidental access — the "oops, I typed acc.__balance" mistake. It does not stop someone determined. Anyone can still write acc._BankAccount__balance. In Python, private means "actively discouraged", not "sealed". Never use it as a security boundary.
Name mangling ग़लती से होने वाली पहुँच रोकता है — "अरे, मैंने acc.__balance लिख दिया" वाली ग़लती। यह ठान चुके व्यक्ति को नहीं रोकता। कोई भी acc._BankAccount__balance लिख सकता है। Python में private का मतलब है "सख़्ती से हतोत्साहित", "सील बंद" नहीं। इसे कभी security की दीवार न मानें।
名字改编只能阻止 无意 的访问 — 那种"哎呀,我打了 acc.__balance"的失误。它 挡不住 有意为之的人。谁都可以写 acc._BankAccount__balance。在 Python 里,私有意味着"强烈不鼓励",而不是"密封"。永远不要把它当作安全边界。
Public vs Protected vs Private — Three Access Levels 🚦Public बनाम Protected बनाम Private — तीन Access Levels 🚦公有、受保护与私有 — 三种访问级别 🚦
Three attributes on one BankAccount. Watch what happens when outside code tries each of them: public works at once, protected works with a "you shouldn't" warning, private fails with an AttributeError.
एक BankAccount पर तीन attributes। देखें कि बाहर का code हर एक को आज़माता है तो क्या होता है: public तुरंत चलता है, protected "नहीं करना चाहिए" की चेतावनी के साथ चलता है, private AttributeError से fail होता है।
一个 BankAccount 上有三个属性。看看外部代码逐个尝试时会发生什么:公有立即可用,受保护的能用但带着"不该这样"的警告,私有的直接 AttributeError。
Public is open, protected is only a signal, private is truly harder to reach because Python renamed it.
Public खुला है, protected सिर्फ़ संकेत है, private तक पहुँचना सच में मुश्किल है क्योंकि Python ने उसका नाम बदल दिया।
公有是开放的,受保护只是信号,私有真的更难访问,因为 Python 给它改了名。
🚦 Simulator: Access Tester🚦 Simulator: Access Tester🚦 模拟器:访问测试器
Try 11 expressions on acc = BankAccount("Alice", 1000, "1234") — the class from Section 08. The matching zone of the object lights up, and you see the real result or error.
acc = BankAccount("Alice", 1000, "1234") पर 11 expressions आज़माएँ — Section 08 की class। Object का मिलता-जुलता हिस्सा चमकता है, और आपको असली नतीजा या error दिखता है।
在 acc = BankAccount("Alice", 1000, "1234")(第 08 节的类)上尝试 11 个表达式。对象中对应的区域会亮起,并显示真实的结果或错误。
holder_log__balance · __pin · __verify()deposit() · withdraw() · get_balance()How Name Mangling Works 🔤Name Mangling कैसे काम करता है 🔤名字改编是怎样工作的 🔤
When Python compiles the class body, any name that starts with __ (and does not end with __) is rewritten. The rule is simple: add _ClassName in front. Inside the class, self.__balance and the mangled name are the same thing. From outside, only the mangled name works.
जब Python class body को compile करता है, तो हर वह नाम जो __ से शुरू हो (और __ पर ख़त्म न हो) दोबारा लिखा जाता है। नियम आसान है: आगे _ClassName जोड़ दो। Class के अंदर self.__balance और mangled नाम एक ही चीज़ हैं। बाहर से सिर्फ़ mangled नाम चलता है।
Python 编译类体时,任何以 __ 开头(且不以 __ 结尾)的名字都会被改写。规则很简单:在前面加上 _ClassName。在类内部,self.__balance 和改编后的名字是同一个东西;在外部,只有改编后的名字才有效。
The rule is fixed: __name in class C always becomes _C__name. Once you know it, there is no mystery — just a strong nudge to leave it alone.
नियम तय है: class C में __name हमेशा _C__name बनता है। जान लेने के बाद कोई रहस्य नहीं — बस छोड़ देने का ज़ोरदार इशारा।
规则是固定的:类 C 中的 __name 总是变成 _C__name。知道规则后就没有神秘感 — 只是一个"别碰"的强烈提示。
Names with two underscores on both ends — __init__, __str__, __len__ — are called dunders ("double underscore"). They are not mangled and not private. Python reserves them for special methods. Only names that start with __ and do not end with __ are mangled.
दोनों तरफ़ दो underscores वाले नाम — __init__, __str__, __len__ — dunders ("double underscore") कहलाते हैं। ये mangle नहीं होते और private नहीं हैं। Python इन्हें ख़ास methods के लिए रखता है। सिर्फ़ वे नाम mangle होते हैं जो __ से शुरू हों और __ पर ख़त्म न हों।
两端 都有双下划线的名字 — __init__、__str__、__len__ — 叫作 dunder(double underscore)。它们 不会 被改编,也 不是 私有的。Python 把它们留给特殊方法。只有以 __ 开头且不以 __ 结尾的名字才会被改编。
class Report:
def __init__(self):
self.__init_done = True # starts with __ but does not end with __ -> mangled
def __str__(self): # dunder: begins AND ends with __ -> NOT mangled
return "Report object"
r = Report()
print(r.__dict__)
print(str(r), r.__str__()) # dunders are normal, public names
🔤 Simulator: Name Mangler🔤 Simulator: Name Mangler🔤 模拟器:名字改编器
Type any class name and attribute name (or click a preset). The checker walks through Python's rule and shows the stored name, plus what happens from outside. It matched real Python on all 45 preset combinations — including odd cases like a class named ___.
कोई भी class नाम और attribute नाम लिखें (या preset दबाएँ)। Checker Python के नियम को क़दम-दर-क़दम चलाकर रखा गया नाम दिखाता है, और बाहर से क्या होता है वह भी। यह सभी 45 preset combinations पर असली Python से मिला — ___ नाम की class जैसे अजीब cases भी।
输入任意类名和属性名(或点击预设)。检查器按 Python 的规则一步步走,显示存储的名字,以及从外部访问时会怎样。它在全部 45 个预设组合上与真实 Python 一致 — 包括类名为 ___ 这样的特殊情况。
Private Methods — Helpers That Should Never Be Called From Outside 🧰Private Methods — ऐसे Helpers जिन्हें बाहर से कभी न बुलाया जाए 🧰私有方法 — 不应从外部调用的辅助方法 🧰
Everything about private variables applies to private methods too. A method whose name starts with __ is mangled exactly like an attribute. Use this to hide implementation details — checking, formatting, internal setup — that callers should never care about.
Private variables की हर बात private methods पर भी लागू होती है। __ से शुरू होने वाले method का नाम ठीक attribute की तरह mangle होता है। इसका इस्तेमाल उन अंदरूनी बातों को छिपाने में करें — जाँच, formatting, अंदरूनी setup — जिनसे callers को कोई मतलब नहीं होना चाहिए।
关于私有 变量 的一切同样适用于私有 方法。名字以 __ 开头的方法会像属性一样被改编。用它来隐藏实现细节 — 检查、格式化、内部设置 — 这些都是调用方不该关心的。
class User:
def __init__(self, username, password):
self.username = username
self.__password_hash = self.__hash(password) # uses a private method
# -- Public API -------------------------------------
def check_password(self, guess):
return self.__password_hash == self.__hash(guess)
# -- Private helper - never called from outside -----
def __hash(self, s):
# In real code you would use a proper hashing library. Toy here.
return hex(hash(s))
alice = User("alice", "secret")
print(alice.check_password("secret"))
print(alice.check_password("guess"))
# Calling the private helper from outside -> AttributeError
try:
alice.__hash("anything")
except AttributeError as e:
print("Blocked:", e)
print("_User__hash" in dir(User)) # the method was renamed too
The output of hash() changes between runs, but the True / False results do not — the same text always matches itself within one run.
hash() का output हर run में बदलता है, पर True / False नतीजे नहीं बदलते — एक run के अंदर वही text हमेशा ख़ुद से मेल खाता है।
hash() 的输出每次运行都会变,但 True / False 的结果不变 — 在同一次运行中,相同的文本总是与自己匹配。
Callers see one clean entrance. Behind it, private helpers do the real work — verify, check, log. The caller never sees them and never depends on them.
Callers को एक साफ़ दरवाज़ा दिखता है। उसके पीछे private helpers असली काम करते हैं — verify, check, log। Caller उन्हें कभी नहीं देखता, उन पर निर्भर नहीं रहता।
调用方只看到一个干净的入口。入口后面,私有辅助方法做实际工作 — 验证、检查、记录。调用方看不到它们,也不依赖它们。
🧰 Simulator: Private Helpers Step by Step🧰 Simulator: Private Helpers क़दम-दर-क़दम🧰 模拟器:私有辅助方法逐步演示
Step through acc.withdraw(200, pin). Watch the public method open frames for the private helpers on the call stack, and watch the object's __dict__ hold the mangled names. Switch to the wrong PIN to see __verify() stop everything. Recorded with sys.settrace.
acc.withdraw(200, pin) को step करें। देखें कि public method call stack पर private helpers के frames खोलता है, और object का __dict__ mangled नाम रखता है। ग़लत PIN चुनें और देखें कि __verify() सब रोक देता है। sys.settrace से रिकॉर्ड।
逐步运行 acc.withdraw(200, pin)。看公有方法在调用栈上为私有辅助方法打开帧,再看对象的 __dict__ 保存的是 改编后 的名字。切换到错误的 PIN,看 __verify() 如何拦下一切。由 sys.settrace 记录。
Practical Example — BankAccount With a Private Balance and PIN 🏦व्यावहारिक उदाहरण — Private Balance और PIN वाला BankAccount 🏦实例 — 带私有余额和 PIN 的 BankAccount 🏦
A realistic small class that uses all three levels together: a public API, protected logging that subclasses may extend, and private state (balance, PIN) that only the class itself touches, through private helper methods.
एक छोटी, असली-जैसी class जो तीनों स्तर एक साथ इस्तेमाल करती है: public API, protected logging जिसे subclasses बढ़ा सकती हैं, और private state (balance, PIN) जिसे सिर्फ़ class ख़ुद, private helper methods के ज़रिए, छूती है।
一个贴近现实的小类,同时使用三种级别:公有 API、子类可扩展的受保护日志,以及只有类自己通过私有辅助方法才能触碰的私有状态(余额、PIN)。
class BankAccount:
def __init__(self, holder, opening_balance, pin):
if not BankAccount.__is_valid_pin(pin):
raise ValueError("PIN must be 4 digits")
self.holder = holder # public - the customer's name
self._log = [] # protected - subclasses may extend
self.__balance = opening_balance # private - the whole point of a bank
self.__pin = pin # private - should NEVER leak
# == PUBLIC API - the only entry points ==
def deposit(self, amount, pin):
self.__verify(pin)
if amount <= 0:
raise ValueError("Amount must be positive")
self.__balance += amount
self.__log_txn("deposit", amount)
def withdraw(self, amount, pin):
self.__verify(pin)
if amount > self.__balance:
raise ValueError("Insufficient funds")
self.__balance -= amount
self.__log_txn("withdraw", amount)
def get_balance(self, pin):
# Even reading the balance requires the PIN
self.__verify(pin)
return self.__balance
# == PRIVATE HELPERS - hidden implementation details ==
def __verify(self, pin):
if pin != self.__pin:
raise PermissionError("Wrong PIN")
def __log_txn(self, action, amount):
self._log.append((action, amount))
@staticmethod
def __is_valid_pin(pin):
return isinstance(pin, str) and len(pin) == 4 and pin.isdigit()
# -- Normal use - everything goes through the public API --
acc = BankAccount("Alice", opening_balance=1000, pin="1234")
acc.deposit(500, pin="1234")
acc.withdraw(200, pin="1234")
print("Balance:", acc.get_balance(pin="1234"))
print("Log: ", acc._log) # protected - visible but internal
# -- Attempts to bypass the API --
try: acc.withdraw(100, pin="9999")
except PermissionError as e: print("Blocked:", e) # wrong PIN
try: print(acc.__balance)
except AttributeError as e: print("Blocked:", e) # direct read fails
try: acc.__verify("9999")
except AttributeError as e: print("Blocked:", e) # the private helper cannot be called
try: BankAccount("Bob", 50, pin="12")
except ValueError as e: print("Blocked:", e) # the private static check ran in __init__
The last line shows one more private helper at work: the static method __is_valid_pin (also mangled) rejects a 2-digit PIN inside __init__.
आख़िरी line एक और private helper को काम करते दिखाती है: static method __is_valid_pin (यह भी mangled) __init__ के अंदर 2 अंकों वाला PIN ठुकरा देता है।
最后一行展示了另一个私有辅助方法:静态方法 __is_valid_pin(同样被改编)在 __init__ 中拒绝了 2 位数的 PIN。
Nobody sets the balance by accident — every change goes through deposit or withdraw, which check the PIN and the amount. Nobody reads the balance without the PIN. Nobody calls __verify by its plain name. The class shows a small, safe surface, and everything else is renamed out of the way. That is encapsulation in Python — as long as nobody deliberately uses the mangled names.
कोई भी balance ग़लती से नहीं बदलता — हर बदलाव deposit या withdraw से होता है, जो PIN और amount जाँचते हैं। PIN के बिना कोई balance नहीं पढ़ता। कोई __verify को सादे नाम से नहीं बुलाता। Class एक छोटी, सुरक्षित सतह दिखाती है, और बाक़ी सब नाम बदलकर रास्ते से हटा दिया गया है। Python में यही encapsulation है — जब तक कोई जान-बूझकर mangled नाम इस्तेमाल न करे।
没有人会无意中改动余额 — 每次修改都经过 deposit 或 withdraw,它们会检查 PIN 和金额。没有 PIN 就读不到余额。没有人能用原名调用 __verify。类只暴露一个小而安全的表面,其余一切都被改名挪开。这就是 Python 中的 封装 — 前提是没人故意使用改编后的名字。
🏦 Simulator: Bank Vault🏦 Simulator: Bank Vault🏦 模拟器:银行金库
Use the public API with the right or wrong PIN. Then try the three tricks: write acc.__balance from outside, read it back, and cheat with the mangled name. The live __dict__ shows what really changes. Checked against the real class on 300 random sequences.
सही या ग़लत PIN के साथ public API इस्तेमाल करें। फिर तीन चालें आज़माएँ: बाहर से acc.__balance लिखें, उसे वापस पढ़ें, और mangled नाम से धोखा दें। Live __dict__ दिखाता है कि असल में क्या बदलता है। असली class से 300 random sequences पर जाँचा गया।
用正确或错误的 PIN 调用公有 API。然后试试三个把戏:从外部写 acc.__balance、再读回来、以及用改编后的名字作弊。实时的 __dict__ 显示真正改变了什么。已在 300 个随机序列上与真实类核对。
Public API vs Private Implementation — The Contract 🤝Public API बनाम Private Implementation — अनुबंध 🤝公有 API 与私有实现 — 契约 🤝
Named with __x (two leading underscores)__x नाम (आगे दो underscores)以 __x 命名(前面两个下划线) |
Python renames them → _ClassName__xPython नाम बदलता है → _ClassName__xPython 改名为 → _ClassName__x |
| You are free to change them any timeआप इन्हें कभी भी बदल सकते हैं你可以 随时修改 它们 |
| Callers must not depend on themCallers को इन पर निर्भर नहीं होना चाहिए调用方不应依赖它们 |
Examples: __balance, __pin, __hash()उदाहरण: __balance, __pin, __hash()例如:__balance、__pin、__hash() |
No prefix (deposit, get_balance)कोई prefix नहीं (deposit, get_balance)无前缀(deposit、get_balance) |
| Callable from anywhere — that is the pointकहीं से भी बुला सकते हैं — यही मक़सद है任何地方都能调用 — 这正是目的 |
| You promise not to break them without a new versionआप वादा करते हैं कि इन्हें नहीं तोड़ेंगे (नए version के बिना)你 承诺不会破坏它们(除非发布新版本) |
| Every caller safely depends on themहर caller इन पर सुरक्षित रूप से निर्भर है每个调用方都可以放心依赖 |
| Change them → everyone who uses them must updateइन्हें बदलें → इस्तेमाल करने वाले सबको update करना होगा修改它们 → 所有使用者都得跟着更新 |
The whole point of private members is freedom. Once you publish a public method, changing its signature breaks every caller. But you can swap the private implementation any time — add caching, change the algorithm, split one helper into three — as long as the public API behaves the same. Private members are the space where you can refactor without fear.
Private members का पूरा मक़सद आज़ादी है। एक बार public method जारी कर दिया, तो उसका signature बदलने से हर caller टूटता है। पर private implementation आप कभी भी बदल सकते हैं — caching जोड़ें, algorithm बदलें, एक helper को तीन में बाँटें — बस public API वैसा ही व्यवहार करे। Private members वह जगह हैं जहाँ आप बिना डर refactor कर सकते हैं।
私有成员的全部意义在于 自由。公有方法一旦发布,改变它的签名就会破坏所有调用方。但私有实现可以随时更换 — 加缓存、换算法、把一个辅助方法拆成三个 — 只要公有 API 的行为不变。私有成员就是你可以放心重构的空间。
Common Mistakes (and Fixes) 🩹आम ग़लतियाँ (और उनके हल) 🩹常见错误(及修正)🩹
| Mistakeग़लती错误 | What goes wrongक्या बिगड़ता है会出什么问题 | Fixहल修正 |
|---|---|---|
| Reading a private attribute from outside by its mangled nameबाहर से mangled नाम से private attribute पढ़ना从外部用改编名读取私有属性 | acc._BankAccount__balance works, but ties your code to the class nameacc._BankAccount__balance चलता है, पर आपका code class के नाम से बँध जाता हैacc._BankAccount__balance 能用,但让你的代码绑死在类名上 | Don't. Add a public getter method insteadऐसा न करें। इसकी जगह public getter method जोड़ें不要这样做。改为添加公有 getter 方法 |
Using __name when you mean "internal, but subclasses too""अंदरूनी, पर subclasses के लिए भी" के लिए __name इस्तेमाल करना想要"内部但子类也能用"却用了 __name | Subclasses cannot easily read or change it — mangling gives each class its own nameSubclasses आसानी से पढ़ या बदल नहीं सकतीं — mangling हर class को अलग नाम देता है子类无法轻松读取或修改 — 改编让每个类有各自的名字 | Use _name (one underscore)_name (एक underscore) इस्तेमाल करें使用 _name(一个下划线) |
| Treating private as securityPrivate को security मानना把私有当作安全措施 | Anyone can still read _ClassName__xकोई भी _ClassName__x पढ़ सकता है任何人都能读取 _ClassName__x | Don't keep secrets in an attribute expecting them to be hiddenछिपे रहने की उम्मीद में secrets को attribute में न रखें不要指望属性能把秘密藏起来 |
Naming things __init or __strनाम __init या __str रखना起名为 __init 或 __str | Confusing — looks like a dunder, but is still mangledभ्रम — dunder जैसा दिखता है, पर फिर भी mangle होता है容易混淆 — 看着像 dunder,但仍会被改编 | Use a clear name like _init_helper or __init_helper_init_helper या __init_helper जैसा साफ़ नाम रखें用清晰的名字,如 _init_helper 或 __init_helper |
obj.__attr = value from outsideबाहर से obj.__attr = value在外部写 obj.__attr = value | Makes a new attribute named literally __attr (no mangling outside the class)__attr नाम का नया attribute बनता है (class के बाहर mangling नहीं)新建一个名字就叫 __attr 的属性(类外不改编) | Never write double-underscore names from outsideबाहर से दो underscore वाले नाम कभी न लिखें永远不要在外部写双下划线名字 |
| Using two underscores everywhereहर जगह दो underscores到处都用双下划线 | Subclasses cannot take part; mangled names clutter __dict__Subclasses हिस्सा नहीं ले पातीं; mangled नाम __dict__ भर देते हैं子类无法参与;改编名让 __dict__ 杂乱 | Default to _x; keep __x for truly private stateडिफ़ॉल्ट _x; __x सिर्फ़ सच में private state के लिए默认用 _x;__x 留给真正私有的状态 |
| Making everything private "just in case""बस यूँ ही" सब private बनाना"以防万一"把一切都设为私有 | The class becomes hard to extend and to testClass को बढ़ाना और test करना मुश्किल हो जाता है类变得难以扩展和测试 | Start with a small public API; make things private only for a reasonछोटी public API से शुरू करें; किसी वजह से ही private बनाएँ从小的公有 API 开始;有理由时才设为私有 |
The obj.__attr = value trap, run for real:
obj.__attr = value वाला जाल, असली run में:
obj.__attr = value 陷阱的真实运行:
class BankAccount:
def __init__(self, balance):
self.__balance = balance
acc = BankAccount(1000)
acc.__balance = 0 # outside the class: NO mangling happens here
print(acc.__dict__) # a brand-new attribute named literally "__balance"
print(acc._BankAccount__balance) # the real balance is untouched
🩺 Simulator: Bug Doctor🩺 Simulator: Bug Doctor🩺 模拟器:Bug 医生
Four of these mistakes, run for real. Buggy code on the left, fixed code on the right, changed lines marked. Two of them give no error — just a wrong or unsafe result.
इनमें से चार ग़लतियाँ, असली run में। बाईं ओर buggy code, दाईं ओर ठीक किया हुआ, बदली lines चिह्नित। इनमें से दो कोई error नहीं देतीं — बस ग़लत या असुरक्षित नतीजा।
其中四个错误的真实运行。左边是有 bug 的代码,右边是修正后的代码,改动的行已标出。其中两个 不报错 — 只是结果错误或不安全。
Quick Reference 📋Quick Reference 📋速查表 📋
| PrefixPrefix前缀 | Calledनाम称呼 | What Python doesPython क्या करता हैPython 做什么 | When to useकब इस्तेमाल करें何时使用 |
|---|---|---|---|
x | public | Nothing — fully accessibleकुछ नहीं — पूरी तरह उपलब्ध什么都不做 — 完全可访问 | The class's official APIClass की official API类的正式 API |
_x | protected (convention)protected (परंपरा)受保护(约定) | Nothing — a signal to peopleकुछ नहीं — लोगों के लिए संकेत什么都不做 — 给人看的信号 | Internal, but subclasses may use itअंदरूनी, पर subclasses इस्तेमाल कर सकती हैं内部使用,但子类可以用 |
__x | private (name-mangled)private (name-mangled)私有(名字改编) | Renames to _ClassName__x_ClassName__x नाम कर देता है改名为 _ClassName__x | Truly internal — nobody else touches itसच में अंदरूनी — कोई और नहीं छूता真正内部 — 别人不碰 |
__x__ | dunder (special)dunder (ख़ास)dunder(特殊) | Nothing — reserved for special methodsकुछ नहीं — ख़ास methods के लिए आरक्षित什么都不做 — 留给特殊方法 | Only for Python's own special methodsसिर्फ़ Python के अपने ख़ास methods के लिए只用于 Python 自己的特殊方法 |
_x_ | unusual — avoidअसामान्य — बचें少见 — 避免 | Nothing specialकुछ ख़ास नहीं没有特殊处理 | No convention — don't invent oneकोई परंपरा नहीं — नई न बनाएँ没有约定 — 别自创 |
| Taskकाम任务 | SyntaxSyntax语法 | Notesनोट说明 |
|---|---|---|
| Declare a private attributePrivate attribute declare करना声明私有属性 | self.__balance = 1000 | Only inside the classसिर्फ़ class के अंदर只在类内部 |
| Declare a private methodPrivate method declare करना声明私有方法 | def __verify(self, pin): | Called as self.__verify(...) insideअंदर self.__verify(...) से बुलाएँ在内部用 self.__verify(...) 调用 |
| See the mangled namesMangled नाम देखना查看改编后的名字 | obj.__dict__ | Shows _ClassName__x keys_ClassName__x keys दिखाता है显示 _ClassName__x 键 |
| Reach a private (don't)Private तक पहुँचना (न करें)访问私有成员(别这样做) | obj._ClassName__x | Cheating — reject it in code reviewधोखा — code review में ठुकराएँ作弊 — 代码审查时拒绝 |
| Provide a public getterPublic getter देना提供公有 getter | def get_x(self): return self.__x | The right way to expose a private valuePrivate value दिखाने का सही तरीक़ा暴露私有值的正确方式 |
| Modern getter/setterआधुनिक getter/setter现代 getter/setter | @property | Preferred over get/set methods — covered in an upcoming tutorialget/set methods से बेहतर — आने वाले tutorial में优于 get/set 方法 — 将在后续教程讲解 |
Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆
private keyword. It has naming conventions and name mangling. Both are signals, not security. Anyone determined can still reach in.private keyword नहीं है। इसमें naming conventions और name mangling हैं। दोनों संकेत हैं, security नहीं। ठान चुका कोई भी अंदर पहुँच सकता है।private 关键字。它只有命名约定和名字改编。两者都是信号,不是安全措施。有心人照样能伸手进去。_x is a polite convention — "internal, don't touch unless you know what you're doing". Python does not enforce it. Every Python developer respects it._x एक विनम्र परंपरा है — "अंदरूनी, जब तक पता न हो, न छुएँ"। Python इसे लागू नहीं करता। हर Python developer इसे मानता है।_x 是礼貌的约定 — "内部使用,不懂别碰"。Python 不强制,但每个 Python 开发者都会遵守。__x trigger name mangling: Python renames it to _ClassName__x. Accidental access from outside fails with AttributeError. Deliberate access still works, but it shouts "I am cheating".__x name mangling चालू करते हैं: Python नाम _ClassName__x कर देता है। बाहर से ग़लती से पहुँचना AttributeError देता है। जान-बूझकर पहुँचना फिर भी चलता है, पर चिल्लाकर कहता है "मैं धोखा दे रहा हूँ"।__x 触发名字改编:Python 把它改名为 _ClassName__x。从外部无意访问会得到 AttributeError。故意访问仍然可行,但等于大喊"我在作弊"。__init__, __str__) are not private. They are Python's special (dunder) names. Never invent your own __myname__.__init__, __str__) private नहीं हैं। ये Python के ख़ास (dunder) नाम हैं। अपना __myname__ कभी न बनाएँ।__init__、__str__)不是私有的。它们是 Python 的特殊(dunder)名字。永远不要自创 __myname__。_x. Use __x only for a specific reason — usually sensitive state (a PIN, a token) or a name that must not clash with a subclass's attribute of the same name._x रखें। __x किसी ख़ास वजह से ही लें — आमतौर पर संवेदनशील state (PIN, token) या ऐसा नाम जो subclass के उसी नाम वाले attribute से न टकराए।_x。只在有具体理由时才用 __x — 通常是敏感状态(PIN、令牌),或必须与子类同名属性避免冲突的名字。__verify), formatting (__hash), internal bookkeeping (__log_txn). Keep them small; they are the ingredients of your public methods.__verify), formatting (__hash), अंदरूनी हिसाब (__log_txn)। इन्हें छोटा रखें; ये आपके public methods की सामग्री हैं।__verify)、格式化(__hash)、内部记账(__log_txn)。保持它们小巧;它们是公有方法的原料。__password.__password नहीं।__password。__x). Subclasses may need it → protected (_x). Callers depend on it → public. Match the name to your promise.__x)। Subclasses को चाहिए → protected (_x)। Callers निर्भर हैं → public। नाम को अपने वादे से मिलाएँ।__x)。子类可能需要 → 受保护(_x)。调用方依赖它 → 公有。让名字与你的承诺相符。Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题
No prefix = public API. One underscore = "internal, please knock" — only a convention. Two underscores = name mangling: inside class C, __x is stored as _C__x, so the plain name fails from outside. Dunders like __init__ are never mangled. None of this is security — it is a clear signal. Give callers a small public API, keep helpers private, and change private data only through methods.
कोई prefix नहीं = public API। एक underscore = "अंदरूनी, कृपया दस्तक दें" — सिर्फ़ परंपरा। दो underscores = name mangling: class C के अंदर __x _C__x नाम से रखा जाता है, इसलिए बाहर से सादा नाम fail होता है। __init__ जैसे dunders कभी mangle नहीं होते। इनमें से कुछ भी security नहीं — यह एक साफ़ संकेत है। Callers को छोटी public API दें, helpers को private रखें, और private data को सिर्फ़ methods से बदलें।
无前缀 = 公有 API。单下划线 = "内部使用,请敲门" — 只是约定。双下划线 = 名字改编:在类 C 中,__x 存为 _C__x,所以原名在外部会失败。像 __init__ 这样的 dunder 从不改编。这些都不是安全措施 — 而是清晰的信号。给调用方一个小而公有的 API,把辅助方法设为私有,只通过方法修改私有数据。