不是“面向对象八股”,不是“Java 式模板”,
从self到__slots__,从“继承陷阱”到“元类黑魔法”,
① 一句话定义:类 = 模板 + 工厂 🏭
类是对象的模板,对象是类的实例。
类比:类 = 饼干模具,对象 = 饼干。
② 基本语法:5 分钟写个类 📝
class Dog:
"""狗狗模板"""
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
return f"{self.name}: 汪汪!"
# 实例化
d = Dog("Lucky", 3)
print(d.bark()) # Lucky: 汪汪!
口诀:“
__init__是构造器,self是实例本身”
③ 成员分类:一张图记牢 🗺️
| 类型 | 写法 | 归属 | 调用 |
|---|---|---|---|
| 实例属性 | self.x = 1 | 对象 | obj.x |
| 类属性 | cls.x = 1 | 类 | Class.x |
| 实例方法 | def foo(self): | 对象 | obj.foo() |
| 类方法 | @classmethod | 类 | Class.foo() |
| 静态方法 | @staticmethod | 无 | Class.foo() |
④ 实战:类属性计数器 ⚡️
class Student:
_count = 0 # 类属性
def __init__(self, name):
self.name = name
Student._count += 1
@classmethod
def how_many(cls):
return cls._count
print(Student.how_many()) # 0
Student("Alice"); Student("Bob")
print(Student.how_many()) # 2
结论:类属性 = 全实例共享,实例属性 = 各管各
⑤ 私有化:双下划线陷阱 🕳️
class Bag:
def __init__(self): self.__items = [] # 伪私有
def add(self, item): self.__items.append(item)
b = Bag(); b.add("book")
print(b.__items) # AttributeError: 'Bag' object has no attribute '__items'
print(b._Bag__items) # ['book'] ← 真名暴露!
结论:
__x是名字粉碎,不是真私有!
⑥ 魔法方法:10 个必背 ✨
| 方法 | 作用 | 示例 |
|---|---|---|
__init__ | 构造器 | def __init__(self): |
__str__ | 字符串 | return f"{self.name}" |
__len__ | len(obj) | return self.size |
__eq__ | obj1 == obj2 | return self.id == other.id |
__iter__ | for i in obj | yield self.data |
__getitem__ | obj[key] | return self.data[key] |
__call__ | obj() | return self.compute() |
__enter__ | with obj | return self |
__exit__ | with 结束 | cleanup() |
__slots__ | 限制属性 | __slots__ = ('x', 'y') |
⑦ 性能加速:__slots__ 省内存 🚀
class Point:
__slots__ = ('x', 'y') # 无 __dict__
def __init__(self, x, y): self.x = x; self.y = y
p = Point(1, 2)
print(p.__dict__) # AttributeError: 'Point' object has no attribute '__dict__'
结论:百万实例 → 内存 ↓30%,速度 ↑10%
⑧ 现代写法:@dataclass 冻结 🧊
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(1, 2)
p.x = 3 # dataclasses.FrozenInstanceError
结论:需要字段名 + 类型提示 → 用 frozen dataclass 代替裸 tuple
⑨ 元类黑魔法:类工厂 🧙♂️(彩蛋)
class Meta(type):
def __new__(mcs, name, bases, attrs):
attrs['_count'] = 0
return super().__new__(mcs, name, bases, attrs)
class MyClass(metaclass=Meta):
pass
print(MyClass._count) # 0
结论:元类 = 类的模板,AOP 利器
⑩ 趣味实战:元组深拷贝调试器(10 行)
import copy, json
def debug_copy(obj):
"""打印对象及其一层子对象的 id"""
print(f"Root id: {id(obj)}")
if hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes)):
for i, item in enumerate(obj):
print(f" [{i}] id: {id(item)} -> {type(item)}")
# 使用
a = [1, [2, 3]]
b = copy.deepcopy(a)
debug_copy(a)
debug_copy(b)
🏁 一句话口诀(背它!)
“类是模板,self 是实例,魔法方法变身对象行为,
**类属性共享,实例属性私有,dataclass 冻结,元类造工厂!”**🎵