Skip to content

🏡Python 的类和对象

一、 类和对象

  • 类(Class):类是对象的蓝图或模板,定义了对象的属性(数据)和行为(方法)。可以把它想象成一个“模具”。
  • 对象(Object):对象是类的实例,是根据类创建的实体。每个对象都有类中定义的属性和方法,但可以有不同的属性值。就像用模具做出来的具体产品。

1. 定义一个类

在 Python 中,使用 class 关键字定义类:

python
class 类名:
    # 属性(变量)
    # 方法(函数)
    pass
  • 类名通常使用大驼峰命名法(如 MyClass)。
  • 类中可以定义属性(数据)和方法(函数)。

2. 创建对象

通过类名加括号可以创建对象:

python
对象名 = 类名()

二、类的组成部分

1. 属性

属性是类中定义的变量,用来存储数据。通常通过 __init__ 方法(构造函数)初始化。属性有以下2种:

  • 实例属性:每个对象独有的属性,定义在 __init__ 中,使用 self.属性名
  • 类属性:所有对象共享的属性,定义在类中但不在方法内。

2. 方法

方法是类中定义的函数,描述对象的行为。类中的方法通常需要以 self 作为第一个参数,表示调用该方法的对象本身。

在 Python 类中__init__() 是一个特殊方法(构造函数),在创建对象时自动调用,用于初始化对象的属性。

3. 示例

下面是一个简单的例子,展示如何定义类、创建对象并使用属性和方法:

python
# 定义一个类
class Dog:
    # 构造函数,初始化属性
    def __init__(self, name, age):
        self.name = name  # 实例属性
        self.age = age

    # 定义方法
    def bark(self):
        return f"{self.name} 说:汪汪!"

    def get_age(self):
        return f"{self.name} 的年龄是 {self.age} 岁"

# 创建对象
dog1 = Dog("小黑", 3)  # 实例化对象
dog2 = Dog("小白", 5)

# 访问属性
print(dog1.name)  # 输出:小黑
print(dog2.age)   # 输出:5

# 调用方法
print(dog1.bark())      # 输出:小黑 说:汪汪!
print(dog2.get_age())   # 输出:小白 的年龄是 5 岁

以下是一个包含类属性和对象属性的示例程序:

python
class Dog:
    # 这是类属性,属于整个类,所有狗狗共享
    dog_cnt = 0  
    species = "Canis familiaris"

    def __init__(self, name):
        # 这是实例属性,每只狗都有自己的名字
        self.name = name  
        # 每创建一个对象,类属性计数+1
        Dog.dog_cnt += 1

# 创建两个狗狗对象
dog1 = Dog("小黑")
dog2 = Dog("小白")

# 访问类属性
print("狗狗总数:", Dog.dog_cnt)          # 输出:狗狗总数: 2
print("dog1的物种:", dog1.species)       # 输出:dog1的物种: Canis familiaris
print("dog2的物种:", dog2.species)       # 输出:dog2的物种: Canis familiaris

# 访问实例属性
print("dog1的名字:", dog1.name)           # 输出:dog1的名字: 小黑
print("dog2的名字:", dog2.name)           # 输出:dog2的名字: 小白

# 修改实例属性,不影响类属性
dog1.species = "New Species"
print("修改后dog1的物种:", dog1.species)  # 输出:修改后dog1的物种: New Species
print("dog2的物种仍是:", dog2.species)   # 输出:dog2的物种仍是: Canis familiaris
print("类Dog的物种:", Dog.species)       # 输出:类Dog的物种: Canis familiaris

4. self 是什么?

当你调用一个对象的方法时,比如:

python
dog1.bark()

其实 Python 自动帮你转换成了:

python
Dog.bark(dog1)

所以,你写的方法定义一定要带 self,这样才能接收这个“自动传入的对象”。(这个self只是一个参数的名字,和函数参数一样,是可以自定义的,但是不建议修改)

python
class Dog:
    def bark(self):
        print(f"{self} 说:汪汪!")

dog1 = Dog()
dog1.bark()          # Python 自动变成 Dog.bark(dog1)
Dog.bark(dog1)       # 这两句等价

三、 类和对象的特点

  • 多态:不同类的对象可以调用相同名称的方法,行为不同。
  • 封装:隐藏内部实现,保护数据。
  • 继承:复用代码,扩展功能。

1. 封装(Encapsulation)

定义:把对象的属性和方法隐藏起来,只暴露必要接口。

为什么要用私有属性?

  • 私有属性是在属性名前加两个下划线 __,如 self.__address
  • 它不能被外部直接访问或修改,起到保护数据的作用
  • 防止外部代码随意改变对象内部状态,避免程序出现错误或不安全的情况
  • 只能通过类内部的方法(getter/setter)来访问和修改私有属性

比喻:房子的内部布局和装修细节不允许外人随便更改,只有房主(类内部方法)能操作。

示例代码:

python
class House:
    def __init__(self, address, size):
        self.__address = address  # 私有属性
        self.size = size          # 公开属性

    def get_address(self):
        return self.__address

    def set_address(self, new_address):
        # 这里可以加逻辑限制地址格式,保护数据安全
        self.__address = new_address

    def show_info(self):
        print(f"房子位于 {self.__address},面积是 {self.size} 平方米")

house = House("幸福小区A栋", 100)

print(house.size)           # 可以直接访问
print(house.get_address())  # 通过方法访问私有属性

# 下面代码会报错,不能直接访问私有属性
# print(house.__address)   

house.set_address("阳光小区B栋")
print(house.get_address())

2. 继承(Inheritance)

定义:子类继承父类属性和方法,复用代码并扩展新功能。

比喻:别墅设计图继承普通房设计图,还加了泳池、花园等。

代码示例:

python
class House:
    def __init__(self, address, size):
        self.address = address
        self.size = size

    def show_info(self):
        print(f"房子位于 {self.address},面积是 {self.size} 平方米")

class Villa(House):  # Villa : 别墅
    def __init__(self, address, size, has_pool):
        super().__init__(address, size)  # 调用父类构造函数初始化属性
        self.has_pool = has_pool

    def show_info(self):
        super().show_info()
        print(f"是否有泳池:{'有' if self.has_pool else '没有'}")

# 测试:
villa = Villa("绿野别墅区", 200, True)
villa.show_info()

House 类中定义了一个 show_info() 方法,用于打印房子的地址和面积。而在 Villa 类中,虽然 Villa 继承了 House,也自动继承了这个方法,但我们又重新定义了一个同名的方法 show_info(),这就构成了方法的重写(override)

python
super().__init__(address, size)

作用

  • 调用父类的构造函数,初始化父类定义的属性
  • 避免子类重复写父类已有的初始化代码
  • 确保父类初始化逻辑被执行,避免属性缺失

如果不调用它,子类对象不会拥有父类的属性,程序会出错:

python
class House:
    def __init__(self, address):
        self.address = address

class Villa(House):
    def __init__(self, has_pool):
        self.has_pool = has_pool

villa = Villa(True)
print(villa.has_pool)    # ✅ 输出 True
print(villa.address)     # ❌ 报错:没有 address 属性

❗ 为什么报错?

虽然 Villa 继承了 House,但是它重写了 __init__() 方法,没有调用父类的 __init__(),所以:

  • self.address = address 根本没执行
  • 对象中也就没有 address 这个属性
  • 直接访问 villa.address 就会报错

如果子类没有重写 __init__() 方法,那就会自动使用父类的构造函数。

以下程序片段不重写 __init__()函数,所以它默认调用了父类的构建函数,也就要求输入一个参数作为address

python
class House:
    def __init__(self, address):
        self.address = address

class Villa(House):
    pass

villa = Villa("602")     # 必须输入一个值 作为 address 参数
print(villa.address)     # ✅ 输出 602

3. 多态(Polymorphism)

定义:不同对象对同一方法调用做出不同响应。

比喻:开门动作,普通房是手动门,别墅是自动门。

代码示例

python
class House:
    def open_door(self):
        print("普通房门被打开")

class Villa(House):
    def open_door(self):
        print("别墅自动门被打开")

def open_any_door(house):
    house.open_door()

house = House()
villa = Villa()

open_any_door(house)  # 输出:普通房门被打开
open_any_door(villa)  # 输出:别墅自动门被打开

4. 综合示例

python
class House:
    def __init__(self, address, size):
        self.__address = address
        self.size = size

    def get_address(self):
        return self.__address

    def set_address(self, new_address):
        self.__address = new_address

    def show_info(self):
        print(f"房子位于 {self.__address},面积是 {self.size} 平方米")

    def open_door(self):
        print("普通房门被打开")

class Villa(House):
    def __init__(self, address, size, has_pool):
        super().__init__(address, size)
        self.has_pool = has_pool

    def show_info(self):
        super().show_info()
        print(f"是否有泳池:{'有' if self.has_pool else '没有'}")

    def open_door(self):
        print("别墅自动门被打开")

def open_any_door(house):
    house.open_door()

# 测试代码
house = House("幸福小区A栋", 100)
print(house.get_address())
house.set_address("阳光小区B栋")
print(house.get_address())
house.show_info()

print("------")

villa = Villa("绿野别墅区", 200, True)
villa.show_info()

print("------")

open_any_door(house)
open_any_door(villa)

四、练习题:类和对象

  1. 定义一个类 Person,不需要写任何属性和方法,然后创建一个对象 p1
  2. Person 类中添加一个构造函数,初始化两个属性:姓名(name)和年龄(age)。然后创建对象并打印这两个属性。
  3. Person 类中添加一个方法 say_hello(),打印出 "你好,我是XXX",XXX 是对象的名字。注意使用 self
  4. Person 类添加一个类属性 species = "Human",并创建两个对象 p1p2,分别打印它们的 species。然后修改 p1species"Alien",再打印 p1.speciesp2.species,看看有什么不同。
  5. 修改 Person 类,使 name 属性变成私有的(__name),并提供两个方法:get_name()set_name(new_name)。创建对象后尝试直接访问私有属性,再通过方法修改并访问。
  6. 创建一个 Student 类,包含属性:姓名、年级和成绩,并添加方法 show_info() 显示学生信息。要求使用 __init__self 正确初始化并访问。
  7. 创建一个父类 Animal,包含属性 name 和方法 speak()。再创建一个子类 Cat,继承自 Animal,并重写 speak() 方法,打印 "喵喵叫"。
  8. 在上一个题的 Cat 类中,使用 super().__init__(name) 调用父类的构造函数初始化 name 属性。然后调用 show_info()speak()
  9. 定义一个函数 make_animal_speak(animal),接收一个 Animal 类型的对象,调用其 speak() 方法。创建多个子类(Dog, Cat, Duck),都继承自 Animal 并重写 speak(),测试函数的多态效果。
  10. 设计一个基类 House,包含私有属性 __address、公开属性 size 和方法 show_info()open_door()。再定义一个子类 Villa,增加属性 has_pool,重写 show_info()open_door() 方法。要求使用封装、继承、super() 和多态函数 show_house(h) 进行测试。

💬 与我联系 QQ:774165314 | 微信:Yonas_Luo