资讯动态

python 类的基础详解与应用

发布时间:2026/10/8 16:02:24 来源:尧图企业网站定制
前言学习类的最好方式不是背概念而是拿它解决一个真实的小问题。但在此之前基础语法必须过关class怎么写、self是什么、实例属性和类属性有什么区别、为什么__init__里要写self.x x。一个典型误解是类一定比函数高级所以能写类就别写函数。事实相反如果一组数据加几个操作用字典加函数往往就够了只有当数据和行为需要长期绑定、需要复用与扩展时类才显出价值。本文先梳理基础语法再给出一个完整的小应用一个简单的库存管理最后介绍dataclasses如何把只装数据的类写得更省事。一、基础语法速览# 适用于 Python 3.8class Product:tax_rate 0.1 # 类属性所有实例共享def __init__(self, name, price):self.name name # 实例属性每个实例一份self.price pricedef price_with_tax(self):return round(self.price * (1 Product.tax_rate), 2)def __repr__(self):return fProduct({self.name!r}, {self.price})book Product(书, 50)print(book.price_with_tax()) # 55.0print(book.__dict__) # {name: 书, price: 50}要点回顾class 类名:开始定义类体缩进__init__是初始化方法第一个参数是self类属性写在类体里实例属性用self.名字 值声明实例属性存在self.__dict__里类属性不在。对比项类属性实例属性定义位置类体里直接赋值__init__等方法里self.x v存储位置类的__dict__实例的__dict__共享性所有实例共享每个实例独立可变对象风险高共享同一份低访问优先级低高二、方法返回新对象还是原地修改设计类时一个高频抉择是方法是返回新对象还是原地修改自己。原则是一致性——同一个类里别混着来。# 适用于 Python 3.8class Cart:def __init__(self):self.items []def add(self, item):原地修改返回 None。self.items.append(item)def total(self):return sum(price for _, price in self.items)c Cart()c.add((苹果, 5))c.add((香蕉, 3))print(c.total()) # 8list.append()、list.sort()都是原地修改并返回None的代表。如果你写一个add却返回新列表调用者很容易误以为原对象也变了。统一成原地修改 返回None或返回新对象 不改自己代码就好推理了。三、应用示例库存管理下面用一个稍完整的例子把类属性、实例属性、方法、__repr__、简单校验都用上。# 适用于 Python 3.8class Inventory:def __init__(self):self._stock {} # {名称: 数量}def add(self, name, qty):if qty 0:raise ValueError(数量必须为正)self._stock[name] self._stock.get(name, 0) qtyreturn self._stock[name]def remove(self, name, qty):if name not in self._stock:raise KeyError(f库存中没有 {name})if self._stock[name] qty:raise ValueError(f{name} 库存不足)self._stock[name] - qtyreturn self._stock[name]def report(self):if not self._stock:return 库存为空return .join(f{k} x{v} for k, v in sorted(self._stock.items()))def __repr__(self):return fInventory({self._stock!r})inv Inventory()inv.add(键盘, 10)inv.add(鼠标, 5)inv.remove(鼠标, 2)print(inv.report()) # 键盘 x10鼠标 x3print(inv) # Inventory({键盘: 10, 鼠标: 3})这里的_stock用单下划线表示内部使用对外只暴露方法。调用方不需要知道内部是字典还是别的结构将来换成数据库也影响不到外部代码——这正是封装的价值。四、用dataclasses简化数据类如果类的主要职责就是装几个字段用标准库dataclasses能省掉大量样板代码。它从 Python 3.7 开始提供。# 适用于 Python 3.7from dataclasses import dataclass, fielddataclassclass Order:order_id: intcustomer: stritems: list field(default_factorylist)def total_items(self):return len(self.items)o Order(1, Ada)o.items.append(笔记本)print(o) # Order(order_id1, customerAda, items[笔记本])print(o.total_items()) # 1print(Order(1, Ada) Order(1, Ada)) # True自动生成 __eq__field(default_factorylist)是关键直接写items: list []会触发可变默认值检查并报错default_factory则让每个实例都新建一个列表。# 适用于 Python 3.7from dataclasses import dataclassdataclass(frozenTrue)class Point:x: inty: intp Point(1, 2)print(hash(p) is not None) # Truefrozen 且 eq 为 True 时会生成 __hash__frozenTrue生成不可变实例此时__eq__与__hash__会配套生成实例可用于集合和字典键若eqTrue默认而frozenFalse则__hash__被置为None实例不可哈希。五、什么时候不该用类场景更合适的选择一次性脚本、几行数据处理直接写函数纯配置常量模块级常量或字典只为分组命名空间模块或SimpleNamespace需要快速传输/序列化字典或namedtuple需要长期持有状态与行为类判断标准很简单如果这些数据总是和这几个操作一起出现且需要多份实例就用类否则函数就够了。常见坑点可变默认参数❌python # 适用于 Python 3.8 from dataclasses import dataclassdataclass class A: items: list [] # ValueError ✅python # 适用于 Python 3.8 from dataclasses import fielddataclass class A: items: list field(default_factorylist) 类属性当共享状态用❌python # 适用于 Python 3.8 class Counter: n 0def __init__(self): self.n 1 # 实际是实例属性类上的 n 不涨 ✅python # 适用于 Python 3.8 class Counter: n 0def __init__(self): type(self).n 1 写dataclass字段时漏了类型注解❌python # 适用于 Python 3.7 dataclass class A: x 0 # 没有注解不会被当成字段✅python # 适用于 Python 3.7 dataclass class A: x: int 0 # 有注解才是字段frozenTrue后还想赋值❌python # 适用于 Python 3.7 p Point(1, 2) p.x 5 # FrozenInstanceError✅python # 适用于 Python 3.7 from dataclasses import replacep2 replace(p, x5) print(p2) # Point(x5, y2) 在同一类里混用返回新对象和原地修改❌python # 适用于 Python 3.8 class Cart: def add(self, x): self.items.append(x) # 原地 return self.items # 又返回引用调用方易误用✅python # 适用于 Python 3.8 class Cart: def add(self, x): self.items.append(x) # 明确原地修改返回 None实例属性和类属性同名❌python # 适用于 Python 3.8 class A: status newdef __init__(self): self.status new # 同名容易给读者造成混淆 ✅python # 适用于 Python 3.8 class A: default_status new # 换个不冲突的名字def __init__(self): self.status A.default_status 忘了self导致NameError❌python # 适用于 Python 3.8 class A: def __init__(self, v): value v # 没挂到 self 上出了作用域就没了✅python # 适用于 Python 3.8 class A: def __init__(self, v): self.value v总结知识点结论类属性 vs 实例属性前者共享、后者独立self实例的显式引用必须写原地修改返回None并保持风格一致dataclasses数据类样板代码的解法3.7可变默认值用field(default_factory...)何时用类状态与行为长期绑定、需要多实例类的基础不复杂难的是克制不是所有数据都值得封装成类。先写函数等到状态和行为总是一起出现时再升级成类并保持修改风格一致代码自然就好读了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑