1、类的定义与实例化

class Person:
    """这是一个人类的基本类"""
    
    def __init__(self, name, age):
        """初始化方法(构造函数)
        
        参数:
            name (str): 姓名
            age (int): 年龄
        """
        self.name = name  # 实例属性:姓名
        self.age = age    # 实例属性:年龄
    
    def introduce(self):
        """自我介绍方法"""
        print(f"大家好,我叫{self.name},今年{self.age}岁。")

# 实例化类
person1 = Person("张三", 25)  # 创建Person类的实例
person1.introduce()  # 调用实例方法
# 输出: 大家好,我叫张三,今年25岁。

2、类属性和实例属性

class Dog:
    """狗类示例"""
    
    species = "Canis familiaris"  # 类属性(所有实例共享)
    
    def __init__(self, name, breed):
        """初始化方法
        
        参数:
            name (str): 狗的名字
            breed (str): 狗的品种
        """
        self.name = name    # 实例属性
        self.breed = breed  # 实例属性
    
    def bark(self):
        """狗叫的方法"""
        print(f"{self.name}在叫:汪汪!")

# 访问类属性
print(Dog.species)  # 输出: Canis familiaris

# 创建实例
dog1 = Dog("小黑", "拉布拉多")
dog1.bark()  # 输出: 小黑在叫:汪汪!

# 实例也可以访问类属性
print(dog1.species)  # 输出: Canis familiaris

3、继承与方法重写

class Animal:
    """动物基类"""
    
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        """动物发声方法"""
        print("动物发出声音")

class Cat(Animal):
    """猫类,继承自动物类"""
    
    def speak(self):
        """重写父类的speak方法"""
        print(f"{self.name}在叫:喵喵!")

class Dog(Animal):
    """狗类,继承自动物类"""
    
    def speak(self):
        """重写父类的speak方法"""
        print(f"{self.name}在叫:汪汪!")

# 使用继承
cat = Cat("小花")
dog = Dog("大黄")

cat.speak()  # 输出: 小花在叫:喵喵!
dog.speak()  # 输出: 大黄在叫:汪汪!

4、多态与鸭子类型

鸭子类型是实现多态的一种手段,但“多态”概念更大:子类重写父类方法也叫多态(继承多态);实现特定协议(__iter__, __len__, __enter__/__exit__ …)从而被通用函数/语法接受,也是多态(协议多态)。

一句话总结:多态 = “同一操作,多种形态”;鸭子类型 = Python 实现多态的核心哲学:不用看血统,看行为。

#写在上文第3节之后。
def animal_sound(animal):
    """接受任何有speak方法的对象"""
    animal.speak()  # 只要对象有speak方法就能工作

# 创建不同动物实例
animals = [Cat("咪咪"), Dog("阿黄"), Animal("未知动物")]

for animal in animals:
    animal_sound(animal)
# 输出:
# 咪咪在叫:喵喵!
# 阿黄在叫:汪汪!
# 动物发出声音

5、类方法与静态方法

class Date:
    dt_fmt = "YYYY-MM-DD"        # 类变量:所有实例共享

    def __init__(self, y, m, d):
        self.y, self.m, self.d = y, m, d   # 实例变量

    # 1) 实例方法:至少要把对象自身传进来
    def to_str(self):
        return f"{self.y}-{self.m:02d}-{self.d:02d}"

    # 2) 类方法:把“类本身”作为第一个参数
    @classmethod
    def today(cls):
        from datetime import date
        t = date.today()
        return cls(t.year, t.month, t.day)   # 用 cls 而不是写死 Date

    # 3) 静态方法:既不拿实例,也不拿类,纯粹是包在类里的普通函数
    @staticmethod
    def is_leap(year):
        return year % 4 == 0 and year % 100 != 0 or year % 400 == 0
  • 实例方法:你给自己剪指甲(需要“你自己”这个对象)。
  • 类方法:工厂按“人类”这个模板批量造人(需要“人类”这个类)。
  • 静态方法:判断“某年份是不是闰年”——与哪个人、哪条狗都无关,只是一个放在“人类”知识手册里的工具函数。

(这里‘cls’只是一个 约定俗成的形参名,完全可以换成别的名字,但强烈建议保留 cls 以便一眼看出它是“当前类对象”。)

6、装饰器 

6.1、装饰在类身上的装饰器

(写在 class Foo: 上面,或者通过 Foo = decorator(Foo) 实现)

6.1.1、普通类装饰器

语法糖:@my_decorator
本质:接收“一个类对象”,返回“一个新的类对象”或“原类对象”。

def add_timestamp(cls):
    cls.created_at = __import__('time').time()
    return cls

@add_timestamp
class User:
    pass

print(User.created_at)   # 一串时间戳

6.1.2、内置/标准库类装饰器

  • @dataclass —— 自动生成 __init____repr____eq__ 等。
  • @total_ordering@functools.total_ordering —— 根据部分比较方法补全全部。
  • 框架自带:@router.register@orm.table……

6.2装饰在类内部方法身上的装饰器

(写在类里面 def 的上面)

6.2.1、常见三兄弟

  • @staticmethod —— 让方法失去自动绑定,变成普通函数。
  • @classmethod —— 把“类对象”作为第一参数注入。
  • @property —— 把方法伪装成数据属性,实现 getter。

前两种已在“类方法与静态方法”中提过,所以此处只举例@property。它可以实现像读字段一样 obj.x 取值;像写字段一样 obj.x = value 赋值(配合 @x.setter);像删字段一样 del obj.x 删除(配合 @x.deleter

class Circle:
    """圆类,演示@property用法"""
    
    def __init__(self, radius):
        self._radius = radius  # 使用下划线表示受保护的属性,只是一种约定俗成的写法。
    
    @property
    def radius(self):
        """radius属性的getter方法"""
        print("获取半径值")
        return self._radius
    
    @radius.setter
    def radius(self, value):
        """radius属性的setter方法"""
        if value <= 0:
            raise ValueError("半径必须是正数")
        print("设置半径值")
        self._radius = value
    @radius.deleter
    def radius(self):
        """radius属性的deleter方法"""
        print("删除半径值")
        del self._radius
    @property
    def area(self):
        """计算圆的面积(只读属性)"""
        return 3.14159 * self._radius ** 2

# 使用属性装饰器
circle = Circle(5)
print(circle.radius)  # 输出: 获取半径值 \n 5
print(circle.area)    # 输出: 78.53975
del circle.radius     # 删除半径,此时该实例就没有'radius'属性了

6.2.2、自定义方法装饰器

与普通函数装饰器没区别,只是作用域在类里。

def debug(func):
    def wrapper(self, *a, **kw):
        print(f"[DEBUG] {func.__name__} called")
        return func(self, *a, **kw)
    return wrapper

class BankAccount:
    @debug
    def withdraw(self, amt):
        ...

7、特殊方法(魔术方法)

在 Python 里,所有以 双下划线包围(__name__ 形式)的方法统称 特殊方法(special methods),江湖上又叫 魔术方法(magic methods)。它们不是给你直接调用的,而是被 Python 解释器在特定场景下自动触发,从而让自定义类无缝地融入 Python 的语法糖与运行时机制。

class Book:
    """演示特殊方法的书籍类"""
    
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages
    
    def __str__(self):
        """定义对象的字符串表示(对用户友好)"""
        return f"《{self.title}》 - {self.author}"
    
    def __len__(self):
        """定义len()函数的行为"""
        return self.pages
    
    def __eq__(self, other):
        """定义==操作符的行为"""
        return self.title == other.title and self.author == other.author
    
    def __add__(self, other):
        """定义+操作符的行为"""
        return Book(
            title=f"{self.title} & {other.title}",
            author=f"{self.author} 和 {other.author}",
            pages=self.pages + other.pages
        )

# 使用特殊方法
book1 = Book("Python编程", "Guido", 300)
book2 = Book("算法导论", "Knuth", 500)

print(book1)        # 输出: 《Python编程》 - Guido,等价于print(str(book1))
print(len(book1))   # 输出: 300
print(book1 + book2)  # 输出: 《Python编程 & 算法导论》 - Guido 和 Knuth

8、组合与聚合

组合是‘生同生,死同死’;聚合是‘你先走,我不留’。

8.1、组合示例:Car 与 Engine

class Engine:
    def start(self):
        print("engine starts")

class Car:
    def __init__(self):
        # Engine 在 Car 内部创建
        self._engine = Engine()

    def run(self):
        self._engine.start()

    # Car 消亡时,_engine 被垃圾回收
  • Engine 的生存期完全由 Car 控制;外部拿不到这个 Engine 实例。

  • 如果删除 Car 实例,Engine 也随即被销毁(除非别处还有引用)。

8.2、聚合实例

class Student:
    def __init__(self, name):
        self.name = name

class University:
    def __init__(self, name):
        self.name = name
        self.students = []          # 只是引用列表

    def enroll(self, student: Student):
        self.students.append(student)

# 学生可以先于大学而存在
alice = Student("Alice")
mit = University("MIT")
mit.enroll(alice)

# 大学关门,学生仍在
del mit
print(alice.name)   # 仍能访问
  • University 不创建 Student 对象,也不负责销毁。

  • 学生可以转校、退学,生命周期独立于大学。

(补充注意:Python 没有真正的私有生命周期控制,全靠约定与引用计数:如果整体把部分放在内部列表 / 属性且不暴露出去,就形成“组合”;如果通过构造函数/方法参数传进来,整体只保留引用,则更像“聚合”。)

9、抽象基类 (ABC)

abc(Abstract Base Classes)让你可以“先订协议、后实现”,既能在运行期拦住漏实现的方法,也能在静态检查阶段(mypy、Pyright)发现错误。

(补充说明:mypy、Pyright是Python 的静态类型检查器,功能:在 代码运行之前 帮你发现类型相关的 bug、拼写错误、接口不匹配等问题,而不像传统 Python 那样必须等程序跑到那一行才抛异常。)

from abc import ABC, abstractmethod

class Shape(ABC):                 # ① 继承 ABC
    @abstractmethod               # ② 标记抽象方法
    def area(self) -> float:      # 没有实现体
        ...

class Circle(Shape):
    def __init__(self, r: float):
        self.r = r
    def area(self) -> float:      # ③ 必须实现,否则实例化抛 TypeError
        return 3.1416 * self.r ** 2

c = Circle(2)
print(c.area())                   # 12.5664

10、类的字符串表示

在 Python 里,把一个对象“变成字符串”有两条官方通道:

  1. __repr__(self) —— 给开发者看,目标是“一眼能重建对象”

  2. __str__(self) —— 给终端用户看,目标是“可读、漂亮”

  3. 作用:没有 __repr__ / __str__,你的对象在任何需要“显示”的场合都会变成一串内存地址,调试靠猜、日志靠搜、测试靠断点、用户看不懂。

class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y

    # 给调试用的“官方字符串”
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

    # 给最终用户看的“友好字符串”
    def __str__(self):
        return f"⟨{self.x}, {self.y}⟩"

v = Vector(3, 4)
print(repr(v))  # Vector(3, 4)
print(str(v))   # <3, 4>,未定义__str__时,返回__repr__(反过来不行)

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐