The Story That Explains Inheritance 🍞Inheritance समझाने वाली कहानी 🍞一个讲清继承的故事 🍞
Mum took Grandma's recipe, kept every step, and added her own touch — rosemary.
You took Mum's recipe, kept everything (Grandma's steps and Mum's rosemary), and added your own — garlic and cheese. You did not rewrite the base recipe. You did not rewrite Mum's changes. You inherited everything above you and only added what is new.
That is exactly what inheritance does in Python. A child class automatically gets every attribute and method of its parent class. It can add its own, or replace ("override") certain pieces. No copy-paste. Fix the parent once, and every child gets the fix.
माँ ने दादी की recipe ली, हर step रखा, और अपना स्पर्श जोड़ा — rosemary।
आपने माँ की recipe ली, सब कुछ रखा (दादी के steps और माँ का rosemary), और अपना जोड़ा — garlic और cheese। आपने बुनियादी recipe दोबारा नहीं लिखी। माँ के बदलाव दोबारा नहीं लिखे। आपने ऊपर का सब कुछ विरासत में पाया और सिर्फ़ नया जोड़ा।
Python में inheritance ठीक यही करता है। Child class को अपनी parent class का हर attribute और method अपने आप मिल जाता है। वह अपना जोड़ सकती है, या कुछ हिस्से बदल ("override") सकती है। कोई copy-paste नहीं। Parent को एक बार ठीक करें, हर child को fix मिल जाता है।
妈妈拿来奶奶的食谱,保留每一步,再加上自己的点子 — 迷迭香。
你拿来妈妈的食谱,保留一切(奶奶的步骤和妈妈的迷迭香),再加上你的 — 大蒜和奶酪。你没有重写基础食谱,也没有重写妈妈的改动。你 继承 了上面的一切,只添加新的部分。
这正是 Python 中 继承 所做的事。子类 自动获得 父类 的所有属性和方法。它可以添加自己的,也可以替换("重写")某些部分。不用复制粘贴。父类改一次,所有子类都得到修正。
A child class is not a copy of its parent — it is linked to it. When you call a method on the child that the child does not define, Python automatically walks up the chain and finds it in the parent. This is "IS-A" thinking in code: an ElectricCar is a Car, which is a Vehicle.
Child class अपने parent की copy नहीं है — वह उससे जुड़ी है। जब आप child पर ऐसा method बुलाते हैं जो child में नहीं है, तो Python अपने आप chain में ऊपर जाकर उसे parent में खोज लेता है। Code में यही "IS-A" सोच है: ElectricCar एक Car है, जो एक Vehicle है।
子类 不是 父类的 副本 — 它与父类相连。当你在子类上调用一个子类没有定义的方法时,Python 会自动沿着继承链向上,在父类中找到它。这就是代码中的"IS-A(是一种)"思维:ElectricCar 是一种 Car,而 Car 是一种 Vehicle。
Look for the interactive boxes: the Method Lookup Explorer (Section 05), the super() Chain Stepper (06), the Login Lab (09), the Family Checker (10) and the Bug Doctor (11). Every result in them comes from a real Python run.
Interactive डिब्बे खोजें: Method Lookup Explorer (Section 05), super() Chain Stepper (06), Login Lab (09), Family Checker (10) और Bug Doctor (11)। इनका हर नतीजा असली Python run से आया है।
请留意这些互动框:方法查找探索器(第 05 节)、super() 调用链逐步演示(06)、登录实验室(09)、家族检查器(10)和 Bug 医生(11)。里面的每个结果都来自真实的 Python 运行。
Why We Need Inheritance — Don't Repeat Yourself ♻️Inheritance क्यों चाहिए — ख़ुद को न दोहराएँ ♻️为什么需要继承 — 不要重复自己 ♻️
Suppose you model three kinds of vehicles. Without inheritance, you write the same brand, wheels, speed and start() code three times. Change something? Update three places. Add a fourth vehicle? Copy-paste again. Bugs creep in, and the copies slowly drift apart. This idea — write it once — is called DRY: Don't Repeat Yourself.
मान लीजिए आप तीन तरह के vehicles model करते हैं। Inheritance के बिना आप वही brand, wheels, speed और start() code तीन बार लिखते हैं। कुछ बदलना है? तीन जगह update करें। चौथा vehicle जोड़ना है? फिर copy-paste। Bugs घुसते हैं, और copies धीरे-धीरे अलग हो जाती हैं। यह विचार — एक बार लिखो — DRY कहलाता है: Don't Repeat Yourself।
假设你要为三种交通工具建模。没有继承,你就得把同样的 brand、wheels、speed 和 start() 代码写三遍。要改点什么?改三处。再加第四种?再复制粘贴一次。bug 会混进来,各个副本也会慢慢走样。"只写一次"的思想叫 DRY:Don't Repeat Yourself(不要重复自己)。
| 3 vehicle classes → 3 copies of the same setup3 vehicle classes → एक ही setup की 3 copies3 个交通工具类 → 同样的设置写 3 份 |
| Fix a bug → update every copyBug ठीक करें → हर copy update करें修一个 bug → 每份都要改 |
| Behaviour drifts apart over timeसमय के साथ व्यवहार अलग हो जाता है行为随时间逐渐偏离 |
| A reader cannot tell they are relatedपढ़ने वाला नहीं बता सकता कि ये जुड़े हैं读代码的人看不出它们有关系 |
| Common logic lives once, in the parentसाझा logic एक बार, parent में रहता है共同逻辑只在父类里写一次 |
| Fix a bug in one place — every child gets itएक जगह bug ठीक करें — हर child को मिलता है在一个地方修 bug — 所有子类都受益 |
| A new subclass is tiny — just the differencesनई subclass छोटी होती है — सिर्फ़ फ़र्क़新子类很小 — 只写差异 |
The relationship is visible: class Car(Vehicle)रिश्ता साफ़ दिखता है: class Car(Vehicle)关系一目了然:class Car(Vehicle) |
Basic Syntaxबुनियादी Syntax基本语法
# -- Parent class (also called base class or superclass) --
class Vehicle:
def __init__(self, brand, wheels):
self.brand = brand
self.wheels = wheels
def start(self):
print(f"{self.brand} engine starting...")
# -- Child class: put the parent in parentheses --
class Car(Vehicle): # "Car IS-A Vehicle"
pass # nothing new yet - but it already has __init__ and start()
my_car = Car("Honda", 4) # uses Vehicle's __init__
my_car.start() # uses Vehicle's start()
print(my_car.brand)
print(my_car.wheels)
print("start" in Car.__dict__, "start" in Vehicle.__dict__) # where start() really lives
The last line proves the point: start is not stored in Car at all. It lives only in Vehicle, and Python finds it there. The parent is also called the base class or superclass; the child is the subclass.
आख़िरी line बात साबित करती है: start Car में रखा ही नहीं है। वह सिर्फ़ Vehicle में है, और Python उसे वहीं पाता है। Parent को base class या superclass भी कहते हैं; child को subclass।
最后一行证明了这一点:start 根本不在 Car 里。它只存在于 Vehicle 中,Python 在那里找到它。父类也叫 基类(base class) 或 超类(superclass);子类叫 subclass。
The Inheritance Tree 🌳Inheritance Tree 🌳继承树 🌳
Three levels: Vehicle at the top, Car in the middle, ElectricCar at the bottom. Each child gets everything above it and adds its own. On the right, one tesla object shows everything it can reach.
तीन स्तर: सबसे ऊपर Vehicle, बीच में Car, नीचे ElectricCar। हर child को ऊपर का सब कुछ मिलता है और वह अपना जोड़ता है। दाईं ओर एक tesla object दिखाता है कि वह किन-किन तक पहुँच सकता है।
三层结构:顶部是 Vehicle,中间是 Car,底部是 ElectricCar。每个子类获得上面的一切,再加上自己的。右边的一个 tesla 对象展示了它能访问的全部内容。
Dot colours show where each name is defined: purple = Vehicle, orange = Car, teal = ElectricCar. Only battery_kwh, charge() and start() are written in ElectricCar itself.
Dot के रंग बताते हैं कि हर नाम कहाँ define है: बैंगनी = Vehicle, नारंगी = Car, teal = ElectricCar। सिर्फ़ battery_kwh, charge() और start() ख़ुद ElectricCar में लिखे हैं।
圆点颜色表示每个名字定义在哪里:紫色 = Vehicle,橙色 = Car,青色 = ElectricCar。只有 battery_kwh、charge() 和 start() 写在 ElectricCar 自己里面。
Extending — Adding New Attributes and Methods ➕Extending — नए Attributes और Methods जोड़ना ➕扩展 — 添加新的属性和方法 ➕
Inheriting is only half the story. The real power of a child class is to add its own attributes and methods without touching the parent. The child has everything the parent has, plus whatever you define.
Inherit करना आधी कहानी है। Child class की असली ताक़त है parent को छुए बिना अपने attributes और methods जोड़ना। Child के पास parent का सब कुछ है, और जो आप define करें वह भी।
继承只是故事的一半。子类真正的威力在于不改动父类就能 添加 自己的属性和方法。子类拥有父类的一切,再加上你定义的内容。
class Vehicle:
def __init__(self, brand, wheels):
self.brand = brand
self.wheels = wheels
self.speed = 0
def accelerate(self, amount):
self.speed += amount
print(f"{self.brand} now at {self.speed} km/h")
class Car(Vehicle):
def __init__(self, brand, doors, fuel_type):
# Call the parent's __init__ first - super() is explained in Section 06
super().__init__(brand, wheels=4)
# Now add child-specific instance variables
self.doors = doors
self.fuel_type = fuel_type
# A brand-new method that only Car has
def open_doors(self):
print(f"Opening all {self.doors} doors of the {self.brand}")
honda = Car("Honda Civic", doors=4, fuel_type="petrol")
honda.accelerate(50) # inherited from Vehicle
honda.open_doors() # brand-new on Car
print(honda.wheels) # set by the parent's __init__
print(honda.fuel_type) # added by the child
print(honda.__dict__) # one object holds both layers
honda.__dict__ shows one object with both layers: brand, wheels and speed were set by Vehicle.__init__; doors and fuel_type by Car.__init__.
honda.__dict__ दोनों layers वाला एक object दिखाता है: brand, wheels और speed को Vehicle.__init__ ने set किया; doors और fuel_type को Car.__init__ ने।
honda.__dict__ 显示了一个同时包含两层数据的对象:brand、wheels 和 speed 由 Vehicle.__init__ 设置;doors 和 fuel_type 由 Car.__init__ 设置。
Method Overriding — Replacing the Parent's Behaviour 🔁Method Overriding — Parent का व्यवहार बदलना 🔁方法重写 — 替换父类的行为 🔁
Sometimes the parent's method is not right for the child. An electric car does not start with a roar — it starts silently. Instead of adding a new method, the child redefines a method with the same name. This is called overriding.
कभी-कभी parent का method child के लिए सही नहीं होता। Electric car दहाड़ के साथ start नहीं होती — चुपचाप start होती है। नया method जोड़ने की जगह child उसी नाम का method दोबारा define करता है। इसे overriding कहते हैं।
有时父类的方法并不适合子类。电动车不会轰鸣着启动 — 它安静地启动。子类不是新增方法,而是 用同样的名字重新定义一个方法。这叫 重写(overriding)。
class Car:
def __init__(self, brand):
self.brand = brand
def start(self):
print(f"{self.brand} engine starts with a ROAR!")
class ElectricCar(Car):
# Same method name -> this REPLACES the parent's start()
def start(self):
print(f"{self.brand} powers on silently. Ready to drive.")
petrol = Car("Honda")
electric = ElectricCar("Tesla")
petrol.start()
electric.start()
The first match wins. That is why a child's override always beats the parent's version.
पहला match जीतता है। इसीलिए child का override हमेशा parent के version से आगे रहता है।
第一个匹配的胜出。这就是子类的重写总能压过父类版本的原因。
This search order is called the Method Resolution Order (MRO). Every class can show it to you:
खोज के इस क्रम को Method Resolution Order (MRO) कहते हैं। हर class इसे दिखा सकती है:
这个查找顺序叫 方法解析顺序(MRO)。每个类都能把它显示出来:
class Vehicle: pass
class Car(Vehicle): pass
class ElectricCar(Car): pass
print(ElectricCar.mro())
print([c.__name__ for c in ElectricCar.mro()]) # just the names
ElectricCar.mro() lists the classes in search order. The last one, object, is Python's ultimate base class — every class inherits from it, whether you write it or not.
ElectricCar.mro() खोज के क्रम में classes की list देता है। आख़िरी, object, Python की सबसे बुनियादी class है — हर class उससे inherit करती है, आप लिखें या न लिखें।
ElectricCar.mro() 按查找顺序列出各个类。最后的 object 是 Python 最根本的基类 — 每个类都继承自它,不管你写不写。
🔎 Simulator: Method Lookup Explorer🔎 Simulator: Method Lookup Explorer🔎 模拟器:方法查找探索器
Pick an object and a name. Watch Python check the object's own data first, then each class in the MRO, until the first match. Try charge on the bike, or fly on anything. All 24 results come from real Python (the classes are the ones from Section 08).
कोई object और कोई नाम चुनें। देखें कि Python पहले object का अपना data देखता है, फिर MRO की हर class, जब तक पहला match न मिले। Bike पर charge या किसी पर भी fly आज़माएँ। सभी 24 नतीजे असली Python से हैं (classes वही हैं जो Section 08 में हैं)।
选一个对象和一个名字。看 Python 先查对象自己的数据,再依次查 MRO 中的每个类,直到第一个匹配。试试在 bike 上调用 charge,或在任意对象上调用 fly。全部 24 个结果都来自真实 Python(所用的类与第 08 节相同)。
super() — Reaching Back to the Parent 🪜super() — Parent तक वापस पहुँचना 🪜super() — 回头调用父类 🪜
When you override a method but still want the parent's behaviour plus your own, use super(). It lets you call the parent's version first, then add to it.
जब आप method override करते हैं पर parent का व्यवहार भी चाहिए, साथ में अपना भी, तो super() इस्तेमाल करें। इससे आप पहले parent का version बुलाते हैं, फिर उसमें जोड़ते हैं।
当你重写了一个方法,但仍想要父类的行为 再加上 你自己的,就用 super()。它让你先调用父类的版本,再在此基础上追加。
class Vehicle:
def __init__(self, brand, wheels):
self.brand = brand
self.wheels = wheels
print(f" [Vehicle] set brand={brand}, wheels={wheels}")
def describe(self):
return f"{self.brand} with {self.wheels} wheels"
class Car(Vehicle):
def __init__(self, brand, doors):
super().__init__(brand, wheels=4) # call the parent's __init__ first
self.doors = doors
print(f" [Car] added doors={doors}")
def describe(self):
# EXTEND the parent's describe() instead of replacing it
base = super().describe()
return f"{base}, {self.doors} doors"
class ElectricCar(Car):
def __init__(self, brand, doors, battery_kwh):
super().__init__(brand, doors) # chain up to Car -> Vehicle
self.battery_kwh = battery_kwh
print(f" [ElectricCar] added battery_kwh={battery_kwh}")
def describe(self):
base = super().describe() # Car's describe -> Vehicle's describe
return f"{base}, {self.battery_kwh} kWh battery"
print("Creating Tesla...")
tesla = ElectricCar("Tesla Model 3", doors=4, battery_kwh=75)
print()
print(tesla.describe())
Every level's __init__ called its parent's __init__ first, then added its own attributes. Every level's describe() called the parent's version and added its own detail. That is the whole "extend, don't replace" pattern in one example.
हर स्तर के __init__ ने पहले अपने parent का __init__ बुलाया, फिर अपने attributes जोड़े। हर स्तर के describe() ने parent का version बुलाया और अपनी जानकारी जोड़ी। एक उदाहरण में पूरा "extend करो, replace नहीं" pattern।
每一层的 __init__ 都先调用父类的 __init__,再添加自己的属性。每一层的 describe() 都调用父类版本,再追加自己的细节。一个例子就展示了完整的"扩展而非替换"模式。
ElectricCar("Tesla", 4, 75): each __init__ first climbs to its parent. Vehicle's attributes land first, then Car's, then ElectricCar's — one object, three layers.
ElectricCar("Tesla", 4, 75): हर __init__ पहले अपने parent तक चढ़ता है। Vehicle के attributes पहले आते हैं, फिर Car के, फिर ElectricCar के — एक object, तीन layers।
ElectricCar("Tesla", 4, 75):每个 __init__ 先爬到父类。Vehicle 的属性最先写入,然后是 Car 的,最后是 ElectricCar 的 — 一个对象,三层数据。
🪜 Simulator: super() Chain Stepper🪜 Simulator: super() Chain Stepper🪜 模拟器:super() 调用链逐步演示
Step through a real run. Watch the call stack grow as each super() climbs one level up — every frame has the same self — then shrink as each level finishes and the child continues. Then describe() does the same climb. Recorded from real Python with sys.settrace.
एक असली run को step करें। देखें कि हर super() के एक स्तर ऊपर चढ़ने पर call stack बढ़ता है — हर frame में वही self है — फिर हर स्तर के पूरा होने पर घटता है और child आगे बढ़ता है। फिर describe() भी ऐसे ही चढ़ता है। असली Python से sys.settrace द्वारा रिकॉर्ड।
逐步运行一次真实的程序。看着每次 super() 向上爬一层时 调用栈 变高 — 每个帧里都是 同一个 self — 然后每层完成时栈变矮,子类继续执行。接着 describe() 也同样向上爬。由 sys.settrace 从真实 Python 记录。
Types of Inheritance 🧩Inheritance के प्रकार 🧩继承的类型 🧩
class Car(Vehicle):class Car(Vehicle):class Car(Vehicle):Vehicle → Car → ElectricCar.Vehicle → Car → ElectricCar।Vehicle → Car → ElectricCar。Vehicle → Car, Bike, Truck.Vehicle → Car, Bike, Truck।Vehicle → Car, Bike, Truck。class HybridCar(Petrol, Electric):. Powerful but tricky — use it rarely.class HybridCar(Petrol, Electric):। ताक़तवर पर पेचीदा — कम ही इस्तेमाल करें।class HybridCar(Petrol, Electric):。强大但棘手 — 尽量少用。Arrows go from parent down to child. Multi-level is what this tutorial uses most.
Arrows parent से नीचे child तक जाते हैं। यह tutorial सबसे ज़्यादा multi-level इस्तेमाल करता है।
连线从父类向下指向子类。本教程用得最多的是多层继承。
Multiple inheritance can cause the classic "diamond problem", where two parents provide different versions of the same method. Python settles it with a fixed rule (the C3 MRO), but code becomes harder to read. In practice, prefer single inheritance plus small mixins — tiny classes that add one capability (such as LoggingMixin).
Multiple inheritance से मशहूर "diamond problem" हो सकती है, जहाँ दो parents एक ही method के अलग-अलग versions देते हैं। Python इसे एक तय नियम (C3 MRO) से सुलझाता है, पर code पढ़ना मुश्किल हो जाता है। व्यवहार में single inheritance और छोटे mixins चुनें — छोटी classes जो एक क्षमता जोड़ती हैं (जैसे LoggingMixin)।
多重继承可能导致经典的"菱形问题":两个父类为同一个方法提供了不同的版本。Python 用一条固定规则(C3 MRO)来解决,但代码会更难读。实践中,优先使用单继承加小型 mixin — 只添加一种能力的小类(例如 LoggingMixin)。
Complete Practical Example — Vehicle Hierarchy 🚗पूरा व्यावहारिक उदाहरण — Vehicle Hierarchy 🚗完整实例 — 交通工具层次结构 🚗
A full three-level chain using everything so far: inheritance, extending, overriding and super() chaining. Ready to paste and run.
अब तक की हर चीज़ इस्तेमाल करती एक पूरी तीन-स्तर chain: inheritance, extending, overriding और super() chaining। Paste करें और चलाएँ।
一条完整的三层继承链,用上了目前学过的一切:继承、扩展、重写和 super() 链式调用。可以直接粘贴运行。
class Vehicle:
"""Base class - everything on wheels shares this."""
def __init__(self, brand, wheels):
self.brand = brand
self.wheels = wheels
self.speed = 0
def start(self):
print(f"{self.brand} engine starts with a ROAR!")
def accelerate(self, amount):
self.speed += amount
print(f"{self.brand} → {self.speed} km/h")
def info(self):
return f"{self.brand} · {self.wheels} wheels · {self.speed} km/h"
class Car(Vehicle):
"""A Car is a Vehicle with doors and a fuel type."""
def __init__(self, brand, doors, fuel_type):
super().__init__(brand, wheels=4) # the parent handles brand/wheels/speed
self.doors = doors
self.fuel_type = fuel_type
def open_doors(self):
print(f"{self.brand}: opening {self.doors} doors")
def info(self):
# EXTEND the parent's info - don't rewrite it
return super().info() + f" · {self.doors} doors · {self.fuel_type}"
class ElectricCar(Car):
"""An ElectricCar is a Car powered by a battery."""
def __init__(self, brand, doors, battery_kwh):
super().__init__(brand, doors, fuel_type="electric")
self.battery_kwh = battery_kwh
self.charge_level = 100
# OVERRIDE - electric cars start silently
def start(self):
print(f"{self.brand} powers on silently — battery at {self.charge_level}%")
# A new method only on ElectricCar
def charge(self, amount):
self.charge_level = min(100, self.charge_level + amount)
print(f"{self.brand} charged to {self.charge_level}%")
def info(self):
return super().info() + f" · {self.battery_kwh} kWh · {self.charge_level}% charged"
# -- Create one of each --
honda = Car("Honda Civic", doors=4, fuel_type="petrol")
tesla = ElectricCar("Tesla Model 3", doors=4, battery_kwh=75)
# -- Start each - the override is easy to see --
honda.start() # uses Vehicle.start()
tesla.start() # uses ElectricCar.start() (override)
# -- Inherited methods work everywhere --
honda.accelerate(40) # inherited from Vehicle
tesla.accelerate(80) # inherited from Vehicle
# -- Level-specific methods --
honda.open_doors() # added in Car
tesla.charge(-10) # added in ElectricCar (a negative amount lowers the charge)
print()
print(honda.info()) # chained super().info() calls
print(tesla.info()) # chained super().info() calls, 3 levels deep
The empty line in the output comes from the bare print(). tesla.info() runs three info() methods: ElectricCar's calls Car's, which calls Vehicle's, and each adds its part on the way back.
Output की ख़ाली line ख़ाली print() से आती है। tesla.info() तीन info() methods चलाता है: ElectricCar वाला Car वाले को बुलाता है, जो Vehicle वाले को, और लौटते समय हर एक अपना हिस्सा जोड़ता है।
输出中的空行来自空的 print()。tesla.info() 运行了三个 info() 方法:ElectricCar 的调用 Car 的,Car 的再调用 Vehicle 的,返回途中每一层都加上自己的部分。
Practical Example 2 — Developers Need an Extra OTP 🔐व्यावहारिक उदाहरण 2 — Developers को एक अतिरिक्त OTP चाहिए 🔐实例 2 — 开发者需要额外的 OTP 🔐
A real-world case of "extend, don't replace". Every Employee logs in with a username and password. Developers need one more step — an OTP (one-time password) — because they can reach production systems. The child overrides auth() but reuses the parent's username/password check through super().auth(...) instead of rewriting it.
"Extend करो, replace नहीं" का असली उदाहरण। हर Employee username और password से login करता है। Developers को एक और step चाहिए — OTP (one-time password) — क्योंकि वे production systems तक पहुँच सकते हैं। Child auth() को override करता है, पर username/password की जाँच दोबारा लिखने की जगह super().auth(...) से parent वाली इस्तेमाल करता है।
一个"扩展而非替换"的真实案例。每个 Employee 用用户名和密码登录。Developer 还需要多一步 — OTP(一次性密码)— 因为他们能访问生产系统。子类重写了 auth(),但通过 super().auth(...) 复用父类的用户名/密码检查,而不是重写它。
class Employee:
"""Base auth for every employee - username + password only."""
def __init__(self, username, password):
self.username = username
self._password = password # in real code: store a hash, not plain text
def auth(self, username, password):
"""Verify username + password. Returns True on success."""
print(f" [Employee.auth] checking username & password for {username!r}...")
if username != self.username:
print(" ✗ username mismatch")
return False
if password != self._password:
print(" ✗ password mismatch")
return False
print(" ✓ base credentials OK")
return True
class Developer(Employee):
"""Developers need everything an Employee needs - PLUS an OTP."""
def __init__(self, username, password, otp_secret):
super().__init__(username, password) # let Employee handle username/password
self._otp_secret = otp_secret # developer-specific field
# OVERRIDE - same method name, one extra parameter (otp)
def auth(self, username, password, otp):
# Step 1 - DELEGATE the username/password check to the parent.
# If Employee.auth ever changes, we get the fix for free.
if not super().auth(username, password):
return False
# Step 2 - the EXTRA check that only developers need
print(f" [Developer.auth] checking OTP for {username!r}...")
if otp != self._otp_secret:
print(" ✗ OTP mismatch")
return False
print(" ✓ OTP verified")
return True
# -- Regular employee: only username + password --
alice = Employee("alice", "MyPass123!")
print("Alice logs in:")
print(" → result:", alice.auth("alice", "MyPass123!"))
print()
# -- Developer: needs username + password + OTP --
bob = Developer("bob", "DevPass456!", otp_secret="774123")
print("Bob (dev) logs in with the correct OTP:")
print(" → result:", bob.auth("bob", "DevPass456!", otp="774123"))
print()
print("Bob (dev) logs in with a WRONG OTP - parent OK, child fails:")
print(" → result:", bob.auth("bob", "DevPass456!", otp="000000"))
print()
print("Bob (dev) logs in with a WRONG password - the parent stops early:")
print(" → result:", bob.auth("bob", "wrong", otp="774123"))
Employee.auth(username, password) owns the base check. It knows nothing about OTPs. Add password hashing or logging here later, and every subclass benefits.
Employee.auth(username, password) बुनियादी जाँच का मालिक है। उसे OTP के बारे में कुछ नहीं पता। बाद में यहाँ password hashing या logging जोड़ें, हर subclass को फ़ायदा मिलेगा।
Employee.auth(username, password) 负责基础检查,它对 OTP 一无所知。以后在这里加入密码哈希或日志,所有子类都会受益。
Developer.auth(username, password, otp) takes one extra parameter. Its first step is super().auth(username, password) — reuse, don't rewrite. Only if that passes does it check the OTP.
Developer.auth(username, password, otp) एक अतिरिक्त parameter लेता है। उसका पहला step super().auth(username, password) है — दोबारा इस्तेमाल करो, दोबारा मत लिखो। वह pass हो, तभी OTP जाँचता है।
Developer.auth(username, password, otp) 多接收一个参数。它的第一步是 super().auth(username, password) — 复用,不重写。只有这一步通过,才检查 OTP。
super().auth() already returned False.
ग़लत password पर child की OTP जाँच चलती ही नहीं, क्योंकि super().auth() पहले ही False लौटा चुका है।
密码错误时,子类的 OTP 检查根本不会运行,因为 super().auth() 已经返回了 False。
🔐 Simulator: Login Lab🔐 Simulator: Login Lab🔐 模拟器:登录实验室
Choose who logs in and what they type. The two boxes show which class did which check. Try calling alice.auth with an OTP, or bob.auth without one — the override changed the method's parameters. All 54 combinations were run in real Python.
चुनें कौन login करता है और क्या type करता है। दो डिब्बे दिखाते हैं कि किस class ने कौन-सी जाँच की। alice.auth को OTP के साथ, या bob.auth को बिना OTP के बुलाकर देखें — override ने method के parameters बदल दिए। सभी 54 combinations असली Python में चलाए गए।
选择谁登录、输入什么。两个方框显示哪个类做了哪项检查。试试带 OTP 调用 alice.auth,或不带 OTP 调用 bob.auth — 重写改变了方法的参数。全部 54 种组合都在真实 Python 中运行过。
When a child needs everything the parent does plus one extra step: override the method, call super().method(...) to reuse the parent's logic, then add your extra check. Don't re-implement what the parent already does — the two versions will only drift apart.
जब child को parent का सब कुछ और एक अतिरिक्त step चाहिए: method override करें, parent का logic दोबारा इस्तेमाल करने के लिए super().method(...) बुलाएँ, फिर अपनी अतिरिक्त जाँच जोड़ें। Parent जो पहले से करता है उसे दोबारा न लिखें — दोनों versions बस अलग होते जाएँगे।
当子类需要父类的全部功能 再加 一个额外步骤时:重写该方法,调用 super().method(...) 复用父类逻辑,然后加上你的额外检查。不要重新实现父类已经做的事 — 两个版本只会越走越远。
isinstance() and issubclass() — Checking the Family 👪isinstance() और issubclass() — परिवार की जाँच 👪isinstance() 与 issubclass() — 检查家族关系 👪
Once you have a hierarchy, you often ask: "Is this object part of the family?" Python has two built-in checks, and both understand inheritance — an ElectricCar counts as a Car and as a Vehicle. (The classes are the ones from Section 08.)
Hierarchy बन जाने के बाद आप अक्सर पूछते हैं: "क्या यह object इस परिवार का है?" Python में दो built-in जाँचें हैं, और दोनों inheritance समझती हैं — ElectricCar एक Car भी गिना जाता है और Vehicle भी। (Classes वही हैं जो Section 08 में हैं।)
有了类层次之后,你经常会问:"这个对象属于这个家族吗?"Python 提供了两个内置检查,都理解继承 — ElectricCar 既算 Car,也算 Vehicle。(所用的类与第 08 节相同。)
class Vehicle:
"""Base class - everything on wheels shares this."""
def __init__(self, brand, wheels):
self.brand = brand
self.wheels = wheels
self.speed = 0
def start(self):
print(f"{self.brand} engine starts with a ROAR!")
def accelerate(self, amount):
self.speed += amount
print(f"{self.brand} → {self.speed} km/h")
def info(self):
return f"{self.brand} · {self.wheels} wheels · {self.speed} km/h"
class Car(Vehicle):
"""A Car is a Vehicle with doors and a fuel type."""
def __init__(self, brand, doors, fuel_type):
super().__init__(brand, wheels=4) # the parent handles brand/wheels/speed
self.doors = doors
self.fuel_type = fuel_type
def open_doors(self):
print(f"{self.brand}: opening {self.doors} doors")
def info(self):
# EXTEND the parent's info - don't rewrite it
return super().info() + f" · {self.doors} doors · {self.fuel_type}"
class ElectricCar(Car):
"""An ElectricCar is a Car powered by a battery."""
def __init__(self, brand, doors, battery_kwh):
super().__init__(brand, doors, fuel_type="electric")
self.battery_kwh = battery_kwh
self.charge_level = 100
# OVERRIDE - electric cars start silently
def start(self):
print(f"{self.brand} powers on silently — battery at {self.charge_level}%")
# A new method only on ElectricCar
def charge(self, amount):
self.charge_level = min(100, self.charge_level + amount)
print(f"{self.brand} charged to {self.charge_level}%")
def info(self):
return super().info() + f" · {self.battery_kwh} kWh · {self.charge_level}% charged"
tesla = ElectricCar("Tesla", 4, 75)
# -- isinstance(obj, ClassOrTuple): is this object one of these types? --
print(isinstance(tesla, ElectricCar)) # its own type
print(isinstance(tesla, Car)) # inherits from Car
print(isinstance(tesla, Vehicle)) # inherits from Vehicle
print(isinstance(tesla, object)) # everything is an object
print(isinstance(tesla, int)) # an unrelated type
# -- issubclass(Cls, ParentOrTuple): is Cls a subclass of Parent? --
print(issubclass(ElectricCar, Car))
print(issubclass(ElectricCar, Vehicle)) # walks the whole chain
print(issubclass(Car, ElectricCar)) # a parent is not a subclass of its child
# -- Practical use: accept any Vehicle --
def describe_any_vehicle(v):
if not isinstance(v, Vehicle):
raise TypeError("Expected a Vehicle (or subclass)")
return v.info()
print(describe_any_vehicle(tesla))
try:
describe_any_vehicle("a bicycle")
except TypeError as e:
print("TypeError:", e)
print(type(tesla) == Car, isinstance(tesla, Car)) # type() == rejects the subclass
Avoid type(obj) == Car — it rejects subclasses and quietly breaks inheritance (the last line above prints False for a Tesla). isinstance(obj, Car) accepts Car and every subclass, which is almost always what you want.
type(obj) == Car से बचें — यह subclasses को ठुकरा देता है और चुपचाप inheritance तोड़ देता है (ऊपर की आख़िरी line Tesla के लिए False print करती है)। isinstance(obj, Car) Car और उसकी हर subclass को मानता है — लगभग हमेशा यही चाहिए।
避免写 type(obj) == Car — 它会拒绝子类,悄悄破坏继承(上面最后一行对 Tesla 打印了 False)。isinstance(obj, Car) 接受 Car 及其所有子类,这几乎总是你想要的。
👪 Simulator: Family Checker👪 Simulator: Family Checker👪 模拟器:家族检查器
Pick an object. Compare isinstance() with type() == for five classes — highlighted rows are where they disagree. Below is the full issubclass() table. Every cell is a real Python result.
कोई object चुनें। पाँच classes के लिए isinstance() और type() == की तुलना करें — हाइलाइट rows वहाँ हैं जहाँ दोनों अलग जवाब देते हैं। नीचे पूरी issubclass() table है। हर cell असली Python नतीजा है।
选一个对象。对五个类比较 isinstance() 与 type() == — 高亮的行是两者结果不同的地方。下面是完整的 issubclass() 表。每个单元格都是真实的 Python 结果。
Common Mistakes (and Fixes) 🩹आम ग़लतियाँ (और उनके हल) 🩹常见错误(及修正)🩹
| Mistakeग़लती错误 | What goes wrongक्या बिगड़ता है会出什么问题 | Fixहल修正 |
|---|---|---|
Forgetting super().__init__() in the childChild में super().__init__() भूलना子类中忘记 super().__init__() | The parent's attributes are never set — AttributeError on first useParent के attributes कभी set नहीं होते — पहली बार इस्तेमाल पर AttributeError父类的属性从未设置 — 首次使用时 AttributeError | Call super().__init__(...), usually as the first line of the child's __init__super().__init__(...) बुलाएँ, आमतौर पर child के __init__ की पहली line में调用 super().__init__(...),通常放在子类 __init__ 的第一行 |
Overriding but forgetting super() when you meant to extendExtend करना था पर override में super() भूल गए想扩展,重写时却忘了 super() | The parent's behaviour is silently lostParent का व्यवहार चुपचाप खो जाता है父类的行为悄悄丢失 | Decide: replace (no super()) or extend (call super().method(...))तय करें: replace (super() नहीं) या extend (super().method(...) बुलाएँ)想清楚:替换(不用 super())还是扩展(调用 super().method(...)) |
Using type(obj) == Cartype(obj) == Car इस्तेमाल करना使用 type(obj) == Car | Rejects ElectricCar and every other subclassElectricCar और हर दूसरी subclass को ठुकराता है拒绝 ElectricCar 及其他所有子类 | Use isinstance(obj, Car)isinstance(obj, Car) इस्तेमाल करें使用 isinstance(obj, Car) |
| Deep chains (5+ levels)गहरी chains (5+ स्तर)过深的继承链(5 层以上) | Hard to see where a method really livesदेखना मुश्किल कि method असल में कहाँ है很难看出方法到底在哪里 | Keep trees shallow (2–3 levels); consider composition ("has-a")Trees उथले रखें (2–3 स्तर); composition ("has-a") पर विचार करें保持继承树浅(2–3 层);考虑组合("has-a") |
| Inheriting just to "share code", with no IS-Aबिना IS-A के, बस "code साझा" करने के लिए inherit करना没有 IS-A 关系,只为"共享代码"而继承 | class InvoicePrinter(FileReader): — a printer is not a file readerclass InvoicePrinter(FileReader): — printer file reader नहीं हैclass InvoicePrinter(FileReader): — 打印器不是文件读取器 | Use composition: self.reader = FileReader(...)Composition इस्तेमाल करें: self.reader = FileReader(...)使用组合:self.reader = FileReader(...) |
Naming the class in super()super() में class का नाम लिखना在 super() 里写类名 | Old style super(Car, self) is long and breaks when you rename or copy-pasteपुराना super(Car, self) लंबा है और rename या copy-paste पर टूटता है旧写法 super(Car, self) 冗长,改名或复制粘贴时容易出错 | Use bare super().__init__(...)ख़ाली super().__init__(...) इस्तेमाल करें使用不带参数的 super().__init__(...) |
| Multiple inheritance without understanding the MROMRO समझे बिना multiple inheritance不理解 MRO 就使用多重继承 | Diamond problem — one parent's method silently winsDiamond problem — एक parent का method चुपचाप जीत जाता है菱形问题 — 某个父类的方法悄悄胜出 | Prefer single inheritance + mixins; check ClassName.mro()Single inheritance + mixins चुनें; ClassName.mro() देखें优先单继承 + mixin;查看 ClassName.mro() |
🩺 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 answer.
इनमें से चार ग़लतियाँ, असली run में। बाईं ओर buggy code, दाईं ओर ठीक किया हुआ, बदली lines चिह्नित। इनमें से दो कोई error नहीं देतीं — बस ग़लत जवाब।
其中四个错误的真实运行。左边是有 bug 的代码,右边是修正后的代码,改动的行已标出。其中两个 完全不报错 — 只是给出错误结果。
Quick Reference 📋Quick Reference 📋速查表 📋
| Taskकाम任务 | SyntaxSyntax语法 | Notesनोट说明 |
|---|---|---|
| Declare inheritanceInheritance declare करना声明继承 | class Car(Vehicle): | Parent in parenthesesParentheses में parent父类写在括号里 |
| Call the parent's methodParent का method बुलाना调用父类方法 | super().method(...) | No need to name the parentParent का नाम लिखने की ज़रूरत नहीं不需要写父类名 |
Call the parent's __init__Parent का __init__ बुलाना调用父类的 __init__ | super().__init__(...) | Usually the first line of the child's __init__आमतौर पर child के __init__ की पहली line通常是子类 __init__ 的第一行 |
| Override a methodMethod override करना重写方法 | Redefine it with the same name in the childChild में उसी नाम से दोबारा define करें在子类里用相同名字重新定义 | The child's version replaces the parent'sChild का version parent वाले की जगह लेता है子类版本替换父类版本 |
| Extend a methodMethod extend करना扩展方法 | Override + call super().method(...)Override + super().method(...) बुलाएँ重写 + 调用 super().method(...) | Parent runs, then your additionParent चलता है, फिर आपका जोड़先运行父类,再运行你的追加部分 |
| Check the familyपरिवार जाँचना检查家族 | isinstance(obj, Class) | Accepts subclasses tooSubclasses को भी मानता है子类也算 |
| Check class ancestryClass की वंशावली जाँचना检查类的祖先 | issubclass(A, B) | True if A inherits from BTrue अगर A, B से inherit करता है若 A 继承自 B 则为 True |
| See the lookup orderखोज का क्रम देखना查看查找顺序 | ClassName.mro() | The list of classes Python searchesPython जिन classes को खोजता है उनकी listPython 查找的类列表 |
| Multi-level chainMulti-level chain多层继承链 | class B(A): ... class C(B): ... | Automatic — C inherits from bothअपने आप — C दोनों से inherit करता है自动 — C 同时继承两者 |
| Multiple inheritanceMultiple inheritance多重继承 | class D(A, B): | Use sparingly — prefer mixinsकम इस्तेमाल करें — mixins बेहतर谨慎使用 — 优先 mixin |
Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆
Car is a Vehicle. A SavingsAccount is a BankAccount. An InvoicePrinter is not a FileReader — use composition (self.reader = FileReader()) instead.Car एक Vehicle है। SavingsAccount एक BankAccount है। InvoicePrinter एक FileReader नहीं है — वहाँ composition (self.reader = FileReader()) इस्तेमाल करें।Car 是一种 Vehicle。SavingsAccount 是一种 BankAccount。InvoicePrinter 不是 FileReader — 应改用组合(self.reader = FileReader())。super().__init__(...) in the child's __init__ — usually as the first line. Skipping it leaves the parent's attributes unset, and the first read raises AttributeError.__init__ में super().__init__(...) बुलाएँ — आमतौर पर पहली line में। इसे छोड़ने से parent के attributes set नहीं होते, और पहली बार पढ़ने पर AttributeError आता है।__init__ 中调用 super().__init__(...) — 通常是第一行。跳过它,父类的属性就不会被设置,第一次读取就会抛出 AttributeError。super(), not super(ClassName, self). It is shorter and survives renames and copy-paste.super() इस्तेमाल करें, super(ClassName, self) नहीं। यह छोटा है और rename व copy-paste में नहीं टूटता।super(),而不是 super(ClassName, self)。更短,改名和复制粘贴时也不会出错。super(). Extending = call super().method(...) and add your logic before or after. Never lose the parent's behaviour by accident.super() नहीं। Extend = super().method(...) बुलाएँ और अपना logic पहले या बाद में जोड़ें। Parent का व्यवहार कभी ग़लती से न खोएँ।super()。扩展 = 调用 super().method(...),并在前后加上你的逻辑。绝不要意外丢掉父类的行为。isinstance(obj, Class), not type(obj) == Class. The first respects inheritance; the second breaks the moment someone adds a subclass.type(obj) == Class नहीं, isinstance(obj, Class) इस्तेमाल करें। पहला inheritance को मानता है; दूसरा किसी के subclass जोड़ते ही टूट जाता है।isinstance(obj, Class),不要用 type(obj) == Class。前者尊重继承;后者一旦有人添加子类就会出问题。YourClass.mro(); the first class in the list that defines the name wins.YourClass.mro() चलाएँ; list में जिस पहली class में नाम define है, वही जीतती है।YourClass.mro();列表中第一个定义了该名字的类胜出。LoggingMixin) without being another IS-A parent.LoggingMixin) बिना दूसरा IS-A parent बने।LoggingMixin),而不成为另一个 IS-A 父类。Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题
A child class links to its parent: it gets everything above it and adds only what is new. Python finds a name by searching the object, then each class in the MRO — the first match wins, which is why overrides work. Use super() to extend rather than replace, call super().__init__() in every child, check types with isinstance(), and inherit only when the child truly IS-A parent.
Child class अपने parent से जुड़ी है: उसे ऊपर का सब मिलता है और वह सिर्फ़ नया जोड़ती है। Python किसी नाम को पहले object में, फिर MRO की हर class में खोजता है — पहला match जीतता है, इसीलिए overrides काम करते हैं। Replace की जगह extend करने के लिए super() इस्तेमाल करें, हर child में super().__init__() बुलाएँ, types isinstance() से जाँचें, और तभी inherit करें जब child सच में parent हो (IS-A)।
子类与父类相连:它获得上面的一切,只添加新的部分。Python 查找一个名字时先查对象,再依次查 MRO 中的每个类 — 第一个匹配胜出,这正是重写能生效的原因。用 super() 扩展而不是替换,在每个子类中调用 super().__init__(),用 isinstance() 检查类型,并且只在子类真正"是一种"父类时才继承。