The Story That Explains Classes and Objects 🍪Classes और Objects समझाने वाली कहानी 🍪一个讲清类与对象的故事 🍪
Every cookie has the same shape. But each one is a separate cookie: this one has extra sprinkles, that one has chocolate chips, the third one is a little overbaked. Eating one cookie does not change the others.
The cutter is the class. It defines the shape. Each cookie is an object. You write the class once, then create as many objects as you need — each with its own data.
हर cookie का आकार एक जैसा है। पर हर cookie अलग है: इस पर ज़्यादा sprinkles हैं, उसमें chocolate chips हैं, तीसरी थोड़ी ज़्यादा सिक गई है। एक cookie खाने से बाक़ी नहीं बदलतीं।
Cutter class है। वह आकार तय करता है। हर cookie एक object है। आप class एक बार लिखते हैं, फिर जितने चाहें objects बनाते हैं — हर एक का अपना data।
每块饼干的 形状 都一样。但每块都是独立的:这块多撒了糖粒,那块有巧克力豆,第三块烤得有点过。吃掉一块,不会影响其他饼干。
模具就是 类(class),它定义形状。每块饼干是一个 对象(object)。类只写 一次,然后按需创建任意多个对象 — 每个都有自己的数据。
A class is a blueprint. An object is one real thing built from that blueprint. The blueprint is written once; the objects hold the actual data.
Class एक blueprint (नक़्शा) है। Object उस नक़्शे से बनी एक असली चीज़ है। नक़्शा एक बार लिखा जाता है; असली data objects रखते हैं।
类是一张 蓝图。对象是按蓝图造出来的一个真实的东西。蓝图只写一次;真正的数据保存在对象里。
Look for the interactive boxes: the Object Factory (Section 04), __init__ Step by Step (06), Who Is self? (07), Local vs Instance (10), the Grade Card (12) and the Bank Account Lab (13). Every number in them comes from a real Python run.
Interactive डिब्बे खोजें: Object Factory (Section 04), __init__ क़दम-दर-क़दम (06), self कौन है? (07), Local बनाम Instance (10), Grade Card (12) और Bank Account Lab (13)। इनका हर नंबर असली Python run से आया है।
请留意这些互动框:对象工厂(第 04 节)、__init__ 逐步运行(06)、self 是谁?(07)、局部变量与实例变量(10)、成绩卡(12)和 银行账户实验室(13)。里面的每个数字都来自真实的 Python 运行。
Class vs Object — The Basic Difference 📐Class बनाम Object — बुनियादी फ़र्क़ 📐类与对象 — 根本区别 📐
Defined once, with the class keywordclass keyword से एक बार define होती है用 class 关键字定义一次 |
| Describes the shape of a thingकिसी चीज़ का आकार बताती है描述事物的 形状 |
| Holds no real dog, student or account dataकोई असली dog, student या account data नहीं रखती不保存任何真实的狗、学生或账户数据 |
| Like a recipe, or a blank formRecipe या ख़ाली form की तरह就像菜谱,或一张空白表格 |
Created by calling the class: Dog()Class को call करके बनता है: Dog()通过 调用 类来创建:Dog() |
| Holds real values in its own memoryअपनी memory में असली values रखता है在自己的内存里保存真实的值 |
| Independent of every other objectहर दूसरे object से स्वतंत्र与其他所有对象相互独立 |
| Like a cooked meal, or a filled-in formपके हुए खाने या भरे हुए form की तरह就像做好的菜,或填好的表格 |
The class only describes the slots. Each object fills them with its own values.
Class सिर्फ़ खाँचे बताती है। हर object उन्हें अपनी values से भरता है।
类只描述有哪些格子。每个对象用自己的值把它们填上。
Your First Class — the Absolute Minimumआपकी पहली Class — बिल्कुल न्यूनतम你的第一个类 — 最简形式
class Dog:
pass # an empty body: no data, no methods, no __init__
d1 = Dog() # object 1 - a bare, empty Dog
d2 = Dog() # object 2 - another bare, empty Dog
print(d1) # the address changes on every run
print(d2)
print(d1 is d2) # two separate objects
print(type(d1), isinstance(d1, Dog))
The number after at is a memory address. It changes on every run, so yours will differ. d1 is d2 is False: two calls, two separate objects. isinstance(d1, Dog) confirms that d1 was built from Dog.
at के बाद का नंबर memory address है। यह हर run पर बदलता है, इसलिए आपका अलग होगा। d1 is d2 False है: दो calls, दो अलग objects। isinstance(d1, Dog) पुष्टि करता है कि d1 Dog से बना है।
at 后面的数字是内存地址,每次运行都会变,所以你看到的会不同。d1 is d2 为 False:调用两次,得到两个独立对象。isinstance(d1, Dog) 确认 d1 是由 Dog 造出来的。
We can already make objects. But they carry nothing yet — no name, no breed, no age. How do we give them data?
हम objects बना तो सकते हैं। पर अभी उनमें कुछ नहीं है — न name, न breed, न age। उन्हें data कैसे दें?
我们已经能创建对象了。但它们现在什么都没有 — 没有名字、品种和年龄。怎样给它们数据呢?
Adding Data From Outside — The Manual Way ✍️बाहर से Data जोड़ना — Manual तरीक़ा ✍️从外部添加数据 — 手动方式 ✍️
Python lets you attach an attribute to almost any object from outside the class. Reach in with a dot and assign: d1.name = "Buddy". These are instance variables — made in the most manual way possible.
Python आपको class के बाहर से लगभग किसी भी object पर attribute जोड़ने देता है। Dot से अंदर पहुँचें और assign करें: d1.name = "Buddy"। ये instance variables हैं — सबसे manual तरीक़े से बने।
Python 允许你从类的外部给几乎任何对象附加属性。用点号伸进去赋值即可:d1.name = "Buddy"。这些就是 实例变量 — 只是用最手动的方式创建的。
class Dog:
pass
d1 = Dog()
d2 = Dog()
d3 = Dog()
d1.name = "Buddy"
d1.breed = "Labrador"
d1.age = 3
d2.name = "Milo"
d2.breed = "Beagle"
d2.age = 5
d3.name = "Luna"
d3.breed = "Poodle"
d3.age = 2
print(d1.name, d1.breed, d1.age)
print(d2.name, d2.breed, d2.age)
print(d3.name, d3.breed, d3.age)
print(d1.__dict__) # every object keeps its data in its own dict
The last line shows where the data really lives: every object keeps its attributes in its own dictionary, __dict__.
आख़िरी line दिखाती है कि data असल में कहाँ रहता है: हर object अपने attributes अपनी dictionary __dict__ में रखता है।
最后一行显示数据真正存放的位置:每个对象把属性保存在自己的字典 __dict__ 里。
Three attributes × three dogs = nine lines. Count them as they arrive. (The original drawing showed 6 lines because it left out age.)
तीन attributes × तीन dogs = नौ lines। आते-आते गिनें। (मूल चित्र में age छूट गया था, इसलिए 6 lines दिखती थीं।)
三个属性 × 三只狗 = 九行。看着它们出现时数一数。(原图漏掉了 age,所以只显示 6 行。)
Why the Manual Way Is Painful 😖Manual तरीक़ा तकलीफ़देह क्यों है 😖为什么手动方式很痛苦 😖
The code above works. Every object ends up with the right data. But this style breaks down as soon as a program has more than a handful of objects. Here is exactly why.
ऊपर का code चलता है। हर object को सही data मिलता है। पर जैसे ही program में कुछ से ज़्यादा objects होते हैं, यह तरीक़ा टूट जाता है। ठीक-ठीक कारण ये हैं।
上面的代码能运行,每个对象都拿到了正确的数据。但只要程序里的对象多于几个,这种写法就撑不住了。具体原因如下。
d2.age = 5 and Python says nothing. Code that reads d2.age later crashes with AttributeError — far from the real mistake.d2.age = 5 छूट जाए तो Python कुछ नहीं कहता। बाद में d2.age पढ़ने वाला code AttributeError से crash होता है — असली ग़लती से बहुत दूर।d2.age = 5,Python 一声不吭。之后读取 d2.age 的代码会抛出 AttributeError 崩溃 — 离真正的错误很远。weight, another does not.weight मिलता है, किसी को नहीं।weight,有的没有。d1.nmae = "Buddy" and Python happily creates a new attribute nmae. Later d1.name fails, even though "you set it".d1.nmae = "Buddy" लिखें और Python ख़ुशी से नया attribute nmae बना देता है। बाद में d1.name fail होता है, जबकि "आपने तो set किया था"।d1.nmae = "Buddy",Python 会高高兴兴地创建新属性 nmae。之后 d1.name 出错,尽管"你明明设置过"。d1.age = -50 or d1.age = "hello". Every caller must remember to check — and they will not.d1.age = -50 या d1.age = "hello" को कुछ नहीं रोकता। हर caller को जाँचना याद रखना होगा — और वे नहीं रखेंगे।d1.age = -50 或 d1.age = "hello"。每个调用者都得记得检查 — 而他们不会。Three of these problems in one real run:
इनमें से तीन समस्याएँ एक असली run में:
一次真实运行里就能看到其中三个问题:
class Dog:
pass
d1 = Dog()
d1.nmae = "Buddy" # typo: Python happily creates a NEW attribute
d1.breed = "Labrador" # ... and we forgot age completely
print(d1.__dict__)
try:
print(d1.name)
except AttributeError as e:
print("AttributeError:", e)
try:
print(d1.age)
except AttributeError as e:
print("AttributeError:", e)
d1.age = "hello" # nothing checks the value either
print(d1.age)
🏭 Simulator: The Object Factory🏭 Simulator: Object Factory🏭 模拟器:对象工厂
Build 1 to 6 dogs the manual way or with __init__. Then set a trap: a typo, a forgotten attribute, or a bad value. Each of the 48 combinations was run in real Python — the objects, the output and the error line are exactly what Python produced.
1 से 6 dogs manual तरीक़े से या __init__ से बनाएँ। फिर एक जाल लगाएँ: typo, भूला हुआ attribute, या ग़लत value। सभी 48 combinations असली Python में चलाए गए — objects, output और error line ठीक वही हैं जो Python ने दिए।
用手动方式或 __init__ 创建 1 到 6 只狗。然后设个陷阱:拼写错误、漏掉属性或坏值。全部 48 种组合都在真实 Python 中运行过 — 对象、输出和出错行都与 Python 的结果完全一致。
The setup is outside the class, so the class itself has no idea what data a real Dog should carry. We need to move that setup inside the class, so it runs automatically every time an object is born. That is what __init__ is for.
Setup class के बाहर है, इसलिए class को ख़ुद नहीं पता कि असली Dog के पास कौन-सा data होना चाहिए। हमें यह setup class के अंदर ले जाना है, ताकि हर नए object के बनते ही यह अपने आप चले। __init__ इसी के लिए है।
设置代码在类的 外部,所以类本身并不知道一只真正的 Dog 应该带哪些数据。我们需要把设置移到类的 内部,让它在每个对象诞生时自动运行。这正是 __init__ 的用途。
The Fix — __init__ Runs the Setup Automatically 🏗️हल — __init__ Setup अपने आप चलाता है 🏗️解决办法 — __init__ 自动完成设置 🏗️
__init__ (say "dunder init" — double underscore) is a special method. Python calls it automatically the moment you create an object. You write the setup once, inside the class, and Python runs it for every new object.
__init__ (बोलें "dunder init" — double underscore) एक ख़ास method है। Object बनाते ही Python इसे अपने आप बुलाता है। आप setup एक बार, class के अंदर लिखते हैं, और Python उसे हर नए object के लिए चलाता है।
__init__(读作 "dunder init",即 double underscore,双下划线)是一个特殊方法。你创建对象的那一刻,Python 会 自动 调用它。设置代码只需在类里写一次,Python 会为每个新对象运行它。
class Dog:
def __init__(self, name, breed, age):
# runs automatically every time you write Dog(...)
self.name = name
self.breed = breed
self.age = age
d1 = Dog("Buddy", "Labrador", 3)
d2 = Dog("Milo", "Beagle", 5)
d3 = Dog("Luna", "Poodle", 2)
print(d1.name, d1.breed, d1.age)
print(d2.name, d2.breed, d2.age)
print(d3.name, d3.breed, d3.age)
try:
Dog("Rex", "Boxer") # forgot age -> an error right away
except TypeError as e:
print("TypeError:", e)
| 3 dogs → 9 setup lines3 dogs → 9 setup lines3 只狗 → 9 行设置 |
| 100 dogs → 300 setup lines100 dogs → 300 setup lines100 只狗 → 300 行设置 |
| A forgotten attribute is a silent bugभूला हुआ attribute एक चुपचाप bug है漏掉属性是无声的 bug |
| Typos create ghost attributesTypos ghost attributes बनाते हैं拼写错误产生幽灵属性 |
| The shape of a Dog is written nowhereDog का आकार कहीं नहीं लिखाDog 的结构哪里都没写 |
| No place to check incoming valuesआने वाली values जाँचने की कोई जगह नहीं没有地方检查传入的值 |
| 3 dogs → 3 setup lines3 dogs → 3 setup lines3 只狗 → 3 行设置 |
| 100 dogs → 100 setup lines100 dogs → 100 setup lines100 只狗 → 100 行设置 |
A missing argument → TypeError at onceग़ायब argument → तुरंत TypeError缺少参数 → 立即 TypeError |
| Attribute names live in one placeAttribute के नाम एक ही जगह属性名只在一个地方 |
The shape of a Dog is documented in __init__Dog का आकार __init__ में लिखा हैDog 的结构写在 __init__ 里 |
Validation lives inside __init__जाँच __init__ के अंदर रहती है校验放在 __init__ 里 |
The arguments fly into their self. slots. No line outside the class wires them up.
Arguments अपने self. खाँचों में उड़कर पहुँचते हैं। Class के बाहर कोई line उन्हें नहीं जोड़ती।
参数飞进各自的 self. 格子里。类外没有任何一行代码去连接它们。
It removes repetition, gives every object the same shape, keeps the definition of the data in one central place, and gives you one place to check incoming values. Almost every real Python class you write will start with __init__ for exactly these reasons.
यह दोहराव हटाता है, हर object को एक जैसा आकार देता है, data की परिभाषा एक केंद्रीय जगह रखता है, और आने वाली values जाँचने की एक जगह देता है। इन्हीं कारणों से आपकी लिखी लगभग हर असली Python class __init__ से शुरू होगी।
它消除重复,保证每个对象结构相同,把数据定义集中在一个地方,并提供一个检查传入值的位置。正因如此,你写的几乎每个真实的 Python 类都会以 __init__ 开头。
Anatomy of __init__ 🔬__init__ की बनावट 🔬__init__ 的剖析 🔬
Student.
Python Student type का एक बिल्कुल नया, ख़ाली object बनाता है।
Python 创建一个全新的、空的 Student 类型对象。
Student.__init__(new_object, "Alice", 20, "Physics") for you.
Python आपके लिए Student.__init__(new_object, "Alice", 20, "Physics") बुलाता है।
Python 替你调用 Student.__init__(new_object, "Alice", 20, "Physics")。
__init__, self is that new object. self.name = name attaches data to it.
__init__ के अंदर self वही नया object है। self.name = name उस पर data जोड़ता है।
在 __init__ 里,self 就是 那个新对象。self.name = name 把数据挂到它上面。
__init__ itself returns None.
भरा हुआ object लौटता है, और आपका variable अब उसकी ओर इशारा करता है। __init__ ख़ुद None लौटाता है।
填好的对象被交回,你的变量现在指向它。__init__ 本身返回 None。
class Student:
def __init__(self, name, age, course):
# self comes first, then your own parameters
self.name = name # attach name to this object
self.age = age # attach age to this object
self.course = course # attach course to this object
s1 = Student("Alice", 20, "Physics")
s2 = Student("Bob", 22, "Chemistry")
s3 = Student("Chandra", 19, "Math")
print(s1.name, s1.age, s1.course)
print(s3.name, s3.age, s3.course)
Want proof that __init__ runs by itself? Put a print inside it:
सबूत चाहिए कि __init__ अपने आप चलता है? उसके अंदर print रखें:
想证明 __init__ 会自动运行?在里面放一个 print:
class Student:
def __init__(self, name, age, course):
print(f" __init__ runs: self is {type(self).__name__}, data so far = {self.__dict__}")
self.name = name
self.age = age
self.course = course
print(f" __init__ ends: data = {self.__dict__}")
print("calling Student(...)")
s1 = Student("Alice", 20, "Physics")
print("back outside:", s1.name, s1.age, s1.course)
s2 = Student("Chandra", 19, "Math")
print(s1 is s2, s2.__dict__)
🎞️ Simulator: __init__ Step by Step🎞️ Simulator: __init__ क़दम-दर-क़दम🎞️ 模拟器:逐步运行 __init__
Press Next or Play. Watch the empty object appear, the __init__ frame open with self pointing at it, the slots fill one by one, and the frame vanish while the object stays. Every step was recorded from real Python with sys.settrace.
अगला या चलाएँ दबाएँ। देखें: ख़ाली object आता है, __init__ का frame खुलता है और self उसकी ओर इशारा करता है, खाँचे एक-एक कर भरते हैं, और frame ग़ायब होता है जबकि object बना रहता है। हर step असली Python से sys.settrace द्वारा रिकॉर्ड किया गया।
按 下一步 或 播放。观察:空对象出现,__init__ 的帧打开且 self 指向它,格子逐个填满,然后帧消失而对象留下。每一步都是用 sys.settrace 从真实 Python 中记录的。
Do not write Student.__init__("Alice", 20, "Physics"). Python calls __init__ for you when you write Student("Alice", 20, "Physics"). The class name is the front door; __init__ is the wiring behind the wall. The run below shows what goes wrong: "Alice" is taken as self, so one argument is missing.
Student.__init__("Alice", 20, "Physics") न लिखें। जब आप Student("Alice", 20, "Physics") लिखते हैं, Python आपके लिए __init__ बुलाता है। Class का नाम सामने का दरवाज़ा है; __init__ दीवार के पीछे की wiring है। नीचे का run दिखाता है कि क्या बिगड़ता है: "Alice" को self मान लिया जाता है, इसलिए एक argument कम पड़ता है।
不要写 Student.__init__("Alice", 20, "Physics")。当你写 Student("Alice", 20, "Physics") 时,Python 会替你调用 __init__。类名是前门;__init__ 是墙后的线路。下面的运行展示了出错原因:"Alice" 被当成了 self,于是少了一个参数。
class Student:
def __init__(self, name, age, course):
self.name, self.age, self.course = name, age, course
try:
Student.__init__("Alice", 20, "Physics") # don't call __init__ yourself
except TypeError as e:
print("TypeError:", e)
s = Student("Alice", 20, "Physics") # the class name is the front door
print(s.__dict__)
What Is self? 🪞self क्या है? 🪞self 是什么?🪞
Every method inside a class takes self as its first parameter. It is simply a name for "the object this method was called on". When you write buddy.bark(), Python quietly turns it into Dog.bark(buddy) and puts buddy into self.
Class के अंदर हर method पहले parameter के रूप में self लेता है। यह बस एक नाम है — "वह object जिस पर यह method बुलाया गया"। जब आप buddy.bark() लिखते हैं, Python चुपचाप इसे Dog.bark(buddy) बना देता है और buddy को self में रखता है।
类里的每个方法都把 self 作为第一个参数。它只是一个名字,意思是"调用这个方法的那个对象"。当你写 buddy.bark() 时,Python 悄悄把它变成 Dog.bark(buddy),并把 buddy 放进 self。
self.name = "Alice" means "on this object, create an attribute called name and set it to Alice". Later, self.name anywhere in the class reaches into that same object and reads it back. self is a convention, not a keyword — but every Python programmer uses it, so you should too.
self.name = "Alice" का मतलब है "इस object पर name नाम का attribute बनाओ और उसे Alice रखो"। बाद में class में कहीं भी self.name उसी object में पहुँचकर उसे वापस पढ़ता है। self एक परंपरा है, keyword नहीं — पर हर Python programmer इसे इस्तेमाल करता है, तो आप भी करें।
self.name = "Alice" 的意思是"在 这个 对象上创建名为 name 的属性,并设为 Alice"。之后在类中任何地方写 self.name,都会回到同一个对象里把它读出来。self 是约定俗成的名字,不是关键字 — 但所有 Python 程序员都用它,你也应该用。
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def bark(self):
# self is whichever dog .bark() was called on
print(f"{self.name} says: Woof!")
buddy = Dog("Buddy", "Labrador")
milo = Dog("Milo", "Beagle")
buddy.bark() # Python turns this into Dog.bark(buddy)
milo.bark()
Dog.bark(milo) # the same call, written the long way
🪞 Simulator: Who Is self?🪞 Simulator: self कौन है?🪞 模拟器:self 是谁?
Step through the program. Watch the purple self in each method frame: in the first bark() call it points to Dog #1 (Buddy), in the second to Dog #2 (Milo). Same method, different object.
Program को step करें। हर method frame में बैंगनी self देखें: पहली bark() call में यह Dog #1 (Buddy) की ओर इशारा करता है, दूसरी में Dog #2 (Milo) की ओर। वही method, अलग object।
逐步运行程序。注意每个方法帧里紫色的 self:第一次调用 bark() 时它指向 Dog #1(Buddy),第二次指向 Dog #2(Milo)。同一个方法,不同的对象。
Instance Variables — Where Each Object's Data Lives 🗄️Instance Variables — हर Object का Data कहाँ रहता है 🗄️实例变量 — 每个对象的数据存放在哪里 🗄️
An instance variable is a piece of data attached to one specific object. It is created the moment you write self.something = value inside a method (usually __init__). Every object gets its own copy.
Instance variable data का वह टुकड़ा है जो किसी एक ख़ास object से जुड़ा है। यह उसी पल बनता है जब आप method (आमतौर पर __init__) के अंदर self.something = value लिखते हैं। हर object को अपनी copy मिलती है।
实例变量 是附着在某一个特定对象上的一份数据。你在方法里(通常是 __init__)写下 self.something = value 的那一刻,它就被创建了。每个对象都有自己的一份。
self.attribute = values1.name = "Alice" lives on s1 only. Changing one object never affects another.s1.name = "Alice" सिर्फ़ s1 पर रहता है। एक object बदलने से दूसरा कभी नहीं बदलता।s1.name = "Alice" 只存在于 s1 上。改动一个对象永远不会影响另一个。__init____init__ के अंदर在 __init__ 里__init__. Then every new object has a known shape, visible at a glance.__init__ में बनाएँ। तब हर नए object का आकार पहले से पता होता है, एक नज़र में दिखता है।__init__ 中创建所有实例变量。这样每个新对象的结构都是已知的,一眼就能看清。object.attributeobj.attribute. Inside a method, use self.attribute. Both are the same lookup into the object.obj.attribute इस्तेमाल करें। Method के अंदर self.attribute। दोनों object में एक ही खोज हैं।obj.attribute,在方法内用 self.attribute。两者是对同一对象的同一种查找。Only Bob's balance moves ($500 → $700). Alice and Chandra live in different memory, so their data is safe.
सिर्फ़ Bob का balance बदलता है ($500 → $700)। Alice और Chandra अलग memory में रहते हैं, इसलिए उनका data सुरक्षित है।
只有 Bob 的余额变化($500 → $700)。Alice 和 Chandra 在不同的内存里,所以它们的数据是安全的。
Inside vs Outside the Class 🚪Class के अंदर बनाम बाहर 🚪类的内部与外部 🚪
The same instance variable is reached two ways, depending on where you are. Inside a method you write self.balance. Outside the class you write acc.balance. Both land on the same box in memory.
एक ही instance variable तक दो तरीक़ों से पहुँचते हैं, इस पर निर्भर कि आप कहाँ हैं। Method के अंदर आप self.balance लिखते हैं। Class के बाहर acc.balance। दोनों memory के एक ही डिब्बे पर पहुँचते हैं।
同一个实例变量有两种访问方式,取决于你所在的位置。在方法内部写 self.balance,在类外部写 acc.balance。两者都落在内存中的同一个格子上。
Inside a method: self.balance. Outside: acc.balance. One variable, two prefixes.
Method के अंदर: self.balance। बाहर: acc.balance। एक variable, दो prefix।
方法内:self.balance。外部:acc.balance。一个变量,两种前缀。
class BankAccount:
def __init__(self, holder, balance):
self.holder = holder
self.balance = balance
def deposit(self, amount):
# INSIDE the class -> use self.
self.balance += amount
acc = BankAccount("Alice", 1000)
# OUTSIDE the class -> use the object's name
print(acc.balance) # read from outside
acc.deposit(200) # the method uses self inside
print(acc.balance)
Reading acc.balance from outside is normal. Writing it from outside also works — but it skips any rules a method would check. In Section 13 you will see why changes should go through methods like deposit().
बाहर से acc.balance पढ़ना सामान्य है। बाहर से लिखना भी चलता है — पर वह हर उस नियम को छोड़ देता है जो method जाँचता। Section 13 में आप देखेंगे कि बदलाव deposit() जैसे methods से क्यों होने चाहिए।
从外部读取 acc.balance 很正常。从外部写入也能运行 — 但会跳过方法本来要检查的规则。在第 13 节你会看到为什么修改应该通过 deposit() 这样的方法进行。
Instance Variables vs Local Variables 🫧Instance Variables बनाम Local Variables 🫧实例变量与局部变量 🫧
Inside a method, self.count and count are completely different things. One survives after the method ends. The other is thrown away the moment the method returns.
Method के अंदर self.count और count बिल्कुल अलग चीज़ें हैं। एक method ख़त्म होने के बाद भी बचा रहता है। दूसरा method के return होते ही फेंक दिया जाता है।
在方法内部,self.count 和 count 是完全不同的东西。一个在方法结束后依然存在,另一个在方法返回的瞬间就被丢弃。
count = 100count = 100count = 100 |
| Lives only inside this one method callसिर्फ़ इसी एक method call के अंदर रहता है只存在于这一次方法调用中 |
| Not attached to the objectObject से जुड़ा नहीं不附着在对象上 |
| Gone the moment the method returnsMethod return होते ही ग़ायब方法一返回就消失 |
self.count = 100self.count = 100self.count = 100 |
| Survives across every method callहर method call के पार बचा रहता है在每次方法调用之间都保留 |
| Attached to this specific objectइसी ख़ास object से जुड़ा附着在这个特定对象上 |
| Lives as long as the object livesजब तक object है, तब तक रहता है对象活多久,它就活多久 |
class Counter:
def __init__(self):
self.count = 0 # instance variable - kept on the object
def bad_increment(self):
count = self.count + 1 # local variable - gone when the method returns
def good_increment(self):
self.count += 1 # changes the object
c = Counter()
c.bad_increment()
c.bad_increment()
print(c.count)
c.good_increment()
c.good_increment()
print(c.count)
🫧 Simulator: Local vs Instance🫧 Simulator: Local बनाम Instance🫧 模拟器:局部变量与实例变量
Step through four calls. In bad_increment() a local count = 1 appears in the method frame — then the frame disappears and takes it along. The object still says count: 0. In good_increment() the change lands on the object itself.
चार calls को step करें। bad_increment() में method frame में एक local count = 1 आता है — फिर frame ग़ायब होता है और उसे साथ ले जाता है। Object अब भी count: 0 कहता है। good_increment() में बदलाव सीधे object पर होता है।
逐步运行四次调用。在 bad_increment() 中,方法帧里出现了局部的 count = 1 — 然后帧消失并把它一起带走,对象仍显示 count: 0。在 good_increment() 中,修改直接落在对象本身上。
Practical Example 1 — A Rectangle ▭व्यावहारिक उदाहरण 1 — एक Rectangle ▭实例 1 — 矩形 ▭
Two instance variables, three methods. Every method reads the data of its own object through self.
दो instance variables, तीन methods। हर method self के ज़रिए अपने object का data पढ़ता है।
两个实例变量,三个方法。每个方法都通过 self 读取 自己 那个对象的数据。
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
def describe(self):
print(f"Rectangle {self.width}x{self.height}: "
f"area={self.area()}, perimeter={self.perimeter()}")
r1 = Rectangle(4, 5)
r2 = Rectangle(10, 3)
r1.describe()
r2.describe()
r1.width = 100
r1.describe() # r1 changed
r2.describe() # r2 is exactly as before
Changing r1.width changes only r1. r2 prints exactly the same line as before.
r1.width बदलने से सिर्फ़ r1 बदलता है। r2 पहले जैसी ही line print करता है।
修改 r1.width 只改变 r1。r2 打印的结果与之前完全一样。
Practical Example 2 — A Student Record System 🎓व्यावहारिक उदाहरण 2 — Student Record System 🎓实例 2 — 学生成绩系统 🎓
Each Student object carries its own marks list and computes its own average and grade.
हर Student object अपनी marks list रखता है और अपना average और grade ख़ुद निकालता है।
每个 Student 对象都带着自己的 marks 列表,并自己计算平均分和等级。
class Student:
def __init__(self, roll_no, name, marks):
self.roll_no = roll_no
self.name = name
self.marks = marks
def average(self):
return sum(self.marks) / len(self.marks)
def grade(self):
avg = self.average()
if avg >= 90: return "A"
if avg >= 75: return "B"
if avg >= 60: return "C"
return "F"
def report(self):
print(f"{self.roll_no:3d} {self.name:15s} "
f"Avg: {self.average():5.1f} Grade: {self.grade()}")
classroom = [
Student(1, "Alice", [92, 88, 95, 90]),
Student(2, "Bob", [70, 75, 80, 72]),
Student(3, "Chandra", [55, 62, 58, 65]),
Student(4, "Deepa", [40, 45, 50, 42]),
]
for student in classroom:
student.report()
print(classroom[1].average()) # 74.25 is printed as 74.2 - see the note below
Bob's real average is 74.25 — the last line of the output proves it. It is exactly half-way between 74.2 and 74.3, and Python's formatting rounds an exact half to the even digit. So :5.1f shows 74.2. Alice (91.25) and Deepa (44.25) work the same way. Also note: Bob gets a C, because 74.25 is below 75.
Bob का असली average 74.25 है — output की आख़िरी line यह साबित करती है। यह 74.2 और 74.3 के ठीक बीच है, और Python की formatting ठीक आधे को सम (even) अंक की ओर round करती है। इसलिए :5.1f 74.2 दिखाता है। Alice (91.25) और Deepa (44.25) भी ऐसे ही हैं। यह भी देखें: Bob को C मिलता है, क्योंकि 74.25, 75 से कम है।
Bob 的真实平均分是 74.25 — 输出最后一行证明了这一点。它正好在 74.2 和 74.3 的中间,而 Python 的格式化会把正好一半的情况舍入到 偶数 位。所以 :5.1f 显示 74.2。Alice(91.25)和 Deepa(44.25)也是如此。另外注意:Bob 得 C,因为 74.25 低于 75。
🎓 Simulator: The Grade Card🎓 Simulator: Grade Card🎓 模拟器:成绩卡
Pick a student and drag the marks. Only that object's marks list changes — watch the other report lines stay put. The formatting matches Python's :5.1f exactly (checked on all 401 possible totals).
एक student चुनें और marks खिसकाएँ। सिर्फ़ उसी object की marks list बदलती है — देखें कि बाक़ी report lines वैसी ही रहती हैं। Formatting Python के :5.1f से हूबहू मिलती है (सभी 401 संभव totals पर जाँची गई)।
选一个学生,拖动分数。只有 那个 对象的 marks 列表会变 — 看看其他报告行保持不变。格式与 Python 的 :5.1f 完全一致(已在全部 401 种可能的总分上核对)。
Four separate Student objects each carried their own marks, computed their own average and printed their own report — all from one class. With __init__, creating 4 students took 4 lines. Without it, you would have written 12 manual assignments (3 attributes × 4 students).
चार अलग Student objects ने अपने-अपने marks रखे, अपना average निकाला और अपनी report print की — सब एक ही class से। __init__ के साथ 4 students बनाने में 4 lines लगीं। इसके बिना आपको 12 manual assignments लिखने पड़ते (3 attributes × 4 students)।
四个独立的 Student 对象各自带着自己的 marks,各自计算平均分、打印报告 — 全都来自同一个类。有了 __init__,创建 4 个学生只用了 4 行。没有它,你得写 12 行手动赋值(3 个属性 × 4 个学生)。
Practical Example 3 — A Bank Account With Rules 🏦व्यावहारिक उदाहरण 3 — नियमों वाला Bank Account 🏦实例 3 — 带规则的银行账户 🏦
class BankAccount:
def __init__(self, holder, opening_balance=0):
if opening_balance < 0:
raise ValueError("Opening balance cannot be negative")
self.holder = holder
self.balance = opening_balance
self.history = [] # each account gets its own list
def deposit(self, amount):
if amount <= 0:
raise ValueError("Deposit must be positive")
self.balance += amount
self.history.append(("deposit", amount))
def withdraw(self, amount):
if amount > self.balance:
raise ValueError("Insufficient funds")
self.balance -= amount
self.history.append(("withdraw", amount))
def statement(self):
print(f"\n--- {self.holder} ---")
for action, amount in self.history:
print(f" {action:9s} ${amount}")
print(f" Current balance: ${self.balance}")
alice = BankAccount("Alice", 1000)
bob = BankAccount("Bob")
alice.deposit(500)
alice.withdraw(200)
bob.deposit(100)
bob.deposit(300)
bob.withdraw(50)
alice.statement()
bob.statement()
for bad in [lambda: BankAccount("Eve", -5), lambda: bob.withdraw(10_000), lambda: alice.deposit(0)]:
try:
bad()
except ValueError as e:
print("ValueError:", e)
The blank line before each --- header is real: statement() prints "\n--- ...". The last three lines show the rules at work.
हर --- header से पहले की ख़ाली line असली है: statement() "\n--- ..." print करता है। आख़िरी तीन lines नियमों को काम करते दिखाती हैं।
每个 --- 标题前的空行是真实存在的:statement() 打印的是 "\n--- ..."。最后三行展示了规则在起作用。
🏦 Simulator: Bank Account Lab🏦 Simulator: Bank Account Lab🏦 模拟器:银行账户实验室
Two objects from the class above. Choose an account, an action and an amount, then press Run. Try to overdraw, deposit 0, or create Eve with −5. Then press the ⚠️ button and see what writing balance from outside does. The JavaScript model was checked against the real class on 300 random sequences.
ऊपर की class से दो objects। Account, action और amount चुनें, फिर Run दबाएँ। Overdraw करें, 0 जमा करें, या −5 से Eve बनाएँ। फिर ⚠️ button दबाएँ और देखें कि बाहर से balance लिखने पर क्या होता है। JavaScript model को असली class से 300 random sequences पर जाँचा गया।
来自上面那个类的两个对象。选择账户、操作和金额,然后按 Run。试试透支、存 0 元,或用 −5 创建 Eve。再按 ⚠️ 按钮,看看从外部写 balance 会怎样。这个 JavaScript 模型已在 300 个随机操作序列上与真实类核对过。
__init__ rejects a negative opening balance, and deposit/withdraw enforce their own rules. Manual assignment from outside gives you none of this — anyone could write alice.balance = -1_000_000. Keeping data and its rules together in one class is called encapsulation, and it is a big reason we prefer __init__ and methods.
__init__ negative opening balance ठुकराता है, और deposit/withdraw अपने नियम लागू करते हैं। बाहर से manual assignment में इनमें से कुछ नहीं मिलता — कोई भी alice.balance = -1_000_000 लिख सकता है। Data और उसके नियमों को एक class में साथ रखना encapsulation कहलाता है, और यही बड़ी वजह है कि हम __init__ और methods पसंद करते हैं।
__init__ 拒绝负的开户余额,deposit/withdraw 执行各自的规则。从外部手动赋值则什么保护都没有 — 谁都可以写 alice.balance = -1_000_000。把数据和它的规则放在同一个类里,叫作 封装(encapsulation),这正是我们偏爱 __init__ 和方法的重要原因。
Common Mistakes (and Fixes) 🩹आम ग़लतियाँ (और उनके हल) 🩹常见错误(及修正)🩹
def __init__(name): instead of def __init__(self, name):. Python always passes the object first.def __init__(self, name): की जगह def __init__(name): लिखना। Python हमेशा object को पहले भेजता है।def __init__(self, name): 写成 def __init__(name):。Python 总是先把对象传进来。balance = 100 in a method instead of self.balance = 100. The value is a local and is thrown away.self.balance = 100 की जगह balance = 100 लिखना। Value local है और फेंक दी जाती है।balance = 100 而不是 self.balance = 100。这个值是局部的,会被丢弃。def __init__(self, tags=[]). Every object that uses the default shares the same list. Use tags=None and build a fresh list inside.def __init__(self, tags=[]) कभी न लिखें। Default इस्तेमाल करने वाले सारे objects एक ही list बाँटते हैं। tags=None रखें और अंदर नई list बनाएँ।def __init__(self, tags=[])。所有使用默认值的对象共享同一个列表。改用 tags=None,在内部新建列表。None or []. Readers see at once what data the class holds.None या [] रखें। पढ़ने वाला तुरंत देखता है कि class कौन-सा data रखती है।None 或 []。读代码的人一眼就知道这个类保存哪些数据。raise ValueError in __init__. Fail loudly at creation, not deep inside your logic.__init__ में raise ValueError करें। बनाते समय ज़ोर से fail करें, logic की गहराई में नहीं।__init__ 里 raise ValueError。在创建时大声失败,而不是在逻辑深处。def __repr__(self) so print(obj) shows something useful instead of <__main__.Student object at 0x...>.def __repr__(self) लिखें ताकि print(obj) <__main__.Student object at 0x...> की जगह कुछ काम का दिखाए।def __repr__(self),让 print(obj) 显示有用的内容,而不是 <__main__.Student object at 0x...>。The first three mistakes, run for real — note how the mutable default gives Ravi Asha's book:
पहली तीन ग़लतियाँ, असली run में — देखें कि mutable default से Ravi को Asha की book कैसे मिल जाती है:
前三个错误的真实运行 — 注意可变默认值如何让 Ravi 拿到了 Asha 的书:
class Dog:
def __init__(name): # forgot self
name = name
try:
Dog("Buddy")
except TypeError as e:
print("TypeError:", e)
class Bag:
def __init__(self, owner, items=[]): # mutable default - a trap
self.owner = owner
self.items = items
a = Bag("Asha")
b = Bag("Ravi")
a.items.append("book")
print(b.items, a.items is b.items) # Ravi got Asha's book!
class SafeBag:
def __init__(self, owner, items=None):
self.owner = owner
self.items = [] if items is None else items # a fresh list each time
a = SafeBag("Asha")
b = SafeBag("Ravi")
a.items.append("book")
print(b.items, a.items is b.items)
In the first error, "Buddy" has nowhere to go: the only parameter, name, already received the object. The missing-self. bug is the bad_increment() you stepped through in Section 10. And here is __repr__:
पहले error में "Buddy" के लिए कोई जगह नहीं: इकलौते parameter name को पहले ही object मिल चुका है। self. छूटने वाला bug वही bad_increment() है जिसे आपने Section 10 में step किया। और यह रहा __repr__:
第一个错误里,"Buddy" 无处可去:唯一的参数 name 已经接收了对象本身。漏掉 self. 的 bug 就是你在第 10 节逐步运行过的 bad_increment()。再看 __repr__:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Student(name={self.name!r}, age={self.age})"
s = Student("Alice", 20)
print(s)
print([Student("Bob", 22), Student("Chandra", 19)])
Quick Reference Table 📋Quick Reference Table 📋速查表 📋
| ConceptConcept概念 | SyntaxSyntax语法 | What it doesक्या करता है作用 |
|---|---|---|
| Define a classClass define करना定义类 | class Student: | Creates the blueprintBlueprint बनाता है创建蓝图 |
| Create an objectObject बनाना创建对象 | s = Student("Alice", 20) | Builds a new instance and runs __init__नया instance बनाता है और __init__ चलाता है创建新实例并运行 __init__ |
| InitializerInitializer初始化方法 | def __init__(self, ...): | Runs automatically at creationबनते समय अपने आप चलता है创建时自动运行 |
| Instance variable (inside)Instance variable (अंदर)实例变量(内部) | self.x = value | Attaches data to this objectइस object पर data जोड़ता है把数据挂到这个对象上 |
| Instance variable (outside)Instance variable (बाहर)实例变量(外部) | obj.x = value | Works — but scales badly and skips validationचलता है — पर बड़ा होने पर मुश्किल, जाँच छूटती है能用 — 但难以扩展,也跳过校验 |
| Local variableLocal variable局部变量 | x = value | Thrown away when the method returnsMethod return होते ही फेंका जाता है方法返回时被丢弃 |
| Read from outsideबाहर से पढ़ना从外部读取 | obj.attribute | Fetches the value from the objectObject से value लाता है从对象中取值 |
| Read from insideअंदर से पढ़ना从内部读取 | self.attribute | The same lookup, from inside a methodवही खोज, method के अंदर से同样的查找,在方法内部 |
| Inspect stateState देखना查看状态 | obj.__dict__ | Shows all instance variables as a dictसारे instance variables dict के रूप में दिखाता है以字典形式显示所有实例变量 |
| Check the typeType जाँचना检查类型 | isinstance(obj, Class) | Preferred over comparing type(obj)type(obj) की तुलना से बेहतर优于比较 type(obj) |
Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆
obj.x = value). But don't — it is repetitive, error-prone, unchecked, and gives objects different shapes. Use __init__.obj.x = value)। पर न करें — यह दोहराव भरा, ग़लती वाला, बिना जाँच का है, और objects को अलग-अलग आकार देता है। __init__ इस्तेमाल करें।obj.x = value)。但别这样做 — 它重复、易错、没有检查,还让对象结构各不相同。请用 __init__。BankAccount), variables and methods in snake_case (opening_balance). Following PEP 8 makes code easy to read.BankAccount), variables और methods snake_case में (opening_balance)। PEP 8 मानने से code पढ़ना आसान होता है।BankAccount),变量和方法用 snake_case(opening_balance)。遵循 PEP 8 让代码易读。__init__ runs automatically when you create an object. Never call Student.__init__(...) yourself — write Student(...).__init__ अपने आप चलता है। Student.__init__(...) ख़ुद कभी न बुलाएँ — Student(...) लिखें।__init__ 自动运行。永远不要自己调用 Student.__init__(...) — 写 Student(...)。self — the object the method was called on. It is not magic: Python passes the object in for you.self है — वह object जिस पर method बुलाया गया। यह जादू नहीं: Python आपके लिए object भेजता है।self — 调用该方法的对象。这不是魔法:Python 替你把对象传进来。self. creates an instance variable on that object. Without self. it is a local variable that dies when the method returns.self. से शुरू होने वाला assignment उस object पर instance variable बनाता है। self. के बिना यह local variable है जो method return होते ही ख़त्म हो जाता है।self. 开头的赋值会在该对象上创建实例变量。没有 self. 就是局部变量,方法返回时就消失。obj1.balance never affects obj2.balance.obj1.balance बदलने से obj2.balance कभी नहीं बदलता।obj1.balance 永远不会影响 obj2.balance。__init__, even ones you set later (start them as None or []). The object's shape is then documented in one place.__init__ में declare करें, बाद में set होने वाले भी (None या [] से शुरू करें)। तब object का आकार एक ही जगह लिखा रहता है।__init__ 中声明所有实例变量,包括以后才设置的(先设为 None 或 [])。这样对象的结构就集中写在一处。tags=[]) in __init__. Use tags=None and build a fresh list inside, or every object shares one list.__init__ में mutable default कभी न रखें (tags=[])। tags=None रखें और अंदर नई list बनाएँ, वरना हर object एक ही list बाँटेगा।__init__ 里 永远不要用可变默认值(tags=[])。改用 tags=None 并在内部新建列表,否则所有对象共享同一个列表。__init__. Raise ValueError or TypeError on bad data at creation, not five methods later. Fail fast, fail loud.__init__ के अंदर जाँचें। ग़लत data पर बनाते समय ही ValueError या TypeError raise करें, पाँच methods बाद नहीं। जल्दी fail करें, ज़ोर से fail करें।__init__ 中校验输入。遇到坏数据就在创建时抛出 ValueError 或 TypeError,而不是五个方法之后。尽早失败,大声失败。Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题
A class describes the shape once; each object holds its own data. __init__ runs automatically on every ClassName(...) call and fills the new, empty object through self. Anything written as self.x stays on the object; anything without self. is a local that vanishes. Put the setup and its checks inside the class, and let methods change the data.
Class आकार एक बार बताती है; हर object अपना data रखता है। __init__ हर ClassName(...) call पर अपने आप चलता है और self के ज़रिए नए, ख़ाली object को भरता है। जो कुछ self.x के रूप में लिखा है वह object पर रहता है; जो self. के बिना है वह local है और ग़ायब हो जाता है। Setup और उसकी जाँच class के अंदर रखें, और data को methods से बदलें।
类只描述一次结构;每个对象保存自己的数据。每次调用 ClassName(...),__init__ 都会自动运行,并通过 self 填充新的空对象。写成 self.x 的东西留在对象上;没有 self. 的是局部变量,会消失。把设置和检查放进类里,让方法来修改数据。