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