Intermediate Python 📂 Class and Object · 4 of 10 212 min read

Python OOP — Method Overriding, Static Methods & Static Variables

Master the three tools that make Python class hierarchies flexible and reusable: static variables (class-level constants shared by every instance and subclass), static methods (@staticmethod pure utility functions), and method overriding (subclasses replacing parent behaviour to enable polymorphism). Learn through a coffee-shop-with-three-signature-drinks analogy, three animated SVG diagrams, and a full Espresso/Latte/Cappuccino example that uses all three together.

🌐
SectionSection第 01 节

The Story That Ties These Three Ideas Together ☕इन तीन विचारों को जोड़ने वाली कहानी ☕把三个概念串起来的故事 ☕

The Coffee Shop With Three Signature Drinksतीन ख़ास Drinks वाली Coffee Shop有三款招牌饮品的咖啡店
Step into any branch of a coffee-shop chain. Three things live there together.

On the wall: posted rules — STANDARD_CUP = 250 ml, MAX_BREW_TEMP = 95°C, TAX_RATE = 10%. The same in every branch. Nobody edits them. These are static variables.

Behind the counter: a thermometer, a scale, a stopwatch, a calculator. Anyone can grab one. They do not care whose drink is being made. Pure tools. These are static methods.

At the espresso machine: three signature drinks — Espresso, Latte, Cappuccino. Every barista knows a general prepare() step, but each drink is prepared completely differently. For a Latte, "prepare" does not mean "grind and extract for 25 seconds" — it means "steam milk to 65°C and pour". Same call, different result. That is method overriding.

Wire all three together and you get a family of classes that shares what should be shared and customises what should be customised.
किसी coffee-shop chain की किसी भी branch में जाइए। वहाँ तीन चीज़ें साथ रहती हैं।

दीवार पर: लगे हुए नियम — STANDARD_CUP = 250 ml, MAX_BREW_TEMP = 95°C, TAX_RATE = 10%। हर branch में एक जैसे। कोई इन्हें नहीं बदलता। ये static variables हैं।

Counter के पीछे: thermometer, तराज़ू, stopwatch, calculator। कोई भी उठा सकता है। उन्हें फ़र्क़ नहीं पड़ता कि drink किसका बन रहा है। शुद्ध औज़ार। ये static methods हैं।

Espresso machine पर: तीन ख़ास drinks — Espresso, Latte, Cappuccino। हर barista एक सामान्य prepare() step जानता है, पर हर drink बिल्कुल अलग तरह से बनता है। Latte के लिए "prepare" का मतलब "पीसो और 25 seconds extract करो" नहीं — इसका मतलब है "दूध को 65°C तक भाप दो और डालो"। एक ही call, अलग नतीजा। यही method overriding है।

तीनों को जोड़ दें तो classes का एक ऐसा परिवार मिलता है जो साझा चीज़ें साझा करता है और अलग चीज़ें अपने हिसाब से बदलता है।
走进任何一家咖啡连锁店。那里同时存在三样东西。

墙上:张贴的规则 — STANDARD_CUP = 250 ml,MAX_BREW_TEMP = 95°C,TAX_RATE = 10%。每家分店都一样,没人修改。这些是 静态变量。

柜台后面:温度计、秤、秒表、计算器。谁都可以拿来用,它们不关心在做谁的饮品。纯粹的工具。这些是 静态方法。

咖啡机前:三款招牌饮品 — 浓缩咖啡、拿铁、卡布奇诺。每位咖啡师都知道一个通用的 prepare() 步骤,但每款饮品的做法 完全不同。对拿铁来说,"prepare"不是"研磨并萃取 25 秒",而是"把牛奶蒸到 65°C 再倒入"。同样的调用,不同的结果。这就是 方法重写。

把三者结合起来,你就得到一个类家族:该共享的 共享,该定制的 定制。
🌟
The Three Tools in One Sentenceतीनों Tools एक वाक्य में一句话说清三个工具

Static variables = one copy of a constant, shared by every instance. Static methods = plain functions that live inside the class. Method overriding = a child class redefining a parent's method to change its behaviour. Together they turn one rigid class into a flexible family of classes.

Static variables = एक constant की एक copy, हर instance साझा करता है। Static methods = class के अंदर रहने वाले सादे functions। Method overriding = child class parent के method को दोबारा define करके उसका व्यवहार बदलती है। तीनों मिलकर एक सख़्त class को classes के लचीले परिवार में बदल देते हैं।

静态变量 = 一个常量只有一份,所有实例共享。静态方法 = 住在类里面的普通函数。方法重写 = 子类重新定义父类的方法来改变其行为。三者结合,把一个死板的类变成灵活的类家族。

🏷️
A Note on the Name "Static Variable""Static Variable" नाम पर एक नोट关于"静态变量"这个名字

The Python documentation calls these class variables (or class attributes). "Static variable" is the name used in languages such as Java and C++. Both names mean the same thing here: a value defined directly in the class body. Python does not stop anyone from changing them — UPPER_SNAKE_CASE is only a signal to other programmers.

Python documentation इन्हें class variables (या class attributes) कहती है। "Static variable" Java और C++ जैसी languages का नाम है। यहाँ दोनों नामों का एक ही मतलब है: class body में सीधे लिखी value। Python इन्हें बदलने से किसी को नहीं रोकता — UPPER_SNAKE_CASE सिर्फ़ दूसरे programmers के लिए एक संकेत है।

Python 官方文档把它们称为 类变量(或类属性)。"静态变量"是 Java、C++ 等语言的叫法。在这里两个名字指同一件事:直接写在类体中的值。Python 并不阻止任何人修改它们 — UPPER_SNAKE_CASE 只是给其他程序员的一个信号。

▶️
This Tutorial Has Five Simulators and a Quizइस Tutorial में पाँच Simulators और एक Quiz है本教程有五个模拟器和一个小测验

Look for the interactive boxes: the Shadow Lab (Section 03), the Static Method Machine (04), One Call, Many Drinks (05), Replace vs Extend (06) and the Bug Doctor (09). Every result comes from real Python runs.

Interactive डिब्बे खोजें: Shadow Lab (Section 03), Static Method Machine (04), एक Call, कई Drinks (05), Replace बनाम Extend (06) और Bug Doctor (09)। हर नतीजा असली Python runs से है।

请留意这些互动框:遮蔽实验室(第 03 节)、静态方法机器(04)、一次调用,多种饮品(05)、替换与扩展(06)和 Bug 医生(09)。每个结果都来自真实的 Python 运行。


SectionSection第 02 节

Meet All Three at a Glance 👀तीनों से एक नज़र में मिलिए 👀一眼认识三者 👀

📌
Static VariablesStatic Variables静态变量
class-level constantsclass-level constants类级别常量
Defined at the class level. One copy in total, shared by every instance and every subclass. Named UPPER_SNAKE_CASE to say "don't reassign". Example: Drink.TAX_RATE = 0.10.
Class के स्तर पर define। कुल एक copy, हर instance और हर subclass साझा करते हैं। नाम UPPER_SNAKE_CASE में, ताकि पता चले "दोबारा assign न करें"। उदाहरण: Drink.TAX_RATE = 0.10।
在类级别定义。总共只有一份,所有实例 和 所有子类共享。用 UPPER_SNAKE_CASE 命名,表示"不要重新赋值"。例如:Drink.TAX_RATE = 0.10。
🔧
Static MethodsStatic Methods静态方法
@staticmethod · no self, no cls
Plain functions inside the class. They take no self and no cls — only their own arguments. Called on the class: Drink.celsius_to_fahrenheit(65). Good for validators, converters and pure calculations.
Class के अंदर सादे functions। ये न self लेते हैं न cls — सिर्फ़ अपने arguments। Class पर बुलाए जाते हैं: Drink.celsius_to_fahrenheit(65)। Validators, converters और शुद्ध गणनाओं के लिए अच्छे।
类里的普通函数。不接收 self,也不接收 cls — 只有自己的参数。在类上调用:Drink.celsius_to_fahrenheit(65)。适合校验器、换算器和纯计算。
🔁
Method OverridingMethod Overriding方法重写
same name, new behaviourवही नाम, नया व्यवहार同名,新行为
A subclass defines a method with the same name as one in its parent. Called on the subclass, Python uses the new version. Same call, different behaviour — this is polymorphism.
Subclass अपने parent जैसे नाम का method define करती है। Subclass पर बुलाने पर Python नया version इस्तेमाल करता है। एक ही call, अलग व्यवहार — इसे polymorphism कहते हैं।
子类定义一个与父类方法 同名 的方法。在子类上调用时,Python 使用新版本。同样的调用,不同的行为 — 这就是 多态(polymorphism)。

SectionSection第 03 节

Static Variables — The Shop's Posted Rules 📌Static Variables — Shop के लगे हुए नियम 📌静态变量 — 店里张贴的规则 📌

A static variable is a value written directly under the class line, not inside __init__. There is exactly one copy in memory, and every instance reads it.

Static variable एक value है जो सीधे class line के नीचे लिखी जाती है, __init__ के अंदर नहीं। Memory में ठीक एक copy होती है, और हर instance उसे पढ़ता है।

静态变量是直接写在 class 行下面的值,而不是写在 __init__ 里。内存中只有 一份,每个实例都读取它。

class Drink:
    # == STATIC VARIABLES - one copy, shared by every drink ==
    STANDARD_CUP_ML = 250
    MAX_BREW_TEMP_C = 95
    TAX_RATE        = 0.10
    CURRENCY        = "$"

    def __init__(self, base_price):
        # INSTANCE variables - one per drink
        self.base_price = base_price

# Read via the class (the usual form)
print(Drink.STANDARD_CUP_ML)
print(Drink.TAX_RATE)
print(Drink.CURRENCY)

# Every instance sees the same value - it is not copied per drink
d1 = Drink(3); d2 = Drink(5); d3 = Drink(4)
print(d1.TAX_RATE, d2.TAX_RATE, d3.TAX_RATE)
print(d1.__dict__)                     # only base_price lives on the object
OUTPUT
250 0.1 $ 0.1 0.1 0.1 {'base_price': 3}

The last line shows that TAX_RATE is not stored on the drink. Python finds it on the class.

आख़िरी line दिखाती है कि TAX_RATE drink पर रखा ही नहीं है। Python उसे class पर पाता है।

最后一行表明 TAX_RATE 并不存放在饮品对象上。Python 是在类上找到它的。

One Posted Rulebook — Every Drink Reads Itएक नियम-पुस्तिका — हर Drink उसे पढ़ता है一本规则手册 — 每杯饮品都读取它
One rulebook, every drink reads itSTATIC VARIABLES · class DrinkSTATIC VARIABLES · class Drink静态变量 · class DrinkSTANDARD_CUP_ML = 250MAX_BREW_TEMP_C = 95TAX_RATE = 0.10espressobase_price = 3.00reads TAX_RATE= 0.10lattebase_price = 5.00reads TAX_RATE= 0.10cappuccinobase_price = 4.50reads TAX_RATE= 0.10

Three drinks, three prices — but only one TAX_RATE. Change it on the class and every drink sees the new value.

तीन drinks, तीन prices — पर सिर्फ़ एक TAX_RATE। Class पर बदलें और हर drink नई value देखता है।

三杯饮品,三个价格 — 但只有一个 TAX_RATE。在类上修改,每杯饮品都会看到新值。

⚠️
Never Reassign a Static Constant Through an InstanceStatic Constant को Instance के ज़रिए कभी दोबारा Assign न करें永远不要通过实例给静态常量重新赋值

latte.TAX_RATE = 0.15 does not update the constant. It creates a per-instance shadow that only latte sees — every other drink still reads 0.1. To change a static value (rarely a good idea), do it through the class: Drink.TAX_RATE = 0.15.

latte.TAX_RATE = 0.15 constant को update नहीं करता। यह एक instance-वाला shadow बनाता है जो सिर्फ़ latte देखता है — बाक़ी हर drink अब भी 0.1 पढ़ता है। Static value बदलनी हो (जो कम ही अच्छा विचार है), तो class के ज़रिए बदलें: Drink.TAX_RATE = 0.15।

latte.TAX_RATE = 0.15 不会 更新常量。它只会创建一个只有 latte 能看到的实例级 遮蔽 — 其他饮品仍然读到 0.1。要修改静态值(很少是好主意),请通过类:Drink.TAX_RATE = 0.15。

class Drink:
    TAX_RATE = 0.10
    def __init__(self, base_price):
        self.base_price = base_price

espresso, latte = Drink(3), Drink(5)

latte.TAX_RATE = 0.15            # WRONG: makes a shadow on latte only
print(latte.TAX_RATE, espresso.TAX_RATE, Drink.TAX_RATE)
print(latte.__dict__)

Drink.TAX_RATE = 0.12            # through the class: everyone without a shadow sees it
print(latte.TAX_RATE, espresso.TAX_RATE, Drink.TAX_RATE)
OUTPUT
0.15 0.1 0.1 {'base_price': 5, 'TAX_RATE': 0.15} 0.15 0.12 0.12

🪞 Simulator: Shadow Lab🪞 Simulator: Shadow Lab🪞 模拟器:遮蔽实验室

Run lines in any order. Each drink shows what drink.TAX_RATE reads and where it comes from. The two price rows show something surprising: the static method calculate_total reads Drink.TAX_RATE, so it ignores a shadow — but code that reads self.TAX_RATE does not. Checked against real Python on 300 random sequences.

Lines किसी भी क्रम में चलाएँ। हर drink दिखाता है कि drink.TAX_RATE क्या पढ़ता है और वह कहाँ से आता है। दो price rows एक हैरान करने वाली बात दिखाती हैं: static method calculate_total Drink.TAX_RATE पढ़ता है, इसलिए shadow को नज़रअंदाज़ करता है — पर self.TAX_RATE पढ़ने वाला code नहीं करता। असली Python से 300 random sequences पर जाँचा गया।

以任意顺序运行代码行。每杯饮品显示 drink.TAX_RATE 读到的值及其来源。两行价格揭示了一个意外:静态方法 calculate_total 读取的是 Drink.TAX_RATE,所以它会 忽略 遮蔽 — 而读取 self.TAX_RATE 的代码不会。已在 300 个随机序列上与真实 Python 核对。

Shadow LabShadow Lab遮蔽实验室write through the class, never an instanceclass के ज़रिए लिखें, instance के ज़रिए नहीं通过类写,不要通过实例
Lines run so farअब तक चली lines已运行的代码

SectionSection第 04 节

Static Methods — The Shared Utility Shelf 🔧Static Methods — साझा औज़ारों की Shelf 🔧静态方法 — 共享的工具架 🔧

A static method is written with the @staticmethod decorator and takes no self and no cls. It is a plain function that happens to live inside the class. Use it for validators, converters, formatters and pure calculations.

Static method @staticmethod decorator के साथ लिखा जाता है और न self लेता है न cls। यह एक सादा function है जो बस class के अंदर रहता है। इसे validators, converters, formatters और शुद्ध गणनाओं के लिए इस्तेमाल करें।

静态方法用 @staticmethod 装饰器来写,不接收 self,也不接收 cls。它是一个恰好住在类里的普通函数。适合校验器、换算器、格式化函数和纯计算。

class Drink:
    STANDARD_CUP_ML = 250
    TAX_RATE        = 0.10

    def __init__(self, base_price):
        self.base_price = base_price

    # == STATIC METHOD - pure input -> output, no self, no cls ==
    @staticmethod
    def celsius_to_fahrenheit(c):
        return c * 9 / 5 + 32

    @staticmethod
    def is_valid_size(size_ml):
        return 25 <= size_ml <= 500

    @staticmethod
    def calculate_total(price):
        # Uses the class-level constant - but needs no self or cls
        return price * (1 + Drink.TAX_RATE)

# Call BEFORE any drink exists - that is the whole point
print(Drink.celsius_to_fahrenheit(65))
print(Drink.is_valid_size(200))
print(Drink.is_valid_size(1000))
print(Drink.calculate_total(5.00))          # with 10% tax

latte = Drink(5)
print(latte.celsius_to_fahrenheit(65))      # also works through an instance
OUTPUT
149.0 True False 5.5 149.0
Static Method — Input → Pure Function → OutputStatic Method — Input → शुद्ध Function → Output静态方法 — 输入 → 纯函数 → 输出
Static method as a pure functionDrink.calculate_total(5.00)INPUTINPUT输入5.00@staticmethodcalculate_total(price)price * (1 + TAX_RATE)no self · no cls · pureन self · न cls · शुद्ध无 self · 无 cls · 纯函数OUTPUTOUTPUT输出5.5No object is created · no drink is changed · input in, answer outकोई object नहीं बनता · कोई drink नहीं बदलता · input अंदर, जवाब बाहर不创建对象 · 不改变饮品 · 输入进,答案出

Call it straight on the class. No instance is needed.

इसे सीधे class पर बुलाएँ। Instance की ज़रूरत नहीं।

直接在类上调用,不需要实例。

⚙️ Simulator: Static Method Machine⚙️ Simulator: Static Method Machine⚙️ 模拟器:静态方法机器

Pick a static method and drag the input. Then try two things: call it through an object, and remove the decorator. Every output matches real Python on the whole slider range.

कोई static method चुनें और input खिसकाएँ। फिर दो चीज़ें आज़माएँ: इसे object के ज़रिए बुलाएँ, और decorator हटा दें। हर output पूरी slider range पर असली Python से मिलता है।

选一个静态方法,拖动输入。然后试两件事:通过对象 调用它,以及 去掉装饰器。在整个滑块范围内,每个输出都与真实 Python 一致。

Static Method MachineStatic Method Machine静态方法机器input in, answer outinput अंदर, जवाब बाहर输入进,答案出
Method:Method:方法:
Call:Call:调用:
Decorator:
Input:Input:输入:
INPUT
→
@staticmethod
→
CodeCode代码
Real outputअसली output真实输出

SectionSection第 05 节

Method Overriding — Same Call, Different Behaviour 🔁Method Overriding — एक Call, अलग व्यवहार 🔁方法重写 — 同样的调用,不同的行为 🔁

Method overriding happens when a child class defines a method with the same name as one in its parent. When the method is called on the child, Python uses the child's version, and the parent's version is not used. This is what makes polymorphism possible: many types respond to the same command in different ways.

Method overriding तब होता है जब child class अपने parent जैसे नाम का method define करती है। Child पर method बुलाने पर Python child का version इस्तेमाल करता है, parent वाला नहीं। इसी से polymorphism संभव होता है: कई types एक ही आदेश पर अलग-अलग तरह से जवाब देते हैं।

当子类定义了一个与父类方法 同名 的方法时,就发生了 方法重写。在子类上调用该方法时,Python 使用 子类的版本,父类的版本不会被用到。这让 多态 成为可能:多种类型对同一个命令做出不同的响应。

class Drink:
    def __init__(self, size_ml, base_price):
        self.size_ml    = size_ml
        self.base_price = base_price

    # A generic prepare() - subclasses will override this
    def prepare(self):
        print("Preparing a generic drink - hot water in a cup.")

class Espresso(Drink):
    # -- OVERRIDE - same name, brand-new implementation --
    def prepare(self):
        print(f"Espresso ({self.size_ml}ml): grind beans, tamp, extract 25s.")

class Latte(Drink):
    def prepare(self):
        print(f"Latte ({self.size_ml}ml): pull espresso, steam milk to 65°C, pour art.")

class Cappuccino(Drink):
    def prepare(self):
        print(f"Cappuccino ({self.size_ml}ml): equal parts espresso, milk, foam.")

# Same call, different result - that is polymorphism
for drink in [Espresso(30, 3), Latte(250, 5), Cappuccino(180, 4.5)]:
    drink.prepare()

print([c.__name__ for c in Espresso.mro()])   # where Python searches
OUTPUT
Espresso (30ml): grind beans, tamp, extract 25s. Latte (250ml): pull espresso, steam milk to 65°C, pour art. Cappuccino (180ml): equal parts espresso, milk, foam. ['Espresso', 'Drink', 'object']
🔑
The Rule Python UsesPython का नियमPython 使用的规则

When you call obj.method(), Python starts searching at the object's own class. The first match wins. If Espresso defines prepare(), Python uses it and never looks at Drink.prepare(). The search order is the Method Resolution Order (MRO) — the last line above prints it.

जब आप obj.method() बुलाते हैं, Python object की अपनी class से खोज शुरू करता है। पहला match जीतता है। अगर Espresso में prepare() है, तो Python उसे इस्तेमाल करता है और Drink.prepare() को देखता तक नहीं। खोज का यह क्रम Method Resolution Order (MRO) है — ऊपर की आख़िरी line इसे print करती है।

调用 obj.method() 时,Python 从对象自己的类开始查找。第一个匹配的胜出。如果 Espresso 定义了 prepare(),Python 就用它,根本不会去看 Drink.prepare()。这个查找顺序就是 方法解析顺序(MRO) — 上面最后一行把它打印了出来。

One Call, Three Different Outcomesएक Call, तीन अलग नतीजे一次调用,三种不同结果
One call, three outcomesfor drink in [...]: drink.prepare()PARENT · DrinkPARENT · Drink父类 · Drinkprepare() — generic, not usedprepare() — generic, इस्तेमाल नहींprepare() — 通用版,未使用Espresso(Drink)def prepare(self):grind → tamp → extract* OVERRIDEOUTPUTOUTPUT输出Espresso (30ml): grind…Latte(Drink)def prepare(self):espresso + steamed milk* OVERRIDEOUTPUTOUTPUT输出Latte (250ml): pull…Cappuccino(Drink)def prepare(self):espresso + milk + foam* OVERRIDEOUTPUTOUTPUT输出Cappuccino (180ml): equal…one line, three behavioursएक line, तीन व्यवहार一行代码,三种行为

The loop calls prepare() on each drink in turn. Python picks the right prepare() for each one — the parent's generic version never runs.

Loop हर drink पर बारी-बारी prepare() बुलाता है। Python हर एक के लिए सही prepare() चुनता है — parent का generic version कभी नहीं चलता।

循环依次对每杯饮品调用 prepare()。Python 为每一杯挑选正确的 prepare() — 父类的通用版本从不运行。

🍹 Simulator: One Call, Many Drinks🍹 Simulator: एक Call, कई Drinks🍹 模拟器:一次调用,多种饮品

These are the classes from the complete example in Section 07. Pick a drink to see where prepare() and price_receipt() are found. Then build a menu and press Run loop: the same drink.prepare() call goes to a different class each time. All outputs are real.

ये Section 07 के पूरे उदाहरण की classes हैं। कोई drink चुनें और देखें कि prepare() और price_receipt() कहाँ मिलते हैं। फिर menu बनाएँ और Run loop दबाएँ: वही drink.prepare() call हर बार अलग class तक जाती है। सभी outputs असली हैं।

这些是第 07 节完整示例中的类。选一杯饮品,看看 prepare() 和 price_receipt() 在哪里被找到。然后搭一份菜单,按 Run loop:同一个 drink.prepare() 调用每次都会走到不同的类。所有输出都是真实的。

One Call, Many Drinksएक Call, कई Drinks一次调用,多种饮品the first match winsपहला match जीतता है第一个匹配胜出
Look at:देखें:查看:
CodeCode代码
Real outputअसली output真实输出
menu =
add:जोड़ें:添加:
for drink in menu: drink.prepare(); print(drink.price_receipt())for drink in menu: drink.prepare(); print(drink.price_receipt())for drink in menu: drink.prepare(); print(drink.price_receipt())

SectionSection第 06 节

Override + Extend — Using super() to Keep the Parent's Behaviour 🧩Override + Extend — Parent का व्यवहार रखने के लिए super() 🧩重写 + 扩展 — 用 super() 保留父类行为 🧩

Sometimes you do not want to replace the parent's method — you want to extend it. Call super().method() inside your override to run the parent's version first, then add your own steps.

कभी-कभी आप parent का method replace नहीं करना चाहते — उसे extend करना चाहते हैं। अपने override के अंदर super().method() बुलाएँ ताकि पहले parent का version चले, फिर अपने steps जोड़ें।

有时你并不想 替换 父类的方法 — 而是想 扩展 它。在你的重写中调用 super().method(),先运行父类版本,再加上你自己的步骤。

class Drink:
    def __init__(self, size_ml, base_price):
        self.size_ml    = size_ml
        self.base_price = base_price

    def prepare(self):
        print("  1. Warm the cup")
        print("  2. Fill with liquid")

class FlavouredLatte(Drink):
    def __init__(self, size_ml, base_price, syrup):
        super().__init__(size_ml, base_price)
        self.syrup = syrup

    def prepare(self):
        super().prepare()                       # run the parent's steps first
        print(f"  3. Add {self.syrup} syrup")   # then extend
        print("  4. Steam milk and pour")

drink = FlavouredLatte(250, 5.50, syrup="vanilla")
drink.prepare()
OUTPUT
1. Warm the cup 2. Fill with liquid 3. Add vanilla syrup 4. Steam milk and pour
🏆
The Two Flavours of OverridingOverriding के दो स्वाद重写的两种口味

Replace — override without calling super(). The parent's version never runs. Use it when the child's behaviour is completely different. Extend — override and call super().method() inside. The parent runs, then your addition runs. Use it for "the parent's logic plus a bit more".

Replace — super() बुलाए बिना override। Parent का version कभी नहीं चलता। तब इस्तेमाल करें जब child का व्यवहार बिल्कुल अलग हो। Extend — override करें और अंदर super().method() बुलाएँ। पहले parent चलता है, फिर आपका जोड़। "Parent का logic और थोड़ा ज़्यादा" के लिए इस्तेमाल करें।

替换 — 重写时不调用 super()。父类版本永远不运行。适用于子类行为完全不同的情况。扩展 — 重写 并且 在里面调用 super().method()。先运行父类,再运行你的追加部分。适用于"父类逻辑再多一点"。

Replace vs Extend — the Same Override, With and Without super()Replace बनाम Extend — वही Override, super() के साथ और बिना替换与扩展 — 同一个重写,有无 super() 的区别
Replace vs extendREPLACE · no super()REPLACE · super() नहीं替换 · 不调用 super()1. Warm the cup2. Fill with liquid⚠ parent steps lost⚠ parent के steps खो गए⚠ 父类步骤丢失3. Add vanilla syrup4. Steam milk and pourEXTEND · super().prepare() firstEXTEND · पहले super().prepare()扩展 · 先 super().prepare()1. Warm the cup2. Fill with liquid3. Add vanilla syrup4. Steam milk and pourpurple = Drink (parent) · blue = childबैंगनी = Drink (parent) · नीला = child紫色 = Drink(父类)· 蓝色 = 子类FlavouredLatte(250, 5.50, syrup="vanilla").prepare()

Without super() the parent's two steps are lost. With super() they run first, then the child's steps.

super() के बिना parent के दो steps खो जाते हैं। super() के साथ वे पहले चलते हैं, फिर child के steps।

没有 super(),父类的两个步骤就丢了。有 super(),它们先运行,然后才是子类的步骤。

🧩 Simulator: Replace vs Extend🧩 Simulator: Replace बनाम Extend🧩 模拟器:替换与扩展

Switch between the two versions of FlavouredLatte.prepare() and step through each. With extend, watch super().prepare() open the parent's frame on top of the stack, print steps 1–2, then return. With replace, the parent's prepare() is never called. Recorded with sys.settrace.

FlavouredLatte.prepare() के दोनों versions में बदलें और हर एक को step करें। Extend में देखें कि super().prepare() stack के ऊपर parent का frame खोलता है, steps 1–2 print करता है, फिर लौटता है। Replace में parent का prepare() कभी बुलाया ही नहीं जाता। sys.settrace से रिकॉर्ड।

在 FlavouredLatte.prepare() 的两个版本之间切换,并逐步运行。在 扩展 版本中,看 super().prepare() 在栈顶打开父类的帧,打印第 1–2 步,然后返回。在 替换 版本中,父类的 prepare() 根本不会被调用。由 sys.settrace 记录。

Replace vs ExtendReplace बनाम Extend替换与扩展step through bothदोनों को step करें两种都逐步运行
Version:Version:版本:
CodeCode代码
Call stack (newest at the bottom)Call stack (सबसे नया नीचे)调用栈(最新的在最下面)
The objectObject对象
OutputOutput输出

SectionSection第 07 节

Putting All Three Together — The Complete Coffee Shop 🏪तीनों एक साथ — पूरी Coffee Shop 🏪三者合一 — 完整的咖啡店 🏪

One family of classes that uses static variables (shop rules), static methods (utility functions) and method overriding (each drink's own preparation). This is the real payoff.

Classes का एक परिवार जो static variables (shop के नियम), static methods (utility functions) और method overriding (हर drink का अपना तरीक़ा) इस्तेमाल करता है। यही असली फ़ायदा है।

一个类家族,同时使用静态变量(店规)、静态方法(工具函数)和方法重写(每款饮品自己的做法)。这才是真正的收获。

class Drink:
    # == STATIC VARIABLES - shared across every drink and every subclass ==
    STANDARD_CUP_ML = 250
    MAX_BREW_TEMP_C = 95
    TAX_RATE        = 0.10
    CURRENCY        = "$"

    def __init__(self, size_ml, base_price):
        if not Drink.is_valid_size(size_ml):     # uses a STATIC METHOD
            raise ValueError(f"{size_ml}ml is out of range")
        self.size_ml    = size_ml
        self.base_price = base_price

    # -- Generic prepare - subclasses OVERRIDE this --
    def prepare(self):
        print("Generic drink: hot water, done.")

    # -- Instance method - uses a static variable for its calculation --
    def price_receipt(self):
        total = Drink.calculate_total(self.base_price)
        return f"  Price: {Drink.CURRENCY}{self.base_price:.2f} + tax → {Drink.CURRENCY}{total:.2f}"

    # == STATIC METHODS - utility functions grouped with the class ==
    @staticmethod
    def is_valid_size(size_ml):
        return 25 <= size_ml <= 500

    @staticmethod
    def celsius_to_fahrenheit(c):
        return c * 9 / 5 + 32

    @staticmethod
    def calculate_total(price):
        return price * (1 + Drink.TAX_RATE)

class Espresso(Drink):
    def __init__(self):
        super().__init__(size_ml=30, base_price=3.00)

    # OVERRIDE - Espresso has its own steps
    def prepare(self):
        print(f"[Espresso {self.size_ml}ml] grind → tamp → extract for 25s")

class Latte(Drink):
    def __init__(self):
        super().__init__(size_ml=250, base_price=5.00)

    def prepare(self):
        temp_f = Drink.celsius_to_fahrenheit(65)     # uses a STATIC METHOD
        print(f"[Latte {self.size_ml}ml] pull espresso → steam milk to 65°C ({temp_f}°F) → pour art")

class Cappuccino(Drink):
    def __init__(self):
        super().__init__(size_ml=180, base_price=4.50)

    def prepare(self):
        print(f"[Cappuccino {self.size_ml}ml] equal parts espresso + steamed milk + foam")

class FlavouredLatte(Latte):
    def __init__(self, syrup):
        super().__init__()
        self.syrup = syrup
        self.base_price += 0.75              # flavour surcharge

    # OVERRIDE + EXTEND - run the parent's Latte prepare, then add the syrup step
    def prepare(self):
        super().prepare()
        print(f"              → finish with {self.syrup} syrup")

# -- Use the static methods BEFORE any drink exists --
print("Valid 150ml cup?", Drink.is_valid_size(150))
print("Valid 900ml cup?", Drink.is_valid_size(900))
print("Brew temp in F:", Drink.celsius_to_fahrenheit(Drink.MAX_BREW_TEMP_C))
print()

# -- Same loop, each drink runs its own prepare() --
menu = [Espresso(), Latte(), Cappuccino(), FlavouredLatte(syrup="vanilla")]
for drink in menu:
    drink.prepare()                 # goes to the right override
    print(drink.price_receipt())    # inherited from Drink, uses a static var + method
    print()
OUTPUT
Valid 150ml cup? True Valid 900ml cup? False Brew temp in F: 203.0 [Espresso 30ml] grind → tamp → extract for 25s Price: $3.00 + tax → $3.30 [Latte 250ml] pull espresso → steam milk to 65°C (149.0°F) → pour art Price: $5.00 + tax → $5.50 [Cappuccino 180ml] equal parts espresso + steamed milk + foam Price: $4.50 + tax → $4.95 [Latte 250ml] pull espresso → steam milk to 65°C (149.0°F) → pour art → finish with vanilla syrup Price: $5.75 + tax → $6.33
🏆
Everything Fits Togetherसब कुछ आपस में जुड़ता है一切都契合在一起

Static variables (TAX_RATE, CURRENCY, MAX_BREW_TEMP_C) hold the one-truth constants. Static methods (is_valid_size, celsius_to_fahrenheit, calculate_total) are utilities every drink can call — even __init__ uses one to reject a bad size. Method overriding gives each drink its own prepare(). And FlavouredLatte shows "override + extend" with super().prepare().

Static variables (TAX_RATE, CURRENCY, MAX_BREW_TEMP_C) एक-सच वाले constants रखते हैं। Static methods (is_valid_size, celsius_to_fahrenheit, calculate_total) utilities हैं जिन्हें हर drink बुला सकता है — __init__ भी ग़लत size ठुकराने के लिए एक इस्तेमाल करता है। Method overriding हर drink को अपना prepare() देता है। और FlavouredLatte super().prepare() से "override + extend" दिखाता है।

静态变量(TAX_RATE、CURRENCY、MAX_BREW_TEMP_C)保存唯一可信的常量。静态方法(is_valid_size、celsius_to_fahrenheit、calculate_total)是每杯饮品都能调用的工具 — 连 __init__ 也用它来拒绝不合法的容量。方法重写 让每款饮品有自己的 prepare()。FlavouredLatte 则用 super().prepare() 展示了"重写 + 扩展"。


SectionSection第 08 节

When to Reach for Each Tool — Decision Grid 🧭कब कौन-सा Tool — Decision Grid 🧭何时用哪个工具 — 决策表 🧭

📌
Use a Static Variable When…Static Variable तब, जब…用静态变量,当……
UPPER_SNAKE_CASE constantsUPPER_SNAKE_CASE constantsUPPER_SNAKE_CASE 常量
The value is the same for every instance and every subclass — settings, business rules, physical constants. Prefer values that cannot change in place (str, int, tuple, frozenset).
Value हर instance और हर subclass के लिए एक जैसी है — settings, business नियम, भौतिक constants। ऐसी values चुनें जो अपनी जगह नहीं बदलतीं (str, int, tuple, frozenset)।
这个值对每个实例 和 每个子类都一样 — 配置、业务规则、物理常数。优先使用不能原地修改的值(str、int、tuple、frozenset)。
🔧
Use a Static Method When…Static Method तब, जब…用静态方法,当……
is_valid_x, x_to_y, calc_z
The function needs neither self nor cls, but still belongs with the class — validators, converters, formatters, pure calculations. Grouping them keeps them easy to find.
Function को न self चाहिए न cls, फिर भी वह class से जुड़ा है — validators, converters, formatters, शुद्ध गणनाएँ। साथ रखने से वे आसानी से मिलते हैं।
函数既不需要 self 也不需要 cls,但在概念上属于这个类 — 校验器、换算器、格式化函数、纯计算。放在一起便于查找。
🔁
Use Method Overriding When…Method Overriding तब, जब…用方法重写,当……
child redefines parent methodchild parent का method दोबारा लिखता है子类重新定义父类方法
Different subclasses must answer the same command in different ways. Write the loop once, and each object does the right thing for its type.
अलग-अलग subclasses को एक ही आदेश का जवाब अलग तरह से देना है। Loop एक बार लिखें, और हर object अपने type के हिसाब से सही काम करता है।
不同的子类需要以不同方式响应 同一个命令。循环只写一次,每个对象都按自己的类型做对的事。
🧩
Override + Extend When…Override + Extend तब, जब…重写 + 扩展,当……
super().method() firstपहले super().method()先 super().method()
You want the parent's behaviour and a little extra. Call super().method(...) inside the override, then add your own steps.
आपको parent का व्यवहार और थोड़ा ज़्यादा चाहिए। Override के अंदर super().method(...) बुलाएँ, फिर अपने steps जोड़ें।
你想要父类的行为 再加 一点点。在重写中调用 super().method(...),然后加上你自己的步骤。
❌
Don't Use a Mutable Static VariableMutable Static Variable न रखें不要使用可变的静态变量
shared-mutable trapसाझा-mutable जाल共享可变陷阱
A class-level list or dict is shared by every instance. One .append() hits them all. Put per-instance data in __init__.
Class-level list या dict हर instance साझा करता है। एक .append() सब पर असर डालता है। हर instance का data __init__ में रखें।
类级别的 list 或 dict 被 所有实例 共享。一次 .append() 就会影响全部。每个实例自己的数据放在 __init__ 里。
❌
Don't Override Just to "Fix" the ParentParent को "ठीक" करने के लिए Override न करें不要为了"修补"父类而重写
fix at the right layerसही layer पर ठीक करें在正确的层级修复
If every subclass overrides the same method in the same way, the fix belongs in the parent. Overriding is for real per-subclass differences, not workarounds.
अगर हर subclass एक ही method को एक ही तरह override करती है, तो fix parent में होना चाहिए। Overriding असली अलग-अलग ज़रूरतों के लिए है, जुगाड़ के लिए नहीं।
如果每个子类都用同样的方式重写同一个方法,修复就应该放在父类里。重写是为了子类之间真正的差异,而不是变通补丁。

SectionSection第 09 节

Common Mistakes (and Fixes) 🩹आम ग़लतियाँ (और उनके हल) 🩹常见错误(及修正)🩹

Mistakeग़लती错误What goes wrongक्या बिगड़ता है会出什么问题Fixहल修正
Adding self to a static methodStatic method में self जोड़ना给静态方法加了 selfIt now needs an object; calling it on the class raises TypeErrorअब इसे object चाहिए; class पर बुलाने से TypeError现在它需要对象;在类上调用会抛出 TypeErrorDrop self; add @staticmethod on the line aboveself हटाएँ; ऊपर वाली line पर @staticmethod जोड़ें去掉 self;在上一行加 @staticmethod
Forgetting @staticmethod@staticmethod भूलना忘写 @staticmethodCalling through the class still works, so it hides; calling through an object passes the object as the first argument → TypeErrorClass के ज़रिए call चलती है, इसलिए छिपा रहता है; object के ज़रिए call में object पहला argument बनता है → TypeError通过类调用仍然能用,所以问题被掩盖;通过对象调用时对象被当作第一个参数 → TypeErrorAdd @staticmethod — the decorator is required@staticmethod जोड़ें — decorator ज़रूरी है加上 @staticmethod — 装饰器是必需的
instance.CONSTANT = valueCreates a per-instance shadow — the class constant stays the sameInstance-वाला shadow बनता है — class constant वैसा ही रहता है产生实例级遮蔽 — 类常量保持不变Never write it. To change it, go through the class: Drink.TAX_RATE = ...कभी न लिखें। बदलना हो तो class के ज़रिए: Drink.TAX_RATE = ...永远不要这样写。要修改就通过类:Drink.TAX_RATE = ...
A mutable static variable (orders = [])Mutable static variable (orders = [])可变的静态变量(orders = [])Every instance shares one list; a change from any of them reaches allहर instance एक list साझा करता है; किसी का भी बदलाव सब तक पहुँचता है所有实例共享一个列表;任何一个的修改都会影响全部Put it in __init__: self.orders = []इसे __init__ में रखें: self.orders = []放进 __init__:self.orders = []
Overriding with a different signatureअलग signature के साथ override करना重写时签名不一致Code that calls the parent's way fails on the child with TypeErrorParent वाले तरीक़े से बुलाने वाला code child पर TypeError से fail होता है按父类方式调用的代码在子类上抛出 TypeErrorKeep the same parameters; give any new ones a defaultवही parameters रखें; नए parameters को default दें保持参数一致;新增参数给默认值
Overriding but forgetting super() when you meant to extendExtend करना था पर override में super() भूल गए想扩展,重写时却忘了 super()The parent's steps are silently skippedParent के steps चुपचाप छूट जाते हैं父类的步骤被悄悄跳过Decide on purpose: replace (no super()) or extend (call super())सोच-समझकर तय करें: replace (super() नहीं) या extend (super() बुलाएँ)有意识地决定:替换(不调用 super())还是扩展(调用 super())
Overriding when a setting would doजहाँ setting काफ़ी हो वहाँ override करना本可以用一个设置,却去重写Lots of one-line subclasses (class RedButton(Button))बहुत सी एक-line वाली subclasses (class RedButton(Button))大量只有一行的子类(class RedButton(Button))Use a parameter or class variable in the parent insteadइसकी जगह parent में parameter या class variable इस्तेमाल करें改为在父类中使用参数或类变量

🩺 Simulator: Bug Doctor🩺 Simulator: Bug Doctor🩺 模拟器:Bug 医生

Six of these mistakes, run for real. Buggy code on the left, fixed code on the right, changed lines marked. Three of them give no error at all.

इनमें से छह ग़लतियाँ, असली run में। बाईं ओर buggy code, दाईं ओर ठीक किया हुआ, बदली lines चिह्नित। इनमें से तीन कोई error नहीं देतीं।

其中六个错误的真实运行。左边是有 bug 的代码,右边是修正后的代码,改动的行已标出。其中三个 完全不报错。

Bug DoctorBug DoctorBug 医生6 bugs · real outputs6 bugs · असली outputs6 个 bug · 真实输出
❌ BuggyBuggy有 bug
✅ Fixedठीक किया已修正

SectionSection第 10 节

Quick Reference 📋Quick Reference 📋速查表 📋

Taskकाम任务SyntaxSyntax语法Notesनोट说明
Declare a static variableStatic variable declare करना声明静态变量TAX_RATE = 0.10 under the class lineclass line के नीचे TAX_RATE = 0.10在 class 行下写 TAX_RATE = 0.10UPPER_SNAKE_CASE; treat it as read-onlyUPPER_SNAKE_CASE; इसे सिर्फ़ पढ़ने वाला मानेंUPPER_SNAKE_CASE;当作只读
Read a static variableStatic variable पढ़ना读取静态变量Drink.TAX_RATEThe usual formसामान्य रूप常用写法
Change a static variableStatic variable बदलना修改静态变量Drink.TAX_RATE = 0.12⚠️ Never through an instance⚠️ Instance के ज़रिए कभी नहीं⚠️ 绝不要通过实例
Declare a static methodStatic method declare करना声明静态方法@staticmethod + def fn(x):Decorator on the line above defDecorator def के ऊपर वाली line पर装饰器写在 def 上一行
Call a static methodStatic method बुलाना调用静态方法Drink.is_valid_size(200)No instance neededInstance की ज़रूरत नहीं不需要实例
Override a methodMethod override करना重写方法Redefine it with the same name in the childChild में उसी नाम से दोबारा define करें在子类中用相同名字重新定义The child's version winsChild का version जीतता है子类版本胜出
Extend a methodMethod extend करना扩展方法Override + call super().method(...)Override + super().method(...) बुलाएँ重写 + 调用 super().method(...)Parent runs, then your additionपहले parent, फिर आपका जोड़先运行父类,再运行你的追加
Polymorphic loopPolymorphic loop多态循环for x in items: x.prepare()Each object goes to its own overrideहर object अपने override तक जाता है每个对象都调用自己的重写版本
See the lookup orderखोज का क्रम देखना查看查找顺序Espresso.mro()The first class in the list that defines the method winsList में जिस पहली class में method है, वही जीतती है列表中第一个定义该方法的类胜出

SectionSection第 11 节

Golden Rules and Quiz 🏆सुनहरे नियम और Quiz 🏆黄金法则与小测验 🏆

☕ Overriding, Static Methods and Static Variables — Rules to RememberOverriding, Static Methods और Static Variables — याद रखने वाले नियम重写、静态方法与静态变量 — 必须记住的规则
1
A static variable is a class-level constant. One copy, shared by every instance and every subclass. Name it UPPER_SNAKE_CASE to say "don't reassign".
Static variable class-level constant है। एक copy, हर instance और हर subclass साझा करते हैं। "दोबारा assign न करें" बताने के लिए नाम UPPER_SNAKE_CASE रखें।
静态变量 是类级别的常量。只有一份,所有实例和子类共享。用 UPPER_SNAKE_CASE 命名,表示"不要重新赋值"。
2
Only put immutable values in static variables (numbers, strings, tuples, frozensets). A class-level list or dict is shared by every instance — a bug factory.
Static variables में सिर्फ़ immutable values रखें (numbers, strings, tuples, frozensets)। Class-level list या dict हर instance साझा करता है — bugs का कारख़ाना।
静态变量只放 不可变 的值(数字、字符串、元组、frozenset)。类级别的列表或字典被所有实例共享 — 简直是 bug 工厂。
3
Read static variables through the class: Drink.TAX_RATE. Writing through an instance (d.TAX_RATE = 0.15) does not change the constant — it creates a silent shadow on that one object.
Static variables class के ज़रिए पढ़ें: Drink.TAX_RATE। Instance के ज़रिए लिखने (d.TAX_RATE = 0.15) से constant नहीं बदलता — उस एक object पर चुपचाप shadow बनता है।
通过类读取静态变量:Drink.TAX_RATE。通过实例写入(d.TAX_RATE = 0.15)不会 改变常量 — 只会在那个对象上悄悄产生遮蔽。
4
A static method takes neither self nor cls. If it needs instance data, make it an instance method. If it needs the class, make it a class method. Only when it needs neither is it truly static.
Static method न self लेता है न cls। Instance data चाहिए तो instance method बनाएँ। Class चाहिए तो class method। जब दोनों में से कुछ न चाहिए, तभी वह सच में static है।
静态方法 既不接收 self 也不接收 cls。需要实例数据就写成实例方法;需要类就写成类方法。只有两者都不需要时,才真正是静态的。
5
Always put @staticmethod on the line above def. Without it, calling through the class still works, but calling through an object passes the object as an extra first argument and raises TypeError.
@staticmethod हमेशा def के ऊपर वाली line पर रखें। इसके बिना class के ज़रिए call फिर भी चलती है, पर object के ज़रिए call में object अतिरिक्त पहले argument के रूप में जाता है और TypeError आता है।
始终把 @staticmethod 写在 def 上一行。没有它,通过类调用仍然可行,但通过对象调用会把对象当作多余的第一个参数传入,抛出 TypeError。
6
Use @staticmethod for validators, converters, formatters and pure calculations that belong with the class. If a function has nothing to do with the class, keep it as a normal module function.
@staticmethod को उन validators, converters, formatters और शुद्ध गणनाओं के लिए इस्तेमाल करें जो class से जुड़ी हैं। अगर function का class से कोई लेना-देना नहीं, तो उसे सामान्य module function रखें।
把 @staticmethod 用于与类相关的 校验器、换算器、格式化函数和纯计算。如果函数与类无关,就保留为普通的模块函数。
7
Method overriding = the child redefines a parent method with the same name. The child's version wins. Python starts searching at the object's class and walks up.
Method overriding = child उसी नाम से parent का method दोबारा define करता है। Child का version जीतता है। Python object की class से खोज शुरू करके ऊपर जाता है।
方法重写 = 子类用相同名字重新定义父类方法。子类版本胜出。Python 从对象的类开始向上查找。
8
Choose on purpose between replacing and extending. Replacing = no super(). Extending = call super().method(...) inside the override so the parent's logic still runs.
Replace और extend में सोच-समझकर चुनें। Replace = super() नहीं। Extend = override के अंदर super().method(...) बुलाएँ ताकि parent का logic भी चले।
有意识地在 替换 与 扩展 之间做选择。替换 = 不调用 super()。扩展 = 在重写中调用 super().method(...),让父类逻辑依然运行。
9
Keep the same signature when overriding. If you need extra parameters, give them defaults so old callers keep working. Then every subclass can be called the parent's way.
Override करते समय वही signature रखें। अतिरिक्त parameters चाहिए तो उन्हें default दें ताकि पुराने callers चलते रहें। तब हर subclass को parent वाले तरीक़े से बुलाया जा सकता है।
重写时保持 相同的签名。需要额外参数就给默认值,让旧的调用方继续可用。这样每个子类都能按父类的方式调用。
10
When in doubt, ask: "Is this value the same for everyone?" → static variable. "Does this function need no state at all?" → static method. "Does this behaviour differ per subclass?" → override the parent's method.
शक हो तो पूछें: "क्या यह value सबके लिए एक जैसी है?" → static variable। "क्या इस function को कोई state नहीं चाहिए?" → static method। "क्या यह व्यवहार हर subclass में अलग है?" → parent का method override करें।
拿不准时问自己:"这个值对所有人都一样吗?"→ 静态变量。"这个函数完全不需要状态吗?"→ 静态方法。"这个行为在每个子类中不同吗?"→ 重写父类的方法。

Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题

🏆 QuizPick an answer to see why.जवाब चुनें और कारण देखें।选一个答案,看看原因。
🎯
Key Takeawayमुख्य सीख关键要点

Static variables are one shared set of rules — read them anywhere, change them only through the class. Static methods are plain tools that need no self or cls, so you can call them before any object exists. Method overriding lets one call, drink.prepare(), do the right thing for every subclass — replace the parent's version, or extend it with super().

Static variables नियमों का एक साझा सेट हैं — कहीं भी पढ़ें, बदलें सिर्फ़ class के ज़रिए। Static methods सादे औज़ार हैं जिन्हें न self चाहिए न cls, इसलिए उन्हें किसी object के बनने से पहले भी बुला सकते हैं। Method overriding से एक ही call, drink.prepare(), हर subclass के लिए सही काम करती है — parent का version replace करें, या super() से extend करें।

静态变量是一套共享的规则 — 哪里都能读,只通过类来改。静态方法是不需要 self 或 cls 的普通工具,所以在任何对象存在之前就能调用。方法重写让同一个调用 drink.prepare() 对每个子类都做对的事 — 替换父类版本,或用 super() 扩展它。

🐍 Try Python Yourself

Write, run and visualize Python step by step. No install needed.