Python面向对象编程:类与对象基础
面向对象编程(OOP)是编程的重要范式,Python是一门完全面向对象的语言。本篇将学习类与对象的基本概念、定义和使用。
一、什么是面向对象?
1. 面向过程 vs 面向对象
- 面向过程:以「动作」为中心,关注「怎么做」,用函数组织代码
- 面向对象:以「事物」为中心,关注「谁来做」,用类和对象组织代码
生活类比:
- 面向过程:把大象装进冰箱 → 开门 → 装大象 → 关门(关注步骤)
- 面向对象:冰箱有开门/关门方法,大象有被装的属性(关注对象)
2. 类与对象的关系
- 类(Class):是模板/蓝图,描述一类事物的共同特征和行为
- 对象(Object):是类的具体实例,是真实存在的个体
比如:
- 「人类」是一个类,「张三」是人类的一个对象
- 「汽车」是一个类,「你家那辆车」是汽车的一个对象
二、定义类和创建对象
1. 基本语法
python
class 类名:
"""类文档字符串"""
def __init__(self, 参数列表):
"""初始化方法,创建对象时自动调用"""
self.属性名 = 参数
def 方法名(self, 参数列表):
"""实例方法"""
方法体
2. 第一个类:Person
python
class Person:
"""人类"""
def __init__(self, name, age):
"""初始化方法"""
self.name = name # 实例属性
self.age = age
def say_hello(self):
"""打招呼方法"""
print(f"你好,我叫{self.name},今年{self.age}岁")
def have_birthday(self):
"""过生日,年龄+1"""
self.age += 1
print(f"生日快乐!{self.name}现在{self.age}岁了")
# 创建对象(实例化)
p1 = Person("小明", 18)
p2 = Person("小红", 17)
# 调用方法
p1.say_hello() # 你好,我叫小明,今年18岁
p2.say_hello() # 你好,我叫小红,今年17岁
p1.have_birthday() # 生日快乐!小明现在19岁了
# 访问属性
print(p1.name) # 小明
print(p1.age) # 19
3. self是什么?
self 代表当前对象本身,类似于其他语言中的 this。
- 定义方法时,第一个参数必须是self(约定俗成,也可以叫别的名字,但不建议)
- 调用方法时,不需要手动传self,Python会自动传入
- 通过self可以访问对象的属性和其他方法
python
class Demo:
def show(self):
print(f"self的id: {id(self)}")
d = Demo()
d.show() # self的id: 140...
print(f"d的id: {id(d)}") # d的id: 140...(和self相同)
三、属性详解
1. 实例属性 vs 类属性
python
class Dog:
# 类属性:所有实例共享
species = "犬科动物"
count = 0 # 计数器
def __init__(self, name, age):
# 实例属性:每个实例独有
self.name = name
self.age = age
Dog.count += 1 # 创建一个实例,计数器+1
# 创建实例
d1 = Dog("旺财", 3)
d2 = Dog("大黄", 5)
# 实例属性(每个对象不同)
print(d1.name) # 旺财
print(d2.name) # 大黄
# 类属性(所有对象共享)
print(d1.species) # 犬科动物
print(d2.species) # 犬科动物
print(Dog.count) # 2(创建了2个实例)
# 修改类属性(通过类名修改,影响所有实例)
Dog.species = "哺乳动物"
print(d1.species) # 哺乳动物
# 通过实例修改类属性(实际上是创建了同名实例属性,不影响其他实例)
d1.species = "小狗狗"
print(d1.species) # 小狗狗(实例属性)
print(d2.species) # 哺乳动物(还是类属性)
2. 属性访问控制
Python没有真正的私有属性,但有约定:
python
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner # 公开属性
self._balance = balance # 约定为"内部使用"(单下划线)
self.__password = "1234" # 名称修饰,外部不能直接访问(双下划线)
def get_balance(self):
"""获取余额(通过方法访问私有属性)"""
return self._balance
def deposit(self, amount):
"""存款"""
if amount > 0:
self._balance += amount
print(f"存款{amount}成功,余额{self._balance}")
else:
print("存款金额必须大于0")
account = BankAccount("小明", 1000)
print(account.get_balance()) # 1000
account.deposit(500) # 存款500成功,余额1500
# print(account.__password) # 报错,不能直接访问
print(account._BankAccount__password) # 可以通过这种方式访问(不推荐)
⚠️ Python的「私有」是约定大于强制,双下划线只是名称修饰,不是真正的访问控制。
3. @property装饰器
用property可以把方法变成属性访问,实现getter/setter。
python
class Student:
def __init__(self, name, score):
self.name = name
self._score = score
@property
def score(self):
"""获取分数(getter)"""
return self._score
@score.setter
def score(self, value):
"""设置分数(setter),可以加验证逻辑"""
if 0 <= value <= 100:
self._score = value
else:
raise ValueError("分数必须在0-100之间")
@property
def grade(self):
"""只读属性(只有getter,没有setter)"""
if self._score >= 90:
return "A"
elif self._score >= 80:
return "B"
elif self._score >= 60:
return "C"
else:
return "D"
s = Student("小明", 85)
print(s.score) # 85(像访问属性一样调用方法)
print(s.grade) # B
s.score = 95 # 通过setter修改
print(s.score) # 95
print(s.grade) # A
# s.score = 120 # 会抛出ValueError
四、方法详解
1. 实例方法
最常见的方法,第一个参数是self,可以访问实例属性和类属性。
python
class Example:
def instance_method(self):
print("这是实例方法", self)
2. 类方法
用 @classmethod 装饰,第一个参数是cls(代表类本身)。
python
class Person:
total = 0
def __init__(self, name):
self.name = name
Person.total += 1
@classmethod
def get_total(cls):
"""类方法:访问类属性"""
return cls.total
@classmethod
def from_string(cls, info_str):
"""类方法:作为备用构造函数"""
name = info_str.split("-")[0]
return cls(name) # cls相当于Person
print(Person.get_total()) # 0
p1 = Person("小明")
print(Person.get_total()) # 1
# 用类方法创建对象
p2 = Person.from_string("小红-18")
print(p2.name) # 小红
3. 静态方法
用 @staticmethod 装饰,不需要self或cls参数,就是普通函数放在类里。
python
class MathUtils:
@staticmethod
def is_even(n):
"""判断是否为偶数"""
return n % 2 == 0
@staticmethod
def max_of(a, b):
return a if a > b else b
# 不需要创建对象,直接通过类调用
print(MathUtils.is_even(4)) # True
print(MathUtils.max_of(3, 5)) # 5
4. 三种方法对比
| 方法类型 | 装饰器 | 第一个参数 | 能访问实例属性 | 能访问类属性 |
|---|---|---|---|---|
| 实例方法 | 无 | self | ✅ | ✅ |
| 类方法 | @classmethod | cls | ❌ | ✅ |
| 静态方法 | @staticmethod | 无 | ❌ | ❌ |
五、实战:银行账户类
python
class BankAccount:
"""银行账户类"""
# 类属性
bank_name = "Python银行"
total_accounts = 0
def __init__(self, owner, balance=0):
"""初始化账户"""
self.owner = owner
self.__balance = balance # 私有属性
self.transaction_history = [] # 交易记录
BankAccount.total_accounts += 1
@property
def balance(self):
"""只读余额属性"""
return self.__balance
def deposit(self, amount):
"""存款"""
if amount <= 0:
print("存款金额必须大于0")
return False
self.__balance += amount
self.transaction_history.append(("存款", amount))
print(f"存款成功:+{amount}元,当前余额:{self.__balance}元")
return True
def withdraw(self, amount):
"""取款"""
if amount <= 0:
print("取款金额必须大于0")
return False
if amount > self.__balance:
print("余额不足")
return False
self.__balance -= amount
self.transaction_history.append(("取款", amount))
print(f"取款成功:-{amount}元,当前余额:{self.__balance}元")
return True
def transfer(self, target, amount):
"""转账到另一个账户"""
if self.withdraw(amount):
target.deposit(amount)
print(f"转账成功:从{self.owner}转到{target.owner} {amount}元")
return True
return False
def show_history(self):
"""显示交易记录"""
print(f"\n{self.owner}的交易记录:")
for op, amount in self.transaction_history:
print(f" {op}: {amount}元")
print(f" 最终余额: {self.__balance}元")
@classmethod
def get_total_accounts(cls):
"""获取总账户数"""
return cls.total_accounts
# 使用示例
print(f"欢迎使用{BankAccount.bank_name}")
print(f"当前总账户数:{BankAccount.get_total_accounts()}")
# 创建账户
acc1 = BankAccount("小明", 1000)
acc2 = BankAccount("小红", 500)
# 操作
acc1.deposit(500)
acc1.withdraw(200)
acc1.transfer(acc2, 300)
# 查看记录
acc1.show_history()
acc2.show_history()
print(f"\n当前总账户数:{BankAccount.get_total_accounts()}")
六、常见坑点
-
__init__没有return:初始化方法不能返回值,只能返回None
-
忘记self:方法定义时第一个参数必须是self,否则调用时报错
-
类属性和实例属性混淆:修改类属性要用类名,通过实例修改会创建同名实例属性
-
可变对象作为类属性 :所有实例共享同一个列表/字典,修改会互相影响
pythonclass Demo: items = [] # 危险!所有实例共享 d1 = Demo() d2 = Demo() d1.items.append(1) print(d2.items) # [1](被影响了!)
七、课后练习
- 定义一个Circle类,包含半径属性,实现计算面积和周长的方法
- 定义一个Book类,包含书名、作者、价格属性,用@property实现价格不能为负数
- 定义一个Car类,包含品牌、速度属性,实现加速、减速方法(速度不能为负)
- 定义一个Student类,用类方法实现从字符串创建学生对象(如"张三,18,90")
八、小结
本篇学习了面向对象的基本概念、类的定义、实例属性与类属性、三种方法类型(实例方法/类方法/静态方法)以及@property装饰器。面向对象是编程的重要思维方式,下一篇将学习继承、多态等进阶特性。
📌 下一篇预告:《Python面向对象进阶:继承、多态与魔术方法》
本专栏持续更新中,欢迎点赞、收藏、关注三连支持!🐍