第十章:类和对象
10.1 什么是类?
类就像 一个制造小盒子的模具,你可以用这个模具制造出很多相似的小盒子(对象)。每个小盒子都有自己的特性(属性)和行为(方法),它们都遵循同一个设计蓝图。
我们可以理解为 把数据和函数包在一起。
# 定义一个简单的类
class Student:
"""学生类"""
# 数据
def __init__(self, name, score):
self.name = name # 属性:姓名
self.score = score # 属性:成绩
# 方法(函数)
def print_info(self):
"""方法:打印学生信息"""
print(f"学生姓名:{self.name},成绩:{self.score}")
10.2 类和对象的基本概念
10.2.1 类与对象的关系
- 类:是抽象的蓝图,定义了一组对象共有的属性和方法
- 对象:是类的实例,具有类所定义的属性和方法
# 使用类创建对象
student1 = Student("张三", 90)
student2 = Student("李四", 85)
# 每个对象都有自己的属性
print(student1.name) # 张三
print(student2.name) # 李四
# 调用对象的方法
student1.print_info() # 学生姓名:张三,成绩:90
student2.print_info() # 学生姓名:李四,成绩:85
10.2.2 类的组成
一个类通常包含:
- 属性:对象的特征或数据
- 方法:对象的行为或功能
- 构造函数:初始化对象的方法
10.3 定义类
10.3.1 基本语法
使用 class 关键字定义类:
class ClassName:
"""类的文档字符串"""
def __init__(self, 参数1, 参数2, ...):
# 初始化方法,用于设置对象的初始状态
self.属性1 = 参数1
self.属性2 = 参数2
# ...
def 方法1(self, 参数...):
# 方法定义
pass
10.3.2 初始化方法 __init__
__init__ 是一个特殊的方法,在创建对象时自动调用,用于初始化对象的属性:
class Dog:
"""狗类"""
def __init__(self, name, age):
self.name = name
self.age = age
self.breed = "未知" # 设置默认值
# 创建对象时必须提供__init__中必需的参数
my_dog = Dog("旺财", 3)
self 参数是对当前对象的引用,在类的方法中必须作为第一个参数:
- 通过
self可以访问对象的属性和其他方法 - 在调用方法时,不需要手动传递
self,Python会自动处理
10.4 属性和方法
10.4.1 实例属性
实例属性属于每个对象实例,每个对象都有自己的属性副本:
class Car:
"""汽车类"""
def __init__(self, brand, color):
self.brand = brand # 实例属性
self.color = color # 实例属性
car1 = Car("丰田", "白色")
car2 = Car("本田", "黑色")
print(car1.brand) # 丰田
print(car2.brand) # 本田
10.4.2 实例方法
实例方法是在类中定义的函数,第一个参数是 self,用于定义对象的行为:
class Car:
def __init__(self, brand, color):
self.brand = brand
self.color = color
self.speed = 0 # 初始速度
def accelerate(self, increment):
"""加速方法"""
self.speed += increment
print(f"{self.brand}车加速到{self.speed}km/h")
def brake(self, decrement):
"""刹车方法"""
self.speed -= decrement
if self.speed < 0:
self.speed = 0
print(f"{self.brand}车减速到{self.speed}km/h")
# 使用实例方法
my_car = Car("宝马", "蓝色")
my_car.accelerate(20) # 宝马车加速到20km/h
my_car.brake(5) # 宝马车减速到15km/h
10.4.3 类属性
类属性属于类本身,所有对象实例共享同一个类属性:
class Car:
# 类属性
wheels = 4 # 所有汽车都有4个轮子
def __init__(self, brand, color):
self.brand = brand # 实例属性
self.color = color # 实例属性
# 通过类名访问类属性
print(Car.wheels) # 4
# 通过对象实例也可以访问类属性
my_car = Car("奥迪", "红色")
print(my_car.wheels) # 4
# 修改类属性会影响所有实例
Car.wheels = 6
print(my_car.wheels) # 6
10.4.4 类方法
类方法使用 @classmethod 装饰器定义,第一个参数是 cls(表示类本身):
class Car:
wheels = 4
count = 0 # 记录创建的汽车数量
def __init__(self, brand, color):
self.brand = brand
self.color = color
Car.count += 1 # 每次创建对象时计数增加
@classmethod
def get_count(cls):
"""类方法:获取汽车数量"""
return cls.count
@classmethod
def set_wheels(cls, number):
"""类方法:设置轮子数量"""
cls.wheels = number
# 使用类方法
print(Car.get_count()) # 0(还没有创建对象)
car1 = Car("奔驰", "黑色")
car2 = Car("大众", "白色")
print(Car.get_count()) # 2
Car.set_wheels(6)
print(car1.wheels) # 6
10.4.5 静态方法
静态方法使用 @staticmethod 装饰器定义,不需要 self 或 cls 参数:
class Car:
def __init__(self, brand, color):
self.brand = brand
self.color = color
@staticmethod
def is_valid_brand(brand):
"""静态方法:检查品牌是否有效"""
valid_brands = ["丰田", "本田", "宝马", "奔驰", "奥迪"]
return brand in valid_brands
# 使用静态方法
print(Car.is_valid_brand("宝马")) # True
print(Car.is_valid_brand("比亚迪")) # False
# 也可以通过对象调用
my_car = Car("宝马", "蓝色")
print(my_car.is_valid_brand("丰田")) # True
10.5 封装
封装是将数据(属性)和行为(方法)包装在一起,并控制对内部数据的访问。
10.5.1 访问控制
在Python中,使用命名约定来控制访问:
- 公有属性和方法:正常命名,如
name、print_info() - 受保护的属性和方法:以单个下划线开头,如
_protected_var - 私有的属性和方法:以双下划线开头,如
__private_var
class BankAccount:
"""银行账户类"""
def __init__(self, account_holder, balance):
self.account_holder = account_holder # 公有属性
self._account_number = "123456789" # 受保护属性(约定上不直接访问)
self.__balance = balance # 私有属性(无法直接访问)
# 公有方法
def get_balance(self):
"""获取余额(公有方法)"""
return self.__balance
def deposit(self, amount):
"""存款"""
if amount > 0:
self.__balance += amount
return True
return False
def withdraw(self, amount):
"""取款"""
if 0 < amount <= self.__balance:
self.__balance -= amount
return True
return False
# 使用封装
account = BankAccount("张三", 1000)
print(account.account_holder) # 张三(可以访问公有属性)
# print(account.__balance) # 错误!无法直接访问私有属性
print(account.get_balance()) # 1000(通过公有方法访问私有属性)
account.deposit(500)
print(account.get_balance()) # 1500
account.withdraw(200)
print(account.get_balance()) # 1300
10.5.2 属性装饰器
使用 @property 装饰器可以创建只读属性,或者定义getter、setter方法:
class Circle:
"""圆类"""
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""radius属性的getter方法"""
return self._radius
@radius.setter
def radius(self, value):
"""radius属性的setter方法"""
if value >= 0:
self._radius = value
else:
raise ValueError("半径不能为负")
@property
def area(self):
"""计算面积(只读属性)"""
return 3.14159 * self._radius ** 2
@property
def circumference(self):
"""计算周长(只读属性)"""
return 2 * 3.14159 * self._radius
# 使用属性装饰器
circle = Circle(5)
print(circle.radius) # 5(调用getter方法)
circle.radius = 10 # 调用setter方法
print(circle.radius) # 10
print(circle.area) # 314.159(只读属性)
print(circle.circumference) # 62.8318(只读属性)
# circle.radius = -5 # 会抛出ValueError
10.6 继承
继承允许我们定义一个类,它继承另一个类的属性和方法。
10.6.1 基本继承
# 父类(基类)
class Animal:
"""动物类"""
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(f"{self.name}正在吃东西")
def sleep(self):
print(f"{self.name}正在睡觉")
# 子类(派生类)
class Dog(Animal):
"""狗类,继承自动物类"""
def __init__(self, name, age, breed):
# 调用父类的初始化方法
super().__init__(name, age)
self.breed = breed # 子类特有的属性
# 子类特有的方法
def bark(self):
print(f"{self.name}在汪汪叫")
# 重写父类的方法
def eat(self):
print(f"{self.name}正在吃狗粮")
# 使用继承
my_dog = Dog("旺财", 3, "金毛")
# 调用继承的方法
my_dog.eat() # 旺财正在吃狗粮(重写后的方法)
my_dog.sleep() # 旺财正在睡觉(继承的方法)
# 调用子类特有的方法
my_dog.bark() # 旺财在汪汪叫
print(my_dog.breed) # 金毛(子类特有的属性)
10.6.2 多重继承
一个类可以继承多个父类:
class Flyable:
"""可飞行的类"""
def fly(self):
print("正在飞行")
class Swimmable:
"""可游泳的类"""
def swim(self):
print("正在游泳")
class Duck(Flyable, Swimmable):
"""鸭子类,继承自Flyable和Swimmable"""
def __init__(self, name):
self.name = name
def quack(self):
print(f"{self.name}在嘎嘎叫")
# 使用多重继承
duck = Duck("唐老鸭")
duck.quack() # 唐老鸭在嘎嘎叫
duck.fly() # 正在飞行
duck.swim() # 正在游泳
10.6.3 方法解析顺序
当使用多重继承时,Python按照方法解析顺序来查找方法:
class A:
def test(self):
print("A")
class B(A):
def test(self):
print("B")
class C(A):
def test(self):
print("C")
class D(B, C):
pass
# 查看MRO
print(D.__mro__)
d = D()
d.test() # 输出B,按照MRO顺序查找
10.7 多态
多态是指不同类的对象对同一消息(方法调用)做出不同的响应:
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "汪汪!"
class Cat(Animal):
def speak(self):
return "喵喵!"
class Cow(Animal):
def speak(self):
return "哞哞!"
# 多态的例子
def animal_speak(animal):
print(animal.speak())
# 传入不同的对象,调用相同的方法,得到不同的结果
dog = Dog()
cat = Cat()
cow = Cow()
animal_speak(dog) # 汪汪!
animal_speak(cat) # 喵喵!
animal_speak(cow) # 哞哞!
10.8 特殊方法(魔术方法)
特殊方法以双下划线开头和结尾,用于实现类的特定行为:
10.8.1 常见特殊方法
class Book:
"""图书类"""
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
# 字符串表示
def __str__(self):
return f"《{self.title}》 by {self.author}"
def __repr__(self):
return f"Book('{self.title}', '{self.author}', {self.pages})"
# 长度
def __len__(self):
return self.pages
# 比较
def __eq__(self, other):
if isinstance(other, Book):
return self.title == other.title and self.author == other.author
return False
def __lt__(self, other):
"""小于比较,按页数比较"""
if isinstance(other, Book):
return self.pages < other.pages
return NotImplemented
# 使用特殊方法
book1 = Book("Python编程", "John", 300)
book2 = Book("算法导论", "Tom", 500)
print(book1) # 《Python编程》 by John(调用__str__)
print(repr(book1)) # Book('Python编程', 'John', 300)(调用__repr__)
print(len(book1)) # 300(调用__len__)
print(book1 == book2) # False(调用__eq__)
print(book1 < book2) # True(调用__lt__)
10.8.2 算术运算特殊方法
class Vector:
"""向量类"""
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""向量加法"""
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
return NotImplemented
def __sub__(self, other):
"""向量减法"""
if isinstance(other, Vector):
return Vector(self.x - other.x, self.y - other.y)
return NotImplemented
def __mul__(self, scalar):
"""向量数乘"""
if isinstance(scalar, (int, float)):
return Vector(self.x * scalar, self.y * scalar)
return NotImplemented
def __str__(self):
return f"Vector({self.x}, {self.y})"
# 使用算术运算特殊方法
v1 = Vector(2, 3)
v2 = Vector(1, 4)
v3 = v1 + v2 # 向量加法
print(v3) # Vector(3, 7)
v4 = v1 - v2 # 向量减法
print(v4) # Vector(1, -1)
v5 = v1 * 3 # 向量数乘
print(v5) # Vector(6, 9)
本章笔记:
- 类是创建对象的蓝图,定义了对象的属性和方法。
- 使用
class关键字定义类,使用__init__方法初始化对象。 - 类包含实例属性、实例方法、类属性和类方法。
- 封装通过访问控制保护对象内部数据。
- 继承允许子类继承父类的属性和方法,并可以重写或扩展。
- 多态允许不同类的对象对同一方法调用做出不同响应。
- 特殊方法(魔术方法)用于实现类的特定行为。
- 面向对象编程使代码更加模块化、可重用和易于维护。