
引言:什么是面向对象编程?
在前面的课程中,我们编写的都是面向过程的程序:先做什么,再做什么。但现实世界是由一个个对象组成的,每个对象都有自己的属性和行为。
比如:
- 一辆汽车有颜色、品牌、速度等属性,也有启动、加速、刹车等行为
- 一个学生有姓名、年龄、成绩等属性,也有学习、考试等行为
面向对象编程(OOP) 就是把现实世界的事物抽象成程序中的"对象",让代码更接近人类的思维方式。
面向对象编程有三大特性:封装、继承、多态。今天我们学习最基础的部分:类和对象。
一、类和对象
1. 什么是类和对象
类(Class) 是一个模板,定义了一类事物共有的属性和行为。
对象(Object) 是类的具体实例,是类的具体表现。
python
# 类:汽车的模板
class Car:
pass
# 对象:具体的汽车
car1 = Car() # 创建第一辆汽车
car2 = Car() # 创建第二辆汽车
print(car1) # 输出:<__main__.Car object at 0x...>
print(car2) # 输出:<__main__.Car object at 0x...>
print(type(car1)) # 输出:<class '__main__.Car'>
💡 提示:
- 类是抽象的模板,对象是具体的实例
- 一个类可以创建多个对象
- 每个对象都有自己独立的状态
2. 类的定义语法
python
# 基本语法
class 类名:
# 类体
pass
# 示例
class Person:
"""人类"""
pass
class Student:
"""学生类"""
pass
class Dog:
"""狗类"""
pass
💡 提示:
- 类名通常使用大驼峰命名法(PascalCase)
- 类应该有文档字符串说明其用途
3. 创建对象
python
# 定义类
class Dog:
"""狗类"""
pass
# 创建对象
dog1 = Dog()
dog2 = Dog()
# 检查类型
print(isinstance(dog1, Dog)) # 输出:True
print(type(dog1)) # 输出:<class '__main__.Dog'>
二、属性和方法
1. 实例属性
实例属性是属于对象的属性,每个对象都有自己独立的属性值。
python
class Dog:
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age # 实例属性
# 创建对象
dog1 = Dog("旺财", 3)
dog2 = Dog("小白", 2)
# 访问属性
print(f"{dog1.name}今年{dog1.age}岁") # 输出:旺财今年3岁
print(f"{dog2.name}今年{dog2.age}岁") # 输出:小白今年2岁
2. 修改属性
python
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
dog = Dog("旺财", 3)
print(f"修改前:{dog.name},{dog.age}岁")
# 直接修改属性
dog.age = 4
dog.name = "大黄"
print(f"修改后:{dog.name},{dog.age}岁")
3. 实例方法
实例方法 是属于对象的方法,第一个参数必须是 self。
python
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
"""狗叫"""
print(f"{self.name}:汪汪汪!")
def info(self):
"""显示信息"""
print(f"名字:{self.name},年龄:{self.age}")
# 创建对象并调用方法
dog = Dog("旺财", 3)
dog.bark() # 输出:旺财:汪汪汪!
dog.info() # 输出:名字:旺财,年龄:3
💡 提示:
self代表对象本身- 调用方法时不需要传递
self参数
4. 方法中访问和修改属性
python
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def birthday(self):
"""过生日,年龄加1"""
self.age += 1
print(f"{self.name}过生日了,现在{self.age}岁")
def rename(self, new_name):
"""改名"""
old_name = self.name
self.name = new_name
print(f"{old_name}改名为{new_name}")
dog = Dog("旺财", 3)
dog.birthday() # 输出:旺财过生日了,现在4岁
dog.rename("大黄") # 输出:旺财改名为大黄
dog.info() # 输出:名字:大黄,年龄:4
5. 类属性
类属性是属于类的属性,所有对象共享同一个值。
python
class Dog:
# 类属性
species = "犬科"
count = 0
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
Dog.count += 1 # 创建对象时计数
@classmethod
def get_count(cls):
"""获取狗的数量"""
return cls.count
# 创建对象
dog1 = Dog("旺财", 3)
dog2 = Dog("小白", 2)
dog3 = Dog("大黄", 4)
# 访问类属性
print(f"物种:{Dog.species}") # 输出:物种:犬科
print(f"狗的数量:{Dog.get_count()}") # 输出:狗的数量:3
# 通过对象访问类属性
print(f"物种:{dog1.species}") # 输出:物种:犬科
💡 提示:
- 类属性所有对象共享
- 修改类属性会影响所有对象
- 实例属性每个对象独立
6. 类方法和静态方法
python
class MathUtils:
# 类方法
@classmethod
def add(cls, a, b):
return a + b
# 静态方法
@staticmethod
def multiply(a, b):
return a * b
# 调用类方法
result1 = MathUtils.add(3, 5)
print(f"3 + 5 = {result1}") # 输出:3 + 5 = 8
# 调用静态方法
result2 = MathUtils.multiply(3, 5)
print(f"3 × 5 = {result2}") # 输出:3 × 5 = 15
💡 提示:
- 类方法第一个参数是
cls(类本身)- 静态方法没有特殊参数
- 类方法可以访问类属性,静态方法不能
三、__init__方法
1. 什么是__init__
__init__ 是构造方法,在创建对象时自动调用,用于初始化对象的属性。
python
class Person:
def __init__(self):
print("__init__被调用了")
self.name = "未知"
self.age = 0
# 创建对象时自动调用__init__
person = Person() # 输出:__init__被调用了
print(f"姓名:{person.name},年龄:{person.age}")
2. __init__的参数
python
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
print(f"创建了{self.name},{self.age}岁")
# 创建对象时传递参数
person1 = Person("小明", 20) # 输出:创建了小明,20岁
person2 = Person("小红", 22) # 输出:创建了小红,22岁
3. 默认参数
python
class Person:
def __init__(self, name="未知", age=0):
self.name = name
self.age = age
# 使用默认参数
person1 = Person() # 使用所有默认值
person2 = Person("小明") # 只传递name
person3 = Person("小红", 22) # 传递所有参数
print(f"{person1.name},{person1.age}岁") # 输出:未知,0岁
print(f"{person2.name},{person2.age}岁") # 输出:小明,0岁
print(f"{person3.name},{person3.age}岁") # 输出:小红,22岁
4. 关键字参数
python
class Person:
def __init__(self, name, age, city="未知"):
self.name = name
self.age = age
self.city = city
# 使用关键字参数
person = Person(age=20, name="小明", city="北京")
print(f"{person.name},{person.age}岁,来自{person.city}")
5. __init__中的验证
python
class Person:
def __init__(self, name, age):
# 验证参数
if not name:
raise ValueError("姓名不能为空")
if age < 0 or age > 150:
raise ValueError("年龄必须在0-150之间")
self.name = name
self.age = age
# 测试
try:
person = Person("", 20) # 会抛出异常
except ValueError as e:
print(f"错误:{e}")
try:
person = Person("小明", 200) # 会抛出异常
except ValueError as e:
print(f"错误:{e}")
四、封装
1. 公有属性和方法
默认情况下,属性和方法都是公有的,可以从外部访问。
python
class Person:
def __init__(self, name, age):
self.name = name # 公有属性
self.age = age # 公有属性
def info(self): # 公有方法
print(f"姓名:{self.name},年龄:{self.age}")
person = Person("小明", 20)
print(person.name) # 可以直接访问
person.info() # 可以直接调用
2. 私有属性和方法
使用双下划线 __ 开头的属性和方法是私有的,不能从外部直接访问。
python
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age # 私有属性
def __secret(self): # 私有方法
print("这是秘密")
def get_age(self): # 公有方法访问私有属性
return self.__age
def set_age(self, age): # 公有方法修改私有属性
if 0 <= age <= 150:
self.__age = age
else:
print("年龄无效")
person = Person("小明", 20)
# print(person.__age) # AttributeError
# person.__secret() # AttributeError
# 通过公有方法访问
print(person.get_age()) # 输出:20
person.set_age(25)
print(person.get_age()) # 输出:25
💡 提示 :Python的私有只是"名称改写",不是真正的私有。可以通过
_类名__属性名访问。
python
person = Person("小明", 20)
print(person._Person__age) # 输出:20(不推荐)
3. 受保护的属性和方法
使用单下划线 _ 开头的属性和方法是受保护的,表示"建议"不要从外部访问。
python
class Person:
def __init__(self, name, age):
self.name = name
self._age = age # 受保护属性
def _internal_method(self): # 受保护方法
print("内部方法")
person = Person("小明", 20)
print(person._age) # 可以访问,但不推荐
4. 使用@property装饰器
@property 装饰器可以让方法像属性一样访问。
python
class Person:
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):
"""获取年龄"""
return self._age
@age.setter
def age(self, value):
"""设置年龄"""
if 0 <= value <= 150:
self._age = value
else:
raise ValueError("年龄无效")
person = Person("小明", 20)
print(person.age) # 像属性一样访问,输出:20
person.age = 25 # 像属性一样赋值
print(person.age) # 输出:25
# person.age = 200 # 会抛出异常
五、实战练习
练习1:银行账户类
python
class BankAccount:
"""银行账户类"""
def __init__(self, owner, balance=0):
self.owner = owner
self.__balance = balance # 私有属性
def deposit(self, amount):
"""存款"""
if amount > 0:
self.__balance += amount
print(f"存入{amount}元,余额:{self.__balance}元")
else:
print("存款金额必须大于0")
def withdraw(self, amount):
"""取款"""
if amount <= 0:
print("取款金额必须大于0")
elif amount > self.__balance:
print("余额不足")
else:
self.__balance -= amount
print(f"取出{amount}元,余额:{self.__balance}元")
@property
def balance(self):
"""查询余额"""
return self.__balance
def info(self):
"""显示账户信息"""
print(f"账户:{self.owner},余额:{self.__balance}元")
# 测试
account = BankAccount("小明", 1000)
account.info()
account.deposit(500)
account.withdraw(200)
account.withdraw(2000) # 余额不足
print(f"当前余额:{account.balance}元")
练习2:学生管理系统
python
class Student:
"""学生类"""
def __init__(self, student_id, name, age):
self.student_id = student_id
self.name = name
self.age = age
self.courses = []
def enroll(self, course):
"""选课"""
if course not in self.courses:
self.courses.append(course)
print(f"{self.name}选择了{course}")
else:
print(f"{self.name}已经选过{course}")
def drop(self, course):
"""退课"""
if course in self.courses:
self.courses.remove(course)
print(f"{self.name}退选了{course}")
else:
print(f"{self.name}没有选{course}")
def info(self):
"""显示信息"""
print(f"学号:{self.student_id},姓名:{self.name},年龄:{self.age}")
print(f"已选课程:{', '.join(self.courses) if self.courses else '无'}")
class StudentManager:
"""学生管理类"""
def __init__(self):
self.students = []
def add_student(self, student):
"""添加学生"""
self.students.append(student)
print(f"添加了学生:{student.name}")
def find_student(self, student_id):
"""查找学生"""
for student in self.students:
if student.student_id == student_id:
return student
return None
def show_all(self):
"""显示所有学生"""
print(f"共有{len(self.students)}名学生:")
for student in self.students:
student.info()
print()
# 测试
manager = StudentManager()
s1 = Student("001", "张三", 20)
s2 = Student("002", "李四", 21)
manager.add_student(s1)
manager.add_student(s2)
s1.enroll("Python编程")
s1.enroll("数据结构")
s2.enroll("Python编程")
manager.show_all()
练习3:图形类
python
import math
class Circle:
"""圆形类"""
def __init__(self, radius):
self.radius = radius
@property
def area(self):
"""计算面积"""
return math.pi * self.radius ** 2
@property
def perimeter(self):
"""计算周长"""
return 2 * math.pi * self.radius
def info(self):
"""显示信息"""
print(f"圆形:半径={self.radius},面积={self.area:.2f},周长={self.perimeter:.2f}")
class Rectangle:
"""矩形类"""
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
"""计算面积"""
return self.width * self.height
@property
def perimeter(self):
"""计算周长"""
return 2 * (self.width + self.height)
def info(self):
"""显示信息"""
print(f"矩形:宽={self.width},高={self.height},面积={self.area:.2f},周长={self.perimeter:.2f}")
# 测试
circle = Circle(5)
circle.info()
rectangle = Rectangle(4, 6)
rectangle.info()
六、常见问题与排错
问题1:忘记self参数
原因 :实例方法的第一个参数必须是self 解决方法:添加self参数
python
# ❌ 错误
class Person:
def info(): # 缺少self
print("信息")
# ✅ 正确
class Person:
def info(self): # 添加self
print("信息")
问题2:属性未初始化
原因 :在__init__中没有初始化属性 解决方法:在__init__中初始化所有属性
python
# ❌ 错误
class Person:
def __init__(self, name):
self.name = name
# 忘记初始化age
def info(self):
print(f"{self.name},{self.age}岁") # AttributeError
# ✅ 正确
class Person:
def __init__(self, name, age):
self.name = name
self.age = age # 初始化age
def info(self):
print(f"{self.name},{self.age}岁")
问题3:类属性和实例属性混淆
原因 :不清楚类属性和实例属性的区别 解决方法:理解它们的区别
python
class Dog:
species = "犬科" # 类属性
def __init__(self, name):
self.name = name # 实例属性
dog1 = Dog("旺财")
dog2 = Dog("小白")
# 类属性所有对象共享
print(Dog.species) # 输出:犬科
print(dog1.species) # 输出:犬科
print(dog2.species) # 输出:犬科
# 修改类属性
Dog.species = "猫科"
print(dog1.species) # 输出:猫科(所有对象都变了)
# 实例属性每个对象独立
dog1.name = "大黄"
print(dog1.name) # 输出:大黄
print(dog2.name) # 输出:小白(不受影响)
七、本篇小结
今天,我们完成了以下学习内容:
-
类和对象:
- 类是模板,对象是实例
- 使用
class定义类 - 一个类可以创建多个对象
-
属性和方法:
- 实例属性:每个对象独立
- 类属性:所有对象共享
- 实例方法:第一个参数是self
- 类方法和静态方法
-
__init__方法:
- 构造方法,创建对象时自动调用
- 用于初始化对象属性
- 支持默认参数和关键字参数
-
封装:
- 公有、私有、受保护的属性和方法
- 使用
@property装饰器
请记住这三个结论:
- 类是模板,对象是实例:类定义共性,对象保存个性
- self代表对象本身:实例方法的第一个参数必须是self
- __init__初始化对象:创建对象时自动调用,用于设置初始状态
下期预告
在第12期中,我们将学习Python的类的继承与多态,包括:
- 继承的概念和语法
- 方法重写
- 多态的应用
- super()函数的使用
敬请期待!
✍坚持原创,求关注,点赞,收藏