从脚本到工程:掌握 Python 中的 OOP

发布: (2026年2月13日 GMT+8 18:10)
9 分钟阅读
原文: Dev.to

Source: Dev.to

大多数开发者一开始会编写 过程式 代码。它就像食谱:一行行执行的指令列表。对于只有 10 行的脚本还能工作,但在大型系统中很快就会变得一团糟——这里改动一次可能会导致十处出错。

要真正理解 面向对象编程(OOP),请停止只考虑“代码”,而是开始从 现实世界 的角度思考。

在现实世界中,一切都是 对象——你的手机、你的汽车,甚至你的宠物猫。每个对象都有两件事:

  • 它是什么(数据 / 属性) – 例如,颜色、重量、品牌。
  • 它能做什么(行为 / 方法) – 例如,打电话、驾驶、喵叫。

这就像组建一支团队:各自专注的对象管理自己的数据和逻辑。

下面是使用真实购物应用对 OOP 的四大支柱 进行的拆解。

📦 1. 封装

概念

将数据和方法打包成一个单元,并 隐藏 那些混乱的内部实现。

现实类比

想象你的企业现金。在过程式代码中,钱散落在桌子上。而在面向对象编程中,它存放在 保险箱 中——你永远不直接接触现金,而是使用键盘(方法)与之交互。

过程式示例

# --- THE PROCEDURAL APPROACH ---
cart_items = []

def add_item(name, price):
    cart_items.append({'name': name, 'price': price})

def get_total():
    return sum(item['price'] for item in cart_items)

# Implementation
add_item("Mechanical Keyboard", 150.00)
add_item("Gaming Mouse", 80.00)

cart_items = []          # Oops! Someone reset the global variable accidentally.
print(f"Total: ${get_total()}")   # Output: 0 (data lost!)

面向对象示例

# --- THE OOP APPROACH ---
class ShoppingCart:
    def __init__(self):
        # Private attribute: cannot be accessed directly from outside
        self.__cart_items = []

    def add_item(self, name: str, price: float):
        self.__cart_items.append({"name": name, "price": price})

    def get_total(self):
        return sum(item['price'] for item in self.__cart_items)

# --- IMPLEMENTATION ---
cart = ShoppingCart()
cart.add_item("Mechanical Keyboard", 150.00)
cart.add_item("Gaming Mouse", 80.00)

# cart.__cart_items = []  # ERROR! This attribute is protected.
print(f"Total: ${cart.get_total()}")   # Output: Total: $230.0

收获 – 你可以控制 数据 的修改方式。

🎭 2. 抽象与多态

概念

隐藏 实现方式,只暴露 功能

现实类比

通用遥控器:你按下 电源,电视、音响和 DVD 播放器都会开启。你不关心每个设备是如何启动的,只在乎它们都能响应同一个指令。

过程式示例

# --- THE PROCEDURAL APPROACH ---
# Too many arguments to keep track of!
def apply_discount(total, type, value, voucher_code, voucher_exp, buy_X, get_Y):
    # A massive if‑else block
    if type == "percentage":
        return total * (1 - value / 100)
    elif type == "fixed":
        return total - value
    # Adding a new discount type means editing this function again.

# Implementation
print(apply_discount(100, "percentage", 10, '', '', 0, 0))   # 90.0
# The next call fails because of missing arguments:
# print(apply_discount(100, "buyXgetY", 10, '', 0))  # error

面向对象示例

# --- THE OOP APPROACH ---
from abc import ABC, abstractmethod

# --- ABSTRACTION: The Blueprint ---
class Discount(ABC):
    @abstractmethod
    def apply(self, total: float) -> float:
        """Return the discounted total."""
        pass

# --- POLYMORPHISM: Flexible Implementations ---
class PercentageDiscount(Discount):
    def __init__(self, percent: int):
        self.percent = percent

    def apply(self, total):
        return total * (1 - self.percent / 100)

class VoucherDiscount(Discount):
    def __init__(self, code: str, amount: float):
        self.code = code
        self.amount = amount

    def apply(self, total):
        print(f"--- Applying Voucher: {self.code} ---")
        return total - self.amount

class BuyXGetYDiscount(Discount):
    def __init__(self, x_qty: int, y_qty: int, unit_price: float):
        self.x_qty = x_qty
        self.y_qty = y_qty
        self.unit_price = unit_price

    def apply(self, total):
        # Example logic: Get Y items free for every X items bought
        free_items = self.y_qty
        discount_value = free_items * self.unit_price
        print(f"--- Buy {self.x_qty} Get {self.y_qty} Applied ---")
        return total - discount_value

# --- IMPLEMENTATION (The code that NEVER changes) ---
def process_checkout(total, discount: Discount):
    """Calculate final price using the supplied discount strategy."""
    final_price = discount.apply(total)
    print(f"Final Price after discount: ${final_price}\n")

# Usage
process_checkout(200, PercentageDiscount(15))          # 15 % off
process_checkout(200, VoucherDiscount("SAVE50", 50.0)) # $50 voucher
process_checkout(200, BuyXGetYDiscount(2, 1, 20.0))    # Buy 2 get 1 ($20) free

收获 – 这遵循 开放‑封闭原则:你的代码对扩展开放(可以添加新折扣类型),但对修改封闭process_checkout 再也不需要改动)。

Source:

🧬 3. 继承

概念

基于已有类创建新类,这样就不必从头开始。

现实类比

想象一下 基础电器。每个电器都需要电源线和开/关开关。

  • 烤面包机 继承了电源线,但额外添加了加热线圈。
  • 搅拌机 继承了电源线,但额外添加了旋转刀片。

为什么? 你不必每次构建新厨房工具时都“重新发明”电和插头。只需继承基础功能,再加入自己的独特特性。

过程式: “复制‑粘贴” 噩梦

# --- THE PROCEDURAL APPROACH ---
def process_credit_card(user, amount, card_number, cvv):
    # Repeated logic for every payment
    if not user.get("is_logged_in", False):
        return {"status": "failed", "reason": "User not logged in"}
    if amount  zero"}

    status = "success" if len(card_number) == 16 and len(cvv) == 3 else "failed"
    return {"type": "credit_card", "amount": amount, "status": status}

def process_paypal(user, amount, email):
    # Same repeated checks
    if not user.get("is_logged_in", False):
        return {"status": "failed", "reason": "User not logged in"}
    if amount  zero"}

    status = "success" if "@" in email else "failed"
    return {"type": "paypal", "amount": amount, "status": status}

面向对象:继承 + 方法重写

# --- THE OOP APPROACH ---
from abc import ABC, abstractmethod
from typing import Dict

# --- PARENT CLASS ---
class Payment(ABC):
    def __init__(self, user: dict, amount: float):
        self.user = user
        self.amount = amount
        self.status = "pending"
        self.reason = None

    def _pre_check(self) -> bool:
        """Shared validation logic for all payments."""
        if not self.user.get("is_logged_in", False):
            self.status = "failed"
            self.reason = "User not logged in"
            return False
        if self.amount  bool:
        """Child implements payment‑specific validation."""
        pass

    def process(self) -> Dict[str, str]:
        if not self._pre_check():
            return {
                "type": self.__class__.__name__,
                "amount": self.amount,
                "status": self.status,
                "reason": self.reason,
            }
        self.status = "success" if self.validate() else "failed"
        return {
            "type": self.__class__.__name__,
            "amount": self.amount,
            "status": self.status,
        }

# --- CHILD CLASSES ---
class CreditCardPayment(Payment):
    def __init__(self, user: dict, amount: float, card_number: str, cvv: str):
        super().__init__(user, amount)
        self.card_number = card_number
        self.cvv = cvv

    def validate(self) -> bool:
        return len(self.card_number) == 16 and len(self.cvv) == 3

class PayPalPayment(Payment):
    def __init__(self, user: dict, amount: float, email: str):
        super().__init__(user, amount)
        self.email = email

    def validate(self) -> bool:
        return "@" in self.email

# --- USAGE ---
user = {"name": "Alice", "is_logged_in": True}
payments = [
    CreditCardPayment(user, 100, "4111111111111111", "123"),
    PayPalPayment(user, 0, "alice@example.com"),  # Total=0 triggers pre‑check
]
results = [p.process() for p in payments]
# `results` will show status, including failed pre‑checks

收获

不要重复自己 (DRY)。 如果在 validate()_pre_check() 中发现 bug,只需在父类里修复一次,所有子类都会受益。

⚖️ 结论

面向对象编程(OOP)不仅是一种编码风格——它是一种在软件中对现实世界问题进行建模的方法。通过关注具有数据(属性)和行为(方法)的对象,OOP 使开发者能够:

  • 封装 数据并控制访问,防止意外修改。
  • 抽象 复杂逻辑,提供清晰的接口,仅暴露必要的部分。
  • 通过继承 复用 和扩展代码,减少重复并强制一致性。
  • 实现 多态,使遵循相同契约的组件能够灵活、可互换。

与过程式编程相比,OOP 提升了 可维护性可扩展性可读性,让管理大型系统、添加新功能以及执行统一的业务规则变得更容易。掌握 OOP 能让开发者构建稳健、灵活且面向未来的软件,能够映射现实世界的复杂性。

0 浏览
Back to Blog

相关文章

阅读更多 »

Show HN: 简易 org-mode Web 适配器

Org Web Adapter 是一个轻量级本地网页应用,用于浏览和编辑 Org 文件。该应用实现为一个 Python 服务器 main.py 加上一个 HTML 模板。