Python快速学习——第10章:类和对象

4 阅读8分钟

第十章:类和对象

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 装饰器定义,不需要 selfcls 参数:

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中,使用命名约定来控制访问:

  • 公有属性和方法:正常命名,如 nameprint_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)        # 10print(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):
        passclass 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__ 方法初始化对象。
  • 类包含实例属性、实例方法、类属性和类方法。
  • 封装通过访问控制保护对象内部数据。
  • 继承允许子类继承父类的属性和方法,并可以重写或扩展。
  • 多态允许不同类的对象对同一方法调用做出不同响应。
  • 特殊方法(魔术方法)用于实现类的特定行为。
  • 面向对象编程使代码更加模块化、可重用和易于维护。