类与对象|第五部分:面向对象编程 (Object-Oriented Programming - OOP)
欢迎来到Python世界的“创世纪”!在此之前,我们一直在使用Python“神”已经为我们创造好的事物:字符串 str
、列表 list
、字典 dict
等。我们像一个使用者,遵循着它们的规则。
但你是否曾想过,如果我也能创造属于自己的、全新的数据类型呢?比如,一个“汽车”类型,它有自己的“颜色”和“品牌”(数据),还能执行“启动引擎”、“加速”等操作(行为)。或者一个“用户”类型,它有“用户名”和“等级”,还能“登录”和“发帖”。
这就是**面向对象编程(OOP)**的核心思想:将数据和操作这些数据的函数(我们称之为方法)捆绑在一起,形成一个独立的、有机的整体——对象。
这不仅仅是一种新的语法,更是一种全新的世界观。你将从一个听从指令的“过程执行者”,转变为一个创造万物的“上帝”,在你的代码世界里设计、创造并指挥无数个相互协作的对象。让我们开始创造你的第一个“物种”吧!
10.1 OOP基本概念 (类, 对象, 实例)
在进入代码之前,我们必须理解三个核心概念,它们是OOP世界的基石。
-
类 (Class): 蓝图或模板。它定义了一个“物种”应该具有的共同属性(数据)和行为(方法)。例如,“汽车”这个类,它的蓝图上会写着:所有汽车都应该有“颜色”、“品牌”属性,都应该能“启动”和“刹车”。类本身不是一个实体,它只是一种规范。
-
对象 (Object): 根据蓝图创造出来的实体。一辆红色的法拉利、一辆蓝色的福特,它们都是“汽车”类的具体对象。每个对象都拥有类所定义的属性和方法,但它们的属性值(比如颜色)可以是不同的。
-
实例 (Instance): “对象”的另一种称呼。当我们说“
my_car
是Car
类的一个实例”时,意思和“my_car
是Car
类的一个对象”完全一样。实例化就是根据类创建对象的过程。
一句话总结:我们定义一个类
(蓝图),然后通过这个类来实例化
出很多个对象
(实体)。
10.2 定义类 (class
关键字)
在Python中,我们使用 class
关键字来定义一个类。按照惯例(PEP 8规范),类名通常使用驼峰命名法 (PascalCase),即每个单词的首字母都大写。
# 定义一个最简单的“猫”类
# 这张蓝图目前是空的,但它已经是一个合法的类了
class Cat:
pass # pass 是一个占位符,表示什么都不做
# 实例化:根据 Cat 类的蓝图,创建两个具体的猫对象
cat1 = Cat()
cat2 = Cat()
print(cat1) # 输出: <__main__.Cat object at 0x...> (一个Cat对象,以及它在内存中的地址)
print(cat2) # 输出: <__main__.Cat object at 0x...> (另一个Cat对象,地址不同)
10.3 构造函数 (__init__
) 与实例方法
我们空的“猫”蓝图还不够实用。我们希望每只猫被“创造”出来的时候,就应该有自己的名字和年龄。这就要用到一个非常特殊的“魔法方法”——__init__
。
-
__init__(self, ...)
(构造函数/初始化方法): 这个方法在创建对象时会自动被调用。它的主要任务就是完成对象的初始化工作,比如给对象的属性赋初始值。self
参数: 这是__init__
以及所有实例方法的第一个参数。它代表对象本身。当cat1 = Cat("咪咪", 2)
被执行时,Python会自动把cat1
这个对象传给self
。你可以把它理解为第一人称的“我”。
-
实例方法 (Instance Method): 定义在类中的函数,它们是对象的“行为”。实例方法的第一个参数必须是
self
,以便在方法内部可以访问和操作对象自身的属性。
class Cat:
# 构造函数:当一只猫被创建时,这个方法会被自动调用
def __init__(self, name, age):
# self.name = name 的意思是:
# “把传入的 name 参数的值,赋给‘我’(self)这个对象的 name 属性”
print(f"一只叫 {name} 的小猫诞生了!")
self.name = name
self.age = age
# 实例方法:定义猫的行为
def meow(self):
# 在方法内部,通过 self 访问自己的属性
print(f"{self.name} 说:喵~ 我今年 {self.age} 岁了。")
# 实例化时,直接在类名后的括号里传入 __init__ 需要的参数 (除了 self)
cat1 = Cat("咪咪", 2)
cat2 = Cat("小黑", 3)
# 调用实例方法
cat1.meow() # 输出: 咪咪 说:喵~ 我今年 2 岁了。
cat2.meow() # 输出: 小黑 说:喵~ 我今年 3 岁了。
10.4 属性 (实例属性 vs 类属性)
对象的“数据”我们称之为属性。属性分为两种:
-
实例属性 (Instance Attribute): 每个对象独有的属性。它们通常在
__init__
方法中通过self.attribute = value
的方式定义。比如,cat1
的name
是 "咪咪",cat2
的name
是 "小黑",它们互不影响。 -
类属性 (Class Attribute): 所有对象共享的属性。它直接定义在类中,但在所有方法之外。类属性就像是写在“蓝图”上的公共信息。
class Cat:
# 这是类属性,所有 Cat 的实例都共享这个属性
species = "Felis catus" # 猫的学名
def __init__(self, name, age):
# 这些是实例属性,每只猫都不同
self.name = name
self.age = age
# 实例化
cat1 = Cat("咪咪", 2)
cat2 = Cat("小黑", 3)
# 访问实例属性
print(f"{cat1.name}的年龄是 {cat1.age}") # 输出: 咪咪的年龄是 2
# 访问类属性
# 可以通过类名访问
print(f"猫的科学名称是: {Cat.species}")
# 也可以通过实例访问
print(f"{cat1.name} 的物种是: {cat1.species}")
10.5 访问修饰符与封装 (公有、保护 _
、私有 __
)
封装 (Encapsulation) 是OOP的三大支柱之一。它的核心思想是:隐藏对象的内部实现细节,只对外暴露必要的接口。
想象一下开车:你只需要操作方向盘、油门、刹车(接口),而不需要关心发动机内部的活塞如何运动(实现细节)。封装能保护数据不被随意修改,让代码更安全、更易于维护。
Python没有像Java或C++那样严格的 public
, private
关键字,但它有一套命名约定来表示属性的可见性:
-
公有 (Public): 默认情况下,所有属性和方法都是公有的。可以在任何地方被访问。
self.name = name
-
保护 (Protected): 以单下划线
_
开头。这是一种约定,告诉其他程序员:“这是一个内部属性,不建议在类的外部直接访问,但如果你非要访问,我也拦不住你。”
self._mood = "happy"
-
私有 (Private): 以双下划线
__
开头。这不仅仅是约定,Python会对其进行名称改写 (Name Mangling)。如果一个属性叫__balance
,在类的外部,你无法通过object.__balance
访问它。Python会自动把它重命名为_ClassName__balance
。这提供了一种更强的保护机制。
经典案例:银行账户
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner # 公有属性
self._account_type = "checking" # 保护属性
self.__balance = balance # 私有属性
def deposit(self, amount):
"""存款 (公有方法,安全的接口)"""
if amount > 0:
self.__balance += amount
print(f"成功存款 {amount} 元。")
else:
print("存款金额必须为正数!")
def withdraw(self, amount):
"""取款 (公有方法,安全的接口)"""
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"成功取款 {amount} 元。")
else:
print("取款金额无效或余额不足!")
def get_balance(self):
"""提供一个只读的接口来查看余额"""
return self.__balance
# 创建一个账户对象
acc = BankAccount("张三", 1000)
# 尝试直接修改余额 (错误的做法!)
# acc.__balance -= 500 # 这会报错!AttributeError: 'BankAccount' object has no attribute '__balance'
# 因为 Python 已经把它改名为 _BankAccount__balance
# 正确的做法:通过我们提供的公有方法来操作
acc.deposit(500)
acc.withdraw(200)
# 通过安全的接口查看余额
current_balance = acc.get_balance()
print(f"{acc.owner} 的当前余额是: {current_balance} 元。")
恭喜你!你已经成功创造了你的第一个“物种”,并掌握了OOP的基石——类与对象。你学会了如何用 class
定义蓝图,用 __init__
初始化对象,用 self
引用自身,并用封装来保护你的数据。
但这仅仅是OOP奇妙世界的开始。我们创造的“物种”目前还是孤立的。现实世界中,物种之间存在着“继承”关系(比如,“金毛”是“狗”的一种),也存在着“多态”现象(比如,猫和狗都会“叫”,但叫声不同)。
在下一章,我们将深入探索OOP的三大核心特性——继承、多态、抽象,你将学会如何构建复杂的“物种类族”,让你的代码世界变得更加生动和强大!准备好,迎接真正的“进化”吧!