Python 类的基本知识
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 里,把一个对象“变成字符串”有两条官方通道:
-
__repr__(self)—— 给开发者看,目标是“一眼能重建对象” -
__str__(self)—— 给终端用户看,目标是“可读、漂亮” -
作用:没有
__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__(反过来不行)
更多推荐


所有评论(0)