The Story That Explains Abstraction ☕Abstraction समझाने वाली कहानी ☕一个讲清抽象(Abstraction)的故事 ☕
Grind the beans. Measure 18 grams. Tamp with 30 pounds of force. Heat the water to 93 °C. Time the extraction to 25 seconds. Clean the group head afterwards.
All of that happens behind one button. What you use is the interface — Espresso, Latte, Cappuccino, Off. What actually runs is the implementation — pumps, sensors, heaters and timers.
Now buy a different machine — a drip coffee-maker, a French press or a capsule machine. Same idea: "press this, get coffee." Each machine brews in a completely different way, but for you they all offer the same simple action.
That is abstraction. Show what a class does; hide how it does it. Coffee machines, payment methods, database drivers and file-format handlers in software all work this way.
बीज पीसना। 18 ग्राम नापना। 30 pound के ज़ोर से दबाना। पानी 93 °C तक गर्म करना। 25 second तक extraction करना। बाद में group head साफ़ करना।
यह सब एक बटन के पीछे होता है। आप जो इस्तेमाल करते हैं वह interface है — Espresso, Latte, Cappuccino, Off। जो असल में चलता है वह implementation है — pumps, sensors, heaters और timers।
अब दूसरी machine ख़रीदिए — drip coffee-maker, French press या capsule machine। विचार वही: "यह दबाओ, coffee पाओ।" हर machine बिल्कुल अलग तरीक़े से coffee बनाती है, पर आपके लिए सब एक ही आसान काम देती हैं।
यही abstraction है। Class क्या करती है वह दिखाओ; कैसे करती है वह छिपाओ। Software में coffee machines, payment methods, database drivers और file-format handlers सब ऐसे ही काम करते हैं।
磨豆子。称 18 克。用 30 磅的力压粉。把水加热到 93 °C。把萃取时间控制在 25 秒。事后清洗冲煮头。
这一切都发生在一个按钮的 背后。你使用的是 接口(interface) — Espresso、Latte、Cappuccino、Off。真正运行的是 实现(implementation) — 水泵、传感器、加热器和计时器。
现在换一台机器 — 滴滤咖啡机、法压壶或胶囊机。道理一样:"按这里,出咖啡。" 每台机器的冲煮方式完全不同,但对你来说,它们都提供同一个简单的动作。
这就是 抽象(abstraction)。展示类 做什么,隐藏它 怎么做。软件中的咖啡机、支付方式、数据库驱动和文件格式处理器都是这样工作的。
Abstraction is one of the four pillars of OOP, together with encapsulation, inheritance and polymorphism. It says: write a contract that says what an object must do, and let each concrete class fill in the details. Python enforces this contract with Abstract Base Classes (ABC) and the @abstractmethod decorator.
Abstraction OOP के चार स्तंभों में से एक है — encapsulation, inheritance और polymorphism के साथ। यह कहता है: एक contract लिखो जो बताए कि object को क्या करना है, और हर concrete class को details भरने दो। Python यह contract Abstract Base Classes (ABC) और @abstractmethod decorator से लागू करता है।
抽象 是 OOP 四大支柱之一,另外三个是封装、继承和多态。它的意思是:写一份契约,说明对象必须做什么,让每个具体类去补充细节。Python 用 抽象基类(ABC)和 @abstractmethod 装饰器来强制执行这份契约。
Look for the interactive boxes: the ABC Gate Lab (Section 05), Abstract or Concrete? (06), the Payment Router (08), describe() Step by Step and the Shape Studio (09) and the Bug Doctor (11). Every result comes from real Python runs.
Interactive डिब्बे खोजें: ABC Gate Lab (Section 05), Abstract या Concrete? (06), Payment Router (08), describe() क़दम-दर-क़दम और Shape Studio (09) और Bug Doctor (11)। हर नतीजा असली Python runs से है।
请留意这些互动框:ABC 关卡实验室(第 05 节)、抽象还是具体?(06)、支付路由器(08)、describe() 逐步演示 和 图形工作室(09),以及 Bug 医生(11)。每个结果都来自真实的 Python 运行。
Interface vs Implementation 🎛️Interface बनाम Implementation 🎛️接口 vs 实现 🎛️
Every abstraction has two sides. The interface is the promise: "if you are a Shape, you must be able to tell me your area." The implementation is how each concrete class keeps that promise — a circle uses π·r², a rectangle uses w × h.
हर abstraction के दो पहलू होते हैं। Interface वादा है: "अगर तुम Shape हो, तो तुम्हें अपना area बताना ही होगा।" Implementation यह है कि हर concrete class वह वादा कैसे निभाती है — circle π·r² लेता है, rectangle w × h।
每个抽象都有两面。接口 是承诺:"如果你是一个 Shape,你就必须能告诉我你的面积。"实现 是每个具体类如何兑现这个承诺 — 圆用 π·r²,矩形用 w × h。
| Says what must be doneबताता है क्या करना है说明必须做 什么 |
| Declares method names and parametersMethod के नाम और parameters घोषित करता है声明方法名和参数 |
| Does not say howकैसे, यह नहीं बताता不 说明怎么做 |
| Cannot be instantiated directlyसीधे object नहीं बना सकते不能直接实例化 |
| Written once — reused by every implementationएक बार लिखा — हर implementation दोबारा इस्तेमाल करता है只写一次 — 所有实现共用 |
| Says how it is doneबताता है कैसे होता है说明 怎么 做 |
| Fills in every abstract methodहर abstract method भरता है补全每一个抽象方法 |
| Free to use any algorithm insideअंदर कोई भी algorithm लगाने की आज़ादी内部可以使用任何算法 |
| Can be instantiated and usedObject बनाकर इस्तेमाल कर सकते हैं可以实例化并使用 |
| Many concrete classes can share one interfaceकई concrete classes एक ही interface share कर सकती हैं多个具体类可以共用一个接口 |
You press one button on the interface. The six-step implementation stays hidden behind it.
आप interface का एक बटन दबाते हैं। छह क़दम वाला implementation उसके पीछे छिपा रहता है।
你只按接口上的一个按钮,六个步骤的实现都藏在它后面。
Why It Mattersयह क्यों ज़रूरी है为什么这很重要
Without abstraction, callers must know which exact class they are dealing with. With abstraction, callers depend only on the interface — and a new implementation slots in without changing a single line of caller code. This is a short sketch to compare the two styles (it is not meant to be run):
Abstraction के बिना callers को पता होना चाहिए कि वे किस class से बात कर रहे हैं। Abstraction के साथ callers सिर्फ़ interface पर निर्भर होते हैं — और नया implementation caller के code की एक भी line बदले बिना जुड़ जाता है। दोनों तरीक़ों की तुलना के लिए यह छोटा sketch है (इसे चलाने के लिए नहीं लिखा गया):
没有抽象时,调用方必须知道自己面对的是哪个具体类。有了抽象,调用方只依赖接口 — 新的实现可以直接接入,调用方代码一行都不用改。下面是比较两种写法的简短示意(不用于运行):
# Without abstraction - the caller must handle every payment type itself
def process(payment_type, data, amount):
if payment_type == "credit":
# Do card-specific stuff
...
elif payment_type == "upi":
# Do UPI-specific stuff
...
elif payment_type == "crypto":
# Do crypto-specific stuff
...
# A new payment method -> edit this function -> risk breaking callers
# WITH abstraction - the caller just talks to the interface
def process(payment: PaymentMethod, amount):
payment.authenticate()
payment.charge(amount)
# A new payment method? Just implement PaymentMethod. No changes here.
Meet ABC and @abstractmethod 🧩ABC और @abstractmethod से मिलिए 🧩认识 ABC 和 @abstractmethod 🧩
Python's abc module ("Abstract Base Classes") gives you two tools: ABC, a base class to inherit from, and @abstractmethod, a decorator that marks a method as must be overridden.
Python का abc module ("Abstract Base Classes") दो औज़ार देता है: ABC, जिससे inherit करते हैं, और @abstractmethod, एक decorator जो method को override करना ज़रूरी बताता है।
Python 的 abc 模块("抽象基类")提供两样工具:ABC,一个用来继承的基类;以及 @abstractmethod,一个把方法标记为 必须重写 的装饰器。
from abc import ABC, abstractmethod
class Shape(ABC): # inherit from ABC -> this class is abstract
# -- Abstract methods: no body needed, just a signature --
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
# Try to instantiate the abstract class directly -> error
try:
s = Shape()
except TypeError as e:
print("Blocked:", e)
The original tutorial showed Can't instantiate abstract class Shape with abstract methods area, perimeter. That is the Python 3.11-and-older wording. Since Python 3.12 it says without an implementation for abstract methods 'area', 'perimeter'. The error type — TypeError — is the same. All outputs here are real runs on Python 3.13.16.
मूल tutorial में Can't instantiate abstract class Shape with abstract methods area, perimeter दिखाया गया था। यह Python 3.11 और पुराने versions का text है। Python 3.12 से यह without an implementation for abstract methods 'area', 'perimeter' कहता है। Error का प्रकार — TypeError — वही है। यहाँ सारे outputs Python 3.13.16 पर असली runs हैं।
原教程显示的是 Can't instantiate abstract class Shape with abstract methods area, perimeter。这是 Python 3.11 及更早版本的措辞。从 Python 3.12 起,它显示 without an implementation for abstract methods 'area', 'perimeter'。错误类型 — TypeError — 没变。这里所有输出都是在 Python 3.13.16 上真实运行的结果。
A class that inherits from ABC and has at least one @abstractmethod is abstract. An abstract class cannot be instantiated until every abstract method has a real implementation. Those two rules are the whole enforcement system.
जो class ABC से inherit करती है और जिसमें कम से कम एक @abstractmethod है, वह abstract है। Abstract class का object नहीं बन सकता जब तक हर abstract method का असली implementation न हो। पूरा नियंत्रण इन्हीं दो नियमों से चलता है।
继承自 ABC 并且至少有一个 @abstractmethod 的类就是抽象类。在每个抽象方法都有真正的实现之前,抽象类 不能被实例化。整个强制机制就是这两条规则。
The Contract and Its Implementations 📜Contract और उसके Implementations 📜契约与它的实现 📜
One abstract Shape contract sits at the top. It declares area() and perimeter() as promises. Three concrete classes below — Circle, Rectangle, Triangle — each keep the promise in their own way. The interface is the same; the implementations differ.
ऊपर एक abstract Shape contract है। यह area() और perimeter() को वादों के रूप में घोषित करता है। नीचे तीन concrete classes — Circle, Rectangle, Triangle — अपने-अपने तरीक़े से वादा निभाती हैं। Interface एक जैसा है; implementations अलग।
顶部是一个抽象的 Shape 契约,它把 area() 和 perimeter() 声明为承诺。下面三个具体类 — Circle、Rectangle、Triangle — 各自用自己的方式兑现承诺。接口相同,实现不同。
The same two method names in every subclass, with three different formulas underneath. That is abstraction.
हर subclass में वही दो method नाम, और नीचे तीन अलग formulas। यही abstraction है।
每个子类都有同样的两个方法名,底下却是三种不同的公式。这就是抽象。
Abstract Classes Cannot Be Instantiated 🚫Abstract Class का Object नहीं बनता 🚫抽象类不能被实例化 🚫
This guarantee is what makes abstract classes useful. Python refuses to create an object of an abstract class until every abstract method has a real implementation. If you try, you get a TypeError at creation time — before any of your methods can run.
यही guarantee abstract classes को काम का बनाती है। जब तक हर abstract method का असली implementation न हो, Python abstract class का object बनाने से मना कर देता है। कोशिश करें तो object बनते समय ही TypeError मिलता है — आपका कोई method चलने से पहले।
这个保证正是抽象类有用的原因。在每个抽象方法都有真正的实现之前,Python 拒绝 创建抽象类的对象。如果你尝试,在创建时就会得到 TypeError — 在你的任何方法运行之前。
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
# -- Attempt 1: instantiate the abstract class directly -----
try:
s = Shape()
except TypeError as e:
print("Blocked:", e)
# -- Attempt 2: subclass but forget one abstract method -----
class HalfCircle(Shape):
def area(self):
return 42
# perimeter() is still abstract - HalfCircle is still abstract too
try:
h = HalfCircle()
except TypeError as e:
print("Blocked:", e)
# -- Attempt 3: fully concrete - works ----------------------
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side ** 2
def perimeter(self):
return 4 * self.side
sq = Square(5) # works - every abstract method is implemented
print(sq.area(), sq.perimeter()) # 25 20
Every creation passes the ABC gate. If any abstract method is still missing, the object is never created.
हर object बनाने की कोशिश ABC gate से गुज़रती है। कोई abstract method बाक़ी हो तो object कभी नहीं बनता।
每次创建对象都要经过 ABC 关卡。只要还缺一个抽象方法,对象就不会被创建。
🧪 Simulator: ABC Gate Lab🧪 Simulator: ABC Gate Lab🧪 模拟器:ABC 关卡实验室
Switch the pieces on and off. Remove (ABC), remove an @abstractmethod, forget a method in Square — then watch the gate. The code and output are checked against real Python for all 64 combinations.
हिस्सों को on/off कीजिए। (ABC) हटाइए, कोई @abstractmethod हटाइए, Square में कोई method भूल जाइए — फिर gate देखिए। Code और output सभी 64 combinations के लिए असली Python से जाँचे गए हैं।
把各个部分打开或关闭。去掉 (ABC)、去掉某个 @abstractmethod、在 Square 里漏掉一个方法 — 然后看关卡的反应。全部 64 种组合的代码和输出都已与真实 Python 对照。
Abstract and Concrete Methods Together 🤝Abstract और Concrete Methods साथ-साथ 🤝抽象方法与具体方法并存 🤝
An abstract class is not only abstract methods. It can also hold fully written methods that every subclass inherits for free. This is how you share common behaviour and still enforce the contract.
Abstract class में सिर्फ़ abstract methods नहीं होते। उसमें पूरे लिखे हुए methods भी हो सकते हैं, जो हर subclass को मुफ़्त में inherit होते हैं। इसी तरह आप साझा व्यवहार बाँटते हैं और contract भी लागू रखते हैं।
抽象类里不 只 有抽象方法。它也可以包含完整写好的方法,每个子类都能免费继承。这样你既能共享通用行为,又能强制执行契约。
Below, PaymentMethod has a concrete __init__ and log_transaction, plus two abstract methods. The last three lines (added so you can see real output) use the CreditCard subclass.
नीचे PaymentMethod में concrete __init__ और log_transaction हैं, साथ में दो abstract methods। आख़िरी तीन lines (ताकि आप असली output देख सकें) CreditCard subclass इस्तेमाल करती हैं।
下面的 PaymentMethod 有具体的 __init__ 和 log_transaction,外加两个抽象方法。最后三行(为了让你看到真实输出而添加)使用了 CreditCard 子类。
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
def __init__(self, holder):
self.holder = holder # concrete - shared setup
# -- ABSTRACT - every subclass must implement --
@abstractmethod
def authenticate(self):
pass
@abstractmethod
def charge(self, amount):
pass
# -- CONCRETE - shared, but subclasses can still override --
def log_transaction(self, action, amount):
print(f" [log] {self.holder}: {action} of ${amount}")
class CreditCard(PaymentMethod):
def __init__(self, holder, number):
super().__init__(holder) # reuse the base setup
self.number = number
def authenticate(self):
print(f"Verifying card ****{self.number[-4:]}")
return True
def charge(self, amount):
print(f"Charging ${amount} to card")
self.log_transaction("charge", amount) # inherited from base - no duplication
card = CreditCard("Alice", "4111111111113456")
if card.authenticate():
card.charge(500)
Mark a method abstract when every subclass really does it differently — the algorithm itself changes. Keep a method concrete when it would be copied unchanged into every subclass. You get enforcement where it matters and reuse where it is free.
Method को abstract तब बनाइए जब हर subclass उसे सच में अलग तरीक़े से करे — algorithm ही बदल जाए। Method को concrete तब रखिए जब वह हर subclass में बिना बदले copy होता। ज़रूरी जगह पर नियंत्रण, और बाक़ी जगह मुफ़्त reuse।
当每个子类的做法确实不同 — 算法本身会变 — 时,把方法标为 抽象。当方法会被原样复制到每个子类时,把它保留为 具体 方法。该强制的地方强制,能复用的地方免费复用。
🧠 Simulator: Abstract or Concrete?🧠 Simulator: Abstract या Concrete?🧠 模拟器:抽象还是具体?
Eight methods from this tutorial. For each one, ask: "Would every subclass write this exactly the same way?" Yes → concrete. No → abstract.
इस tutorial के आठ methods। हर एक के लिए पूछिए: "क्या हर subclass इसे बिल्कुल एक जैसा लिखेगी?" हाँ → concrete। नहीं → abstract।
本教程中的八个方法。对每一个问一句:"每个子类都会用完全一样的方式写它吗?" 是 → 具体。否 → 抽象。
Polymorphism Through Abstraction 🔀Abstraction के ज़रिए Polymorphism 🔀通过抽象实现多态 🔀
This is where abstraction pays off. One function, process(payment, amount), accepts any object that follows the PaymentMethod contract. Three different objects go through it. Each one runs its own charge() — same interface, different behaviour, no if/else.
यहीं abstraction का फ़ायदा मिलता है। एक function, process(payment, amount), ऐसा कोई भी object लेता है जो PaymentMethod contract मानता हो। तीन अलग objects इससे गुज़रते हैं। हर एक अपना charge() चलाता है — वही interface, अलग व्यवहार, कोई if/else नहीं।
这正是抽象的回报。一个函数 process(payment, amount) 接受 任何 遵守 PaymentMethod 契约的对象。三个不同的对象经过它,每个都运行 自己的 charge() — 接口相同,行为不同,没有 if/else。
process() calls charge() once. Each object runs its own version. A fourth payment type means a new class — process() does not change.
process() एक बार charge() बुलाता है। हर object अपना version चलाता है। चौथा payment type = नई class — process() नहीं बदलता।
process() 只调用一次 charge(),每个对象运行自己的版本。第四种支付方式只需一个新类 — process() 不用改。
Practical Example 1 — Payment Gateway 💳Practical Example 1 — Payment Gateway 💳实战示例 1 — 支付网关 💳
Three payment methods follow one abstract contract. The processor function does not know — or care — which one it holds.
तीन payment methods एक ही abstract contract मानते हैं। Processor function को पता नहीं — न परवाह — कि उसके पास कौन-सा है।
三种支付方式遵守同一份抽象契约。处理函数不知道 — 也不关心 — 自己拿到的是哪一种。
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
"""Abstract base for every payment method."""
def __init__(self, holder):
self.holder = holder
# == ABSTRACT - every payment method must implement =======
@abstractmethod
def authenticate(self) -> bool:
"""Verify the payer's identity."""
...
@abstractmethod
def charge(self, amount) -> None:
"""Deduct the amount from the payer."""
...
@abstractmethod
def refund(self, amount) -> None:
"""Return the amount to the payer."""
...
# == CONCRETE - inherited by every payment method =========
def log(self, action, amount):
print(f" [log] {self.holder}: {action} of {amount}")
class CreditCard(PaymentMethod):
def __init__(self, holder, card_number):
super().__init__(holder)
self.card_number = card_number
def authenticate(self):
print(f"Verifying card ****{self.card_number[-4:]}")
return True
def charge(self, amount):
print(f"Card charged ${amount}")
self.log("card charge", amount)
def refund(self, amount):
print(f"Card refunded ${amount}")
self.log("card refund", amount)
class UPIPayment(PaymentMethod):
def __init__(self, holder, upi_id):
super().__init__(holder)
self.upi_id = upi_id
def authenticate(self):
print(f"UPI PIN check for {self.upi_id}")
return True
def charge(self, amount):
print(f"Debiting ₹{amount} via UPI")
self.log("UPI charge", amount)
def refund(self, amount):
print(f"Refunding ₹{amount} to UPI")
self.log("UPI refund", amount)
class CryptoWallet(PaymentMethod):
def __init__(self, holder, wallet_address):
super().__init__(holder)
self.wallet_address = wallet_address
def authenticate(self):
print("Verifying wallet signature")
return True
def charge(self, amount):
print(f"Broadcasting {amount} to blockchain")
self.log("crypto charge", amount)
def refund(self, amount):
print(f"Broadcasting refund of {amount}")
self.log("crypto refund", amount)
# == Generic processor - does NOT know which method it holds ==
def process_purchase(payment: PaymentMethod, amount):
print(f"\n▶ Processing for {payment.holder}:")
if payment.authenticate():
payment.charge(amount)
# One list of different concrete types, one uniform loop
payments = [
CreditCard("Alice", "4111111111113456"),
UPIPayment("Bob", "bob@upi"),
CryptoWallet("Chandra", "0xabc123"),
]
for p in payments:
process_purchase(p, 500)
# Trying to instantiate the abstract base is blocked
try:
PaymentMethod("Anyone")
except TypeError as e:
print("\nBlocked:", e)
process_purchase has no if payment_type == anywhere. It receives "some PaymentMethod" and trusts the interface. Adding ApplePay next month means writing one new subclass and adding it to the list — nothing else changes. Try exactly that in the simulator below.
process_purchase में कहीं भी if payment_type == नहीं है। उसे "कोई PaymentMethod" मिलता है और वह interface पर भरोसा करता है। अगले महीने ApplePay जोड़ना = एक नई subclass लिखना और list में डालना — और कुछ नहीं बदलता। नीचे simulator में ठीक यही आज़माइए।
process_purchase 里任何地方都没有 if payment_type ==。它收到"某个 PaymentMethod",并信任接口。下个月加上 ApplePay,只需写一个新子类并放进列表 — 其他都不用改。在下面的模拟器里亲自试试。
💳 Simulator: Payment Router💳 Simulator: Payment Router💳 模拟器:支付路由器
Pick an object, an action and an amount. Watch which methods run, in order: the subclass's own versions, and the shared log() from the abstract base. ApplePay is a brand-new subclass — process_purchase was not touched.
Object, action और amount चुनिए। देखिए कौन-से methods किस क्रम में चलते हैं: subclass के अपने versions, और abstract base का साझा log()। ApplePay बिल्कुल नई subclass है — process_purchase को छुआ तक नहीं गया।
选择一个对象、一个操作和一个金额。观察哪些方法按什么顺序运行:子类自己的版本,以及抽象基类中共享的 log()。ApplePay 是全新的子类 — process_purchase 一点没改。
Practical Example 2 — Shape Hierarchy 📐Practical Example 2 — Shape Hierarchy 📐实战示例 2 — 图形层次结构 📐
Here the abstract Shape also has a concrete describe(). It is written once, in the base class — yet it prints the right numbers for every shape, because it calls self.area() and self.perimeter(), and those run each subclass's own version.
यहाँ abstract Shape में एक concrete describe() भी है। यह base class में सिर्फ़ एक बार लिखा है — फिर भी हर shape के सही नंबर छापता है, क्योंकि यह self.area() और self.perimeter() बुलाता है, और वे हर subclass का अपना version चलाते हैं।
这里的抽象类 Shape 还有一个具体方法 describe()。它只在基类里写了一次 — 却能为每种图形打印正确的数字,因为它调用 self.area() 和 self.perimeter(),而这两个方法运行的是各个子类自己的版本。
from abc import ABC, abstractmethod
import math
class Shape(ABC):
@abstractmethod
def area(self):
...
@abstractmethod
def perimeter(self):
...
# Concrete - shared by every shape
def describe(self):
name = type(self).__name__
print(f"{name:10s} area={self.area():7.2f} perimeter={self.perimeter():7.2f}")
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def perimeter(self):
return 2 * math.pi * self.radius
class Rectangle(Shape):
def __init__(self, w, h):
self.w, self.h = w, h
def area(self):
return self.w * self.h
def perimeter(self):
return 2 * (self.w + self.h)
class Triangle(Shape):
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def area(self):
s = (self.a + self.b + self.c) / 2
return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))
def perimeter(self):
return self.a + self.b + self.c
shapes: list[Shape] = [
Circle(5),
Rectangle(4, 6),
Triangle(3, 4, 5),
]
for s in shapes:
s.describe() # inherited concrete method, uses each shape's own area/perimeter
👣 Simulator: describe() Step by Step👣 Simulator: describe() क़दम-दर-क़दम👣 模拟器:describe() 逐步演示
A shorter version of the code, recorded with sys.settrace. Watch the call stack: Shape.describe() (the shared concrete method) calls down into Circle.area() or Rectangle.area(). The abstract Shape.area() never runs.
Code का छोटा version, sys.settrace से रिकॉर्ड किया हुआ। Call stack देखिए: Shape.describe() (साझा concrete method) नीचे Circle.area() या Rectangle.area() को बुलाता है। Abstract Shape.area() कभी नहीं चलता।
这是代码的精简版,用 sys.settrace 录制。观察调用栈:Shape.describe()(共享的具体方法)向下调用 Circle.area() 或 Rectangle.area()。抽象的 Shape.area() 从未运行。
📐 Simulator: Shape Studio📐 Simulator: Shape Studio📐 模拟器:图形工作室
Change the sizes and watch the inherited describe() line update. Try a triangle whose sides cannot meet, such as 2, 3, 9 — the subclass's area() fails, not the contract. Checked against Python on 320 shapes.
Sizes बदलिए और inherited describe() की line बदलते देखिए। ऐसा triangle आज़माइए जिसकी भुजाएँ मिल न सकें, जैसे 2, 3, 9 — subclass का area() fail होता है, contract नहीं। 320 shapes पर Python से जाँचा गया।
改变尺寸,看继承来的 describe() 输出如何变化。试试三边无法围成三角形的情况,比如 2、3、9 — 出错的是子类的 area(),而不是契约。已在 320 个图形上与 Python 对照。
Abstraction vs Encapsulation ⚖️Abstraction बनाम Encapsulation ⚖️抽象 vs 封装 ⚖️
Beginners often mix these two up. Both are about "hiding", but they hide different things.
Beginners अक्सर इन दोनों को मिला देते हैं। दोनों "छिपाने" के बारे में हैं, पर अलग-अलग चीज़ें छिपाते हैं।
初学者经常把这两个概念搞混。两者都与"隐藏"有关,但隐藏的东西不同。
| Hides complexity: what a class does vs howजटिलता छिपाता है: class क्या करती है बनाम कैसे隐藏 复杂性:类做什么 vs 怎么做 |
Tool: ABC + @abstractmethodऔज़ार: ABC + @abstractmethod工具:ABC + @abstractmethod |
| Answers: "which methods must every X have?"जवाब देता है: "हर X में कौन-से methods होने ही चाहिए?"回答:"每个 X 必须有哪些方法?" |
| Enforced at the class-design levelClass design के स्तर पर लागू在类设计层面强制 |
Example: Shape declares area(); Circle implements itउदाहरण: Shape area() घोषित करता है; Circle उसे लिखता है例子:Shape 声明 area(),Circle 实现它 |
| Hides data: private state behind a public APIData छिपाता है: public API के पीछे private state隐藏 数据:把私有状态藏在公共 API 后面 |
Tool: _x, __x naming + @propertyऔज़ार: _x, __x naming + @property工具:_x、__x 命名 + @property |
| Answers: "who can read or change this attribute?"जवाब देता है: "इस attribute को कौन पढ़ या बदल सकता है?"回答:"谁能读取或修改这个属性?" |
| Enforced at the attribute-access levelAttribute access के स्तर पर लागू在属性访问层面强制 |
Example: _balance changes only through a checked deposit()उदाहरण: _balance सिर्फ़ जाँच वाले deposit() से बदलता है例子:_balance 只能通过带检查的 deposit() 修改 |
A well-designed class usually uses both. Abstraction sets the class's contract with the outside world (the public methods every subclass must write). Encapsulation hides its data (private attributes reached only through those methods). One controls the shape of your class; the other controls the doors and locks.
अच्छी तरह design की गई class आमतौर पर दोनों इस्तेमाल करती है। Abstraction बाहरी दुनिया से class का contract तय करता है (वे public methods जो हर subclass को लिखने ही हैं)। Encapsulation उसका data छिपाता है (private attributes जिन तक सिर्फ़ उन्हीं methods से पहुँचा जाता है)। एक class का आकार तय करता है; दूसरा दरवाज़े और ताले।
设计良好的类通常 两者 并用。抽象 规定类与外界的契约(每个子类必须编写的公共方法)。封装 隐藏它的数据(只能通过这些方法访问的私有属性)。一个控制类的 形状,另一个控制 门和锁。
Common Mistakes (and Fixes) 🩹आम ग़लतियाँ (और उनके हल) 🩹常见错误(及修正)🩹
| Mistakeग़लती错误 | What goes wrongक्या बिगड़ता है会出什么问题 | Fixहल修正 |
|---|---|---|
Forgetting to inherit from ABCABC से inherit करना भूलना忘记继承 ABC | @abstractmethod is silently ignored — the class can be instantiated@abstractmethod चुपचाप अनदेखा — class का object बन जाता है@abstractmethod 被悄悄忽略 — 这个类可以被实例化 | Make sure the base class inherits from abc.ABCपक्का करें कि base class abc.ABC से inherit करे确保基类继承自 abc.ABC |
Forgetting the @abstractmethod decorator@abstractmethod decorator भूलना忘记写 @abstractmethod 装饰器 | It is just a normal method; subclasses are not forced to override itयह बस सामान्य method है; subclasses पर override करने का दबाव नहीं它只是普通方法,子类不会被强制重写 | Add @abstractmethod above every method that must be overriddenहर उस method के ऊपर @abstractmethod लगाएँ जिसे override करना ज़रूरी है在每个必须重写的方法上方加 @abstractmethod |
| Trying to instantiate the abstract baseAbstract base का object बनाने की कोशिश试图实例化抽象基类 | TypeError at creation — often a surprise in test codeबनाते समय TypeError — test code में अक्सर चौंकाता है创建时 TypeError — 在测试代码中常常让人意外 | Never call PaymentMethod(...); create a concrete subclassPaymentMethod(...) कभी न बुलाएँ; concrete subclass का object बनाएँ永远不要调用 PaymentMethod(...);去实例化具体子类 |
| Missing one abstract method in a subclassSubclass में एक abstract method छूट जाना子类漏掉一个抽象方法 | The subclass is still abstract; Subclass() raises TypeErrorSubclass अब भी abstract है; Subclass() पर TypeError子类仍是抽象的;Subclass() 抛出 TypeError | Write every abstract method — or keep the subclass abstract on purposeहर abstract method लिखें — या subclass को जान-बूझकर abstract रखें实现 每一个 抽象方法 — 或者有意让子类保持抽象 |
| Changing an abstract method's parametersAbstract method के parameters बदलना修改抽象方法的参数 | Python does not check parameters, so the class is accepted — the call fails later with TypeErrorPython parameters नहीं जाँचता, इसलिए class मान ली जाती है — call बाद में TypeError से fail होती हैPython 不检查参数,所以这个类能通过 — 之后调用时才以 TypeError 失败 | Keep the same parameters. If new ones are needed, give them defaultsParameters वही रखें। नए चाहिए तो उन्हें default दें保持参数一致。需要新参数时给它默认值 |
| Using an abstract class for just one implementationसिर्फ़ एक implementation के लिए abstract class只有一个实现却用抽象类 | Extra code with no benefit — one child, one boilerplate parentबिना फ़ायदे का अतिरिक्त code — एक child, एक खोखला parent多写代码却没好处 — 一个子类配一个样板父类 | Add an abstract base only when you have (or truly expect) two or more implementationsAbstract base तभी जोड़ें जब दो या ज़्यादा implementations हों (या सच में आने वाले हों)只有当你有(或确实预期会有)两个以上实现时才引入抽象基类 |
Treating @abstractmethod as a "TODO" marker@abstractmethod को "TODO" निशान समझना把 @abstractmethod 当作"待办"标记 | No subclass can be created until it fills the method in — half-finished work blocks everyoneजब तक method न भरा जाए कोई subclass नहीं बन सकती — अधूरा काम सबको रोक देता है在补上这个方法之前,任何子类都无法创建 — 半成品会阻塞所有人 | Use it as a design signal: "every subclass must provide this"इसे design संकेत की तरह इस्तेमाल करें: "हर subclass को यह देना ही है"把它当作设计信号:"每个子类都必须提供这个" |
🩺 Simulator: Bug Doctor🩺 Simulator: Bug Doctor🩺 模拟器:Bug 医生
Five of these mistakes, run for real. Buggy code on the left, fixed code on the right, changed lines marked. Notice that two bugs give no error at all — only a quiet None.
इनमें से पाँच ग़लतियाँ, असली run में। बाईं ओर buggy code, दाईं ओर ठीक किया हुआ, बदली lines चिह्नित। ध्यान दें: दो bugs में कोई error नहीं आता — बस चुपचाप None।
其中五个错误的真实运行。左边是有 bug 的代码,右边是修正后的代码,改动的行已标出。注意有两个 bug 完全不报错 — 只是悄悄返回 None。
Quick Reference 📋Quick Reference 📋速查表 📋
| Taskकाम任务 | SyntaxSyntax语法 | Notesनोट说明 |
|---|---|---|
| Import ABC supportABC support import करना导入 ABC 支持 | from abc import ABC, abstractmethod | Standard library — nothing to installStandard library — कुछ install नहीं करना标准库 — 无需安装 |
| Declare an abstract classAbstract class घोषित करना声明抽象类 | class Shape(ABC): | Inherits from ABCABC से inherit करती है继承自 ABC |
| Declare an abstract methodAbstract method घोषित करना声明抽象方法 | @abstractmethod + def m(self): ... | Body can be pass or ...Body pass या ... हो सकता है方法体可以是 pass 或 ... |
| Concrete method in an ABCABC में concrete methodABC 中的具体方法 | Define it normally — no decoratorसामान्य तरह लिखें — कोई decorator नहीं正常定义 — 不加装饰器 | Inherited by every subclassहर subclass को inherit होता है每个子类都会继承 |
| Implement in a subclassSubclass में लिखना在子类中实现 | Override with the same parametersवही parameters रखकर override करें用相同参数重写 | All abstract methods must be presentसारे abstract methods होने चाहिए所有抽象方法都必须实现 |
| Create a concrete objectConcrete object बनाना创建具体对象 | obj = ConcreteClass(...) | Works once every abstract method is writtenहर abstract method लिखे जाने के बाद चलता है每个抽象方法都写好后才能成功 |
| See what is still abstractक्या अब भी abstract है, देखना查看哪些仍是抽象的 | ClassName.__abstractmethods__ | A frozenset of unwritten names (empty when concrete)न लिखे गए नामों का frozenset (concrete हो तो ख़ाली)未实现名字的 frozenset(具体类为空) |
| Type-hint against the interfaceInterface के हिसाब से type hint按接口写类型提示 | def f(p: PaymentMethod): | Callers can pass any subclassCallers कोई भी subclass दे सकते हैं调用方可以传入任何子类 |
| Common patternआम pattern常见模式 | A concrete method calls abstract methodsConcrete method abstract methods बुलाता है具体方法调用抽象方法 | Template Method pattern — like describe()Template Method pattern — जैसे describe()模板方法模式 — 比如 describe() |
The __abstractmethods__ row in action (sorted, because a frozenset has no fixed order):
__abstractmethods__ वाली row असल में (sorted, क्योंकि frozenset का कोई तय क्रम नहीं होता):
__abstractmethods__ 这一行的实际效果(用 sorted 排序,因为 frozenset 没有固定顺序):
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self): ...
@abstractmethod
def perimeter(self): ...
class HalfCircle(Shape):
def area(self):
return 42
class Square(Shape):
def area(self):
return 25
def perimeter(self):
return 20
print(sorted(Shape.__abstractmethods__))
print(sorted(HalfCircle.__abstractmethods__))
print(Square.__abstractmethods__)
Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆
abc.ABC and decorate at least one method with @abstractmethod. You need both — either one alone does not block instantiation.abc.ABC से inherit करें और कम से कम एक method पर @abstractmethod लगाएँ। दोनों चाहिए — अकेला कोई भी object बनना नहीं रोकता।abc.ABC,并给至少一个方法加上 @abstractmethod。两者缺一不可 — 只有其中一个都拦不住实例化。TypeError. That is the enforcement — and the whole point.TypeError आता है। यही नियंत्रण है — और पूरा मक़सद भी।TypeError。这就是强制机制 — 也是全部意义所在。log(), describe() or setup. Reuse what you can; make abstract only what really differs.log(), describe() या setup। जो हो सके reuse करें; abstract सिर्फ़ वह बनाएँ जो सच में अलग है।log()、describe() 或初始化。能复用就复用;只把真正不同的部分设为抽象。def process(p: PaymentMethod), not def process(p: CreditCard). Any subclass then slots in — polymorphism through abstraction.def process(p: PaymentMethod), न कि def process(p: CreditCard)। तब कोई भी subclass फ़िट हो जाती है — abstraction के ज़रिए polymorphism।def process(p: PaymentMethod),而不是 def process(p: CreditCard)。这样任何子类都能接入 — 通过抽象实现多态。@abstractmethod.@abstractmethod लगाएँ।@abstractmethod。Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题
Abstraction separates what from how. Inherit from ABC and mark the must-have methods with @abstractmethod: Python then refuses to create any object until every one of them is written. Put shared behaviour in concrete methods of the base, and write callers against the abstract type — new subclasses then slot in without changing them.
Abstraction क्या को कैसे से अलग करता है। ABC से inherit करें और ज़रूरी methods पर @abstractmethod लगाएँ: तब Python तब तक कोई object नहीं बनने देता जब तक हर एक लिखा न जाए। साझा व्यवहार base के concrete methods में रखें, और callers abstract type के हिसाब से लिखें — नई subclasses उन्हें बदले बिना जुड़ जाती हैं।
抽象把 做什么 和 怎么做 分开。继承 ABC,用 @abstractmethod 标记必须有的方法:在它们全部实现之前,Python 拒绝创建任何对象。把共享行为放在基类的具体方法里,按抽象类型编写调用方 — 新子类无需改动调用方就能接入。