一、为什么需要面向对象
面向对象程序设计(OOP)的核心思想是把数据和操作数据的行为封装在一起,用"对象"来模拟现实世界中的实体。Python 是一门支持多范式编程的语言,但它的面向对象特性非常自然、灵活,几乎一切皆对象。
二、类与对象:一切的起点
在讲方法之前,先明确类(class)和对象(object)的关系:类是对一类事物的抽象描述,对象是类的具体实例。
python
class Dog:
"""一个最简单的类"""
def __init__(self, name, age):
self.name = name
self.age = age
创建对象(实例化)
d = Dog("旺财", 3)
print(d.name, d.age) # 旺财 3
其中 __init__ 是构造方法,在创建对象时自动调用,用于初始化属性。
三、属性:实例属性与类属性
3.1 实例属性
实例属性定义在 __init__ 中,通过 self.xxx 赋值,每个对象各自独立。
python
class Student:
def __init__(self, name, score):
self.name = name # 实例属性
self.score = score # 实例属性
s1 = Student("小明", 90)
s2 = Student("小红", 85)
s1.score = 95
print(s1.score, s2.score) # 95 85,互不影响
3.2 类属性
类属性定义在类体中、方法之外,被所有实例共享,适合存放常量或全局统计信息。
python
class Student:
school = "第一中学" # 类属性
count = 0 # 类属性,用于统计实例数量
def __init__(self, name):
self.name = name
Student.count += 1
a = Student("小明")
b = Student("小红")
print(Student.school) # 第一中学
print(Student.count) # 2
注意:通过实例修改类属性只会创建同名实例属性,不会影响类本身。修改类属性应使用
类名.属性名。
3.3 私有属性与 property
Python 用下划线约定表示访问级别:_name 表示受保护,__name 表示私有(名称改写)。配合 @property 可以实现受控的属性访问。
python
class Account:
def __init__(self, balance):
self.__balance = balance # 私有属性
@property
def balance(self):
"""只读属性"""
return self.__balance
@balance.setter
def balance(self, value):
if value < 0:
raise ValueError("余额不能为负")
self.__balance = value
acc = Account(100)
acc.balance = 200
print(acc.balance) # 200
acc.balance = -1 # 抛出 ValueError
四、方法:实例方法、类方法与静态方法
4.1 实例方法
最常见的类型,第一个参数是 self,可以访问实例属性和类属性。
python
class Circle:
def __init__(self, r):
self.r = r
def area(self): # 实例方法
return 3.14159 * self.r ** 2
c = Circle(2)
print(c.area()) # 12.56636
4.2 类方法
用 @classmethod 装饰,第一个参数是 cls,常用于提供替代构造器。
python
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_string(cls, info):
"""从 'name-age' 字符串创建对象"""
name, age = info.split("-")
return cls(name, int(age))
p = Person.from_string("张三-20")
print(p.name, p.age) # 张三 20
4.3 静态方法
用 @staticmethod 装饰,既不需要 self 也不需要 cls,本质上是放在类命名空间里的普通函数。
python
class MathUtil:
@staticmethod
def is_even(n):
return n % 2 == 0
print(MathUtil.is_even(10)) # True
4.4 三种方法对比
| 类型 | 装饰器 | 第一个参数 | 典型用途 |
|---|---|---|---|
| 实例方法 | 无 | self | 操作实例数据 |
| 类方法 | @classmethod | cls | 替代构造器、操作类状态 |
| 静态方法 | @staticmethod | 无 | 工具函数、与实例无关的逻辑 |
五、继承:复用与扩展
5.1 基本继承
子类继承父类的属性和方法,并可在此基础上扩展或重写。
python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "......"
class Cat(Animal):
def speak(self): # 重写父类方法
return f"{self.name}:喵喵喵"
class Dog(Animal):
def speak(self):
return f"{self.name}:汪汪汪"
print(Cat("咪咪").speak()) # 咪咪:喵喵喵
print(Dog("旺财").speak()) # 旺财:汪汪汪
5.2 使用 super() 调用父类
子类重写 __init__ 时,通常需要调用 super().__init__() 完成父类初始化。
python
class Animal:
def __init__(self, name):
self.name = name
class Bird(Animal):
def init(self, name, can_fly):
super().init(name) # 调用父类构造方法
self.can_fly = can_fly
b = Bird("麻雀", True)
print(b.name, b.can_fly) # 麻雀 True
5.3 多重继承与 MRO
Python 支持多重继承,方法查找顺序由 MRO(方法解析顺序)决定,可用 类名.__mro__ 查看。
python
class A:
def hello(self):
return "A"
class B(A):
def hello(self):
return "B"
class C(A):
def hello(self):
return "C"
class D(B, C):
pass
print(D().hello()) # B
print([c.name for c in D.mro]) # ['D', 'B', 'C', 'A', 'object']
六、多态:同一接口,不同实现
多态指不同类的对象对同一方法调用做出不同响应。Python 是动态类型语言,天然支持"鸭子类型":只要对象有对应方法即可调用,不要求继承关系。
python
class Cat:
def speak(self):
return "喵"
class Dog:
def speak(self):
return "汪"
class Duck:
def speak(self):
return "嘎"
def make_sound(animal):
print(animal.speak())
for a in [Cat(), Dog(), Duck()]:
make_sound(a)
喵
汪
嘎
可以看到,make_sound 并不关心传入对象的具体类型,只关心它是否有 speak 方法。这正是多态带来的灵活性。
6.1 基于继承的多态
python
class Shape:
def area(self):
raise NotImplementedError
class Rectangle(Shape):
def init(self, w, h):
self.w, self.h = w, h
def area(self):
return self.w * self.h
class Circle(Shape):
def init(self, r):
self.r = r
def area(self):
return 3.14159 * self.r ** 2
shapes = [Rectangle(3, 4), Circle(2)]
for s in shapes:
print(f"{type(s).name} 面积:{s.area():.2f}")
Rectangle 面积:12.00
Circle 面积:12.57
七、综合实例:一个简单的员工管理系统
下面把方法、属性、继承与多态综合起来,实现一个可运行的示例。
python
class Employee:
"""员工基类"""
company = "示例科技" # 类属性
def __init__(self, name, base_salary):
self.name = name # 实例属性
self.base_salary = base_salary
def salary(self): # 实例方法,供子类重写
return self.base_salary
def __str__(self):
return f"{self.name}({type(self).__name__})月薪:{self.salary():.2f}"
class Engineer(Employee):
def init(self, name, base_salary, bonus):
super().init(name, base_salary)
self.bonus = bonus
def salary(self):
return self.base_salary + self.bonus
class Manager(Employee):
def init(self, name, base_salary, team_size):
super().init(name, base_salary)
self.team_size = team_size
def salary(self):
return self.base_salary + self.team_size * 500
class Intern(Employee):
@classmethod
def create(cls, name):
"""类方法作为替代构造器"""
return cls(name, 3000)
@staticmethod
def is_valid_days(days):
"""静态方法:校验实习天数"""
return 0 < days <= 180
employees = [
Engineer("张三", 15000, 3000),
Manager("李四", 20000, 3),
Intern.create("王五"),
]
total = 0
for e in employees:
print(e) # 多态:调用各自的 salary()
total += e.salary()
print(f"公司:{Employee.company},总人力成本:{total:.2f}")
print("实习天数合法:", Intern.is_valid_days(90))
运行结果:
张三(Engineer)月薪:18000.00
李四(Manager)月薪:21500.00
王五(Intern)月薪:3000.00
公司:示例科技,总人力成本:42500.00
实习天数合法: True
八、常见误区与最佳实践
- 不要滥用多重继承:优先使用组合(has-a)代替继承(is-a),结构更清晰。
- 类属性不要用可变对象 :如
list、dict作为类属性会被所有实例共享,容易踩坑。 - 私有属性只是约定 :Python 没有真正的访问控制,
__x会被改名为_类名__x。 - 优先使用鸭子类型:不必为了多态强行设计继承体系,接口一致即可。
- 善用
@property:在需要校验或计算属性时,比直接暴露字段更安全。
九、总结
本文从类与对象出发,依次讲解了实例属性与类属性、三种方法类型、单继承与多重继承、以及基于继承和鸭子类型的多态。掌握这些概念后,就能用 Python 写出结构清晰、易于扩展的面向对象代码。
建议读者把文中的综合实例动手敲一遍,并尝试新增一个 Sales 类,体会多态带来的扩展便利。