一、初识面向对象:从现实世界到代码世界
大家好,我是你们这门课的老朋友。今天咱们聊聊面向对象编程——说白了,就是怎么用代码去模拟我们看到的这个世界。
我记得刚入行那会儿,写代码全是函数堆出来的。一个项目下来,几千行的函数满天飞,改一个地方,崩三个地方。后来带我的老大哥说:「你试试用面向对象的方式去思考。」这一试,就再也没回头。
1.1 现实世界 vs 代码世界
你想想看,现实世界里有什么?有「人」、「车」、「房子」这些类别,也有「张三」、「我的特斯拉」、「那栋写字楼」这些具体的个体。
代码世界也一样。我们管类别叫类(Class),管具体的个体叫对象(Object)。
核心对应关系:
- 类 = 现实世界中的「类别」——比如「狗」这个物种
- 对象 = 现实世界中的「具体个体」——比如你家那只叫旺财的柴犬
- 属性 = 对象的特征——比如旺财的毛色是黄的,体重15公斤
- 方法 = 对象的行为——比如旺财会叫、会跑、会摇尾巴
嗯,这里要注意:类是一张蓝图,对象才是真正住进内存的那个实体。我在项目中遇到过不少新手,把类当对象用,结果数据全串了——那叫一个惨。
1.2 第一个类:用代码描述一只狗
光说不练假把式。咱们直接上代码。我习惯用 Python 来演示,因为它最贴近自然语言。
class Dog:
"""狗类——所有狗的蓝图"""
def __init__(self, name, color, weight):
self.name = name # 属性:名字
self.color = color # 属性:毛色
self.weight = weight # 属性:体重
def bark(self):
"""方法:叫"""
print(f"{self.name} 汪汪叫!")
def run(self):
"""方法:跑"""
print(f"{self.name} 跑起来了,毛色是{self.color}的")
# 创建对象
wangcai = Dog("旺财", "黄色", 15)
xiaobai = Dog("小白", "白色", 8)
# 调用方法
wangcai.bark() # 输出:旺财 汪汪叫!
xiaobai.run() # 输出:小白 跑起来了,毛色是白色的
看到了吗?Dog 是类,wangcai 和 xiaobai 是对象。它们各自有各自的属性,互不干扰。这就是面向对象最基础的思想——把数据和操作数据的方法打包在一起。
1.3 面向对象的三大特性概览
说到面向对象,绕不开三大特性:封装、继承、多态。我当年面试的时候,被问烂了。但真正理解它们,是在实际项目中踩了坑之后。
1.3.1 封装:把秘密藏起来
封装,说白了就是把不该暴露的东西藏起来。你开车的时候,踩油门就行,不需要知道发动机里活塞怎么转的。
代码里也一样。有些内部数据和方法,外部不应该直接碰。我曾经接手过一个项目,所有属性都是公开的,结果到处都在改同一个数据,查 bug 查了三天——嗯,从那以后我写类必加私有属性。
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.__balance = balance # 私有属性,外部不能直接访问
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def get_balance(self):
return self.__balance
# 外部只能通过方法操作,不能直接改 __balance
account = BankAccount("张三", 1000)
account.deposit(500)
print(account.get_balance()) # 1500
小技巧:Python 里用双下划线开头(如 __balance)表示私有属性。虽然技术上还是能绕过去,但约定俗成——看到双下划线,就别碰。
1.3.2 继承:站在巨人的肩膀上
继承解决的是代码复用的问题。你写了一个「动物」类,有吃、睡、动这些通用方法。然后你写「狗」类、「猫」类,难道要把这些方法再写一遍?
没必要。让狗和猫继承动物类就行了。
class Animal:
def eat(self):
print("正在吃东西...")
def sleep(self):
print("正在睡觉...")
class Dog(Animal): # 继承 Animal
def bark(self):
print("汪汪!")
class Cat(Animal): # 继承 Animal
def meow(self):
print("喵喵!")
dog = Dog()
dog.eat() # 继承来的方法
dog.bark() # 自己的方法
我个人的习惯是:继承层次不要超过三层。超过三层,代码就变得像俄罗斯套娃,改底层一个方法,上面全受影响。我在一个老项目里见过七层继承——改一个基类,崩了二十几个子类,那场面...
避坑指南:我曾经以为「多用继承显得很高级」,结果维护成本直线上升。后来学乖了:能用组合(把一个类作为另一个类的属性)解决的问题,就别用继承。
1.3.3 多态:同一个接口,不同的行为
多态这个词听起来玄乎,其实很简单:同一个方法名,在不同对象上表现不同。
比如「叫」这个方法。狗叫是「汪汪」,猫叫是「喵喵」,牛叫是「哞哞」。你不需要关心它是什么动物,只需要知道它能「叫」就行。
class Animal:
def make_sound(self):
pass # 父类不实现,留给子类
class Dog(Animal):
def make_sound(self):
print("汪汪!")
class Cat(Animal):
def make_sound(self):
print("喵喵!")
def animal_sound(animal):
animal.make_sound() # 不管传进来的是狗还是猫,都能叫
animals = [Dog(), Cat()]
for a in animals:
animal_sound(a) # 输出:汪汪! 喵喵!
你想想看,如果没有多态,你得写一堆 if type(animal) == Dog 这样的判断。代码又臭又长,加一个新动物还得改判断逻辑。多态让代码对扩展开放,对修改关闭——这就是设计模式里开闭原则的精髓。
1.4 本章知识体系总览
说了这么多,咱们用一张图把核心逻辑串起来。我特意画了这张 SVG,方便你一眼看清整个知识结构。
1.5 小结
这一章咱们把面向对象最核心的概念过了一遍。说白了就是三句话:
- 类是模板,对象是实物
- 封装藏好内部秘密,继承复用已有代码,多态让接口统一
- 写代码的时候,多想想现实世界是怎么建模的
我个人觉得,面向对象与其说是一种编程技术,不如说是一种思维方式。你开始用「对象」的眼光去看待问题的时候,代码自然就清晰了。
好,这一章就到这儿。下一章咱们深入聊聊类与对象的内存模型——搞清楚对象在内存里到底长什么样,很多疑惑就迎刃而解了。