Python实战第11期:面向对象编程基础

引言:什么是面向对象编程?

在前面的课程中,我们编写的都是面向过程的程序:先做什么,再做什么。但现实世界是由一个个对象组成的,每个对象都有自己的属性和行为。

比如:

  • 一辆汽车有颜色、品牌、速度等属性,也有启动、加速、刹车等行为
  • 一个学生有姓名、年龄、成绩等属性,也有学习、考试等行为

面向对象编程(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)  # 输出:小白(不受影响)

七、本篇小结

今天,我们完成了以下学习内容:

  1. 类和对象:

    • 类是模板,对象是实例
    • 使用 class 定义类
    • 一个类可以创建多个对象
  2. 属性和方法:

    • 实例属性:每个对象独立
    • 类属性:所有对象共享
    • 实例方法:第一个参数是self
    • 类方法和静态方法
  3. __init__方法:

    • 构造方法,创建对象时自动调用
    • 用于初始化对象属性
    • 支持默认参数和关键字参数
  4. 封装:

    • 公有、私有、受保护的属性和方法
    • 使用 @property 装饰器

请记住这三个结论:

  1. 类是模板,对象是实例:类定义共性,对象保存个性
  2. self代表对象本身:实例方法的第一个参数必须是self
  3. __init__初始化对象:创建对象时自动调用,用于设置初始状态

下期预告

在第12期中,我们将学习Python的类的继承与多态,包括:

  • 继承的概念和语法
  • 方法重写
  • 多态的应用
  • super()函数的使用

敬请期待!


✍坚持原创,求关注,点赞,收藏

相关推荐
用户693717500138444 分钟前
当我说 Claude 搭配 GLM-5.3-Flash 太好用了,同事却提醒我:你可能正在失去对代码的掌控
android·后端·ios
喵个咪1 小时前
GoWind ShortPlay 拆解:不用模型的推荐闭环——埋点回流、三路召回与已看去重
后端·架构·go
喵个咪1 小时前
GoWind ShortPlay 深度拆解:三服务 BFF + Core,一条请求的完整工程链路
后端·架构·go
devilnumber1 小时前
springboot的保护机制怎么实现的,为何能做到服务不宕机
java·spring boot·后端
未秃头的程序猿1 小时前
一次重复扣款的复盘:分布式锁加错了维度,比不加锁更危险
java·redis·后端
估值探索者1 小时前
【Python量化技术指标实战 02】MACD金叉也分强弱?手工算一次 DIF/DEA 就懂了
java·开发语言·python·股票数据·股票数据api接口
魔都吴所谓1 小时前
【我的开源】Python实现一个离线 PDF 工具箱:架构、骑缝章与踩坑记录
python·pdf·开源
彭亚川Allen1 小时前
从零手搓 RAG 之分块策略
前端·后端·面试