当前位置:首页>python>每天学习一点Python——面向对象编程(OOP)入门详解

每天学习一点Python——面向对象编程(OOP)入门详解

  • 2026-08-24 18:39:08
每天学习一点Python——面向对象编程(OOP)入门详解

每天学习一点Python——面向对象编程(OOP)入门详解

面向对象编程(OOP)是一种将相关属性和行为打包到单个对象中的编程方法。就像工厂的装配线,每个步骤处理一些材料,最终将原材料转化为成品。

今天我们将通过Python语言,从零开始学习OOP的核心概念:类、对象、属性和方法,以及继承。

一、什么是类和对象?

想象你要记录公司员工的信息。如果用列表来表示每个员工:

kirk = ["James Kirk", 34, "Captain", 2265]
spock = ["Spock", 35, "Science Officer", 2254]
mccoy = ["Leonard McCoy", "Chief Medical Officer", 2266]

这样做有几个问题:

  1. 1. 可读性差:如果代码多,你很难记住 kirk[0] 是名字还是年龄。
  2. 2. 容易出错:如果某个员工信息不全(比如mccoy少了年龄),就会导致数据错位。

为了解决这个问题,我们可以使用类来定义一种结构化的数据类型。

1.1 定义类

类就像一张蓝图,它规定了某个事物应该有哪些属性和行为,但它本身不包含具体数据。
对象是根据类创建的具体实例,包含真实数据。

定义一个最简单的类:

class Dog:
    pass
  • • class 是定义类的关键字。
  • • Dog 是类名,Python约定类名使用大驼峰命名法(每个单词首字母大写)。
  • • pass 是占位符,表示暂时什么都不写,避免语法错误。

1.2 添加属性:__init__ 方法

我们希望每只狗都有名字和年龄,这可以通过 __init__ 方法来实现:

class Dog:
    def
 __init__(self, name, age):
        self
.name = name
        self
.age = age
  • • __init__ 是一个特殊方法,在创建对象时自动调用,用于初始化对象属性。
  • • self 代表实例本身,必须是第一个参数。
  • • self.name = name 表示将传入的 name 赋值给该实例的 .name 属性。

💡 提示:self 不是Python关键字,你可以用别的词,但约定俗成用 self,清晰易懂。

1.3 类属性 vs 实例属性

  • • 实例属性:每个对象特有的属性(如每只Dog的名字、年龄)。
  • • 类属性:所有对象共享的属性(如所有Dog都是同一个物种)。
class Dog:
    species = "Canis familiaris"  # 类属性(所有狗共享)

    def
 __init__(self, name, age):
        self
.name = name  # 实例属性(每只狗特有)
        self
.age = age    # 实例属性(每只狗特有)

二、创建对象(实例化)

有了类,我们就可以创建具体的Dog对象了:

buddy = Dog("Buddy", 9)
miles = Dog("Miles", 4)
  • • Dog("Buddy", 9) 调用 __init__ 方法,创建一只名为Buddy、年龄9岁的狗。
  • • 每只Dog都是独立的,内存地址不同:
print(buddy == miles)  # 输出: False

访问属性:

print(buddy.name)  # 输出: Buddy
print
(miles.age)   # 输出: 4
print
(buddy.species)  # 输出: Canis familiaris

三、添加方法

方法是定义在类中的函数,用于描述对象的行为:

class Dog:
    species = "Canis familiaris"

    def
 __init__(self, name, age):
        self
.name = name
        self
.age = age

    def
 description(self):
        return
 f"{self.name} is {self.age} years old"

    def
 speak(self, sound):
        return
 f"{self.name} says {sound}"

使用:

miles = Dog("Miles", 4)
print
(miles.description())  # 输出: Miles is 4 years old
print
(miles.speak("Woof Woof"))  # 输出: Miles says Woof Woof

3.1 为什么description()不用传参,而speak("Woof Woof")需要传参?

这是一个关于Python类方法参数的重要问题。区别在于这两个方法的定义方式不同:

1. description(self) 方法

def description(self):
    return
 f"{self.name} is {self.age} years old"
  • • 只有一个参数:self
  • • self 是Python的约定,表示实例本身
  • • 方法内部使用 self.name 和 self.age 访问实例属性
  • • 调用时不需要传入任何参数,因为Python会自动传递 self

2. speak(self, sound) 方法

def speak(self, sound):
    return
 f"{self.name} says {sound}"
  • • 有两个参数:self 和 sound
  • • self:自动传递的实例引用
  • • sound:需要用户传入的参数

对比表格:

方法
定义时的参数
调用时的参数
说明
description(self)
只有 self
无
self
 自动传入,使用实例属性
speak(self, sound)self
 和 sound
"Woof Woof"self
 自动传入,sound 需要手动传入

3.2 美化输出:__str__ 方法

直接打印对象会显示内存地址,不友好:

print(miles)  # 输出: <__main__.Dog object at 0x...>

我们可以定义 __str__ 方法,自定义打印内容:

class Dog:
    # ... 其他代码不变


    def
 __str__(self):
        return
 f"{self.name} is {self.age} years old"

现在再打印:

print(miles)  # 输出: Miles is 4 years old

💡 提示:__init__、__str__ 这类前后带双下划线的方法称为"dunder方法"(double underscores),用于实现类的特殊行为。

3.3 description() 与 __str__() 的区别

def description(self):
    return
 f"{self.name} is {self.age} years old"

def
 __str__(self):
    return
 f"{self.name} is {self.age} years old"

主要区别:

特性
description()__str__()
方法类型
普通方法
特殊方法(魔术方法)
调用方式
需要显式调用
自动调用
用途
自定义描述逻辑
定义对象的"人类可读"字符串表示
示例调用print(miles.description())print(miles)
字符串转换
需要手动调用
str(miles)
 自动使用
在列表/容器中
不会自动调用
在容器中打印时会自动调用

实际使用中的差异:

class Dog:
    def
 __init__(self, name, age):
        self
.name = name
        self
.age = age

    def
 description(self):
        return
 f"{self.name} is {self.age} years old"

    def
 __str__(self):
        return
 f"Dog: {self.name}, Age: {self.age}"

# 创建实例

miles = Dog("Miles", 4)

两种方法的调用对比:

使用 description()

print("=== 使用 description() ===")
print
(miles.description())  # 需要显式调用方法
# 输出: Miles is 4 years old

使用 __str__()

print("\n=== 使用 __str__() ===")
print
(miles)  # 直接打印对象,自动调用 __str__
# 输出: Dog: Miles, Age: 4

更多 __str__ 的优势

print(str(miles))  # 使用 str() 函数
# 输出: Dog: Miles, Age: 4


# 在 f-string 中

print
(f"My dog: {miles}")
# 输出: My dog: Dog: Miles, Age: 4


# 在列表中

dogs = [miles, Dog("Buddy", 2)]
print
(dogs)
# 输出: [Dog: Miles, Age: 4, Dog: Buddy, Age: 2]

为什么需要 __str__?

  1. 1. Python惯例和约定
    • • __str__ 是Python的内置协议
    • • 很多Python函数和操作会自动调用__str__
  2. 2. 更自然的使用方式
# 有了 __str__ 后,使用更简洁自然
print
(miles)                    # ✅ 直接打印
print
(f"Info: {miles}")         # ✅ 在f-string中
print
("Dog info: " + str(miles))  # ✅ 字符串拼接

# 没有 __str__ 时,需要更多手动操作

print
(miles.description())      # 🔴 总是要加 .description()

四、继承:创建子类

假设我们想区分不同品种的狗,比如杰克罗素梗、腊肠犬、斗牛犬。它们都是狗,但叫声不同。我们可以通过继承来创建子类:

class JackRussellTerrier(Dog):
    pass


class
 Dachshund(Dog):
    pass


class
 Bulldog(Dog):
    pass
  • • 括号中的 Dog 表示父类。
  • • 子类自动拥有父类的所有属性和方法。

4.1 重写方法

不同品种的狗叫声不同,我们可以在子类中重写 speak 方法:

class JackRussellTerrier(Dog):
    def
 speak(self, sound="Arf"):
        return
 f"{self.name} says {sound}"

现在创建一只JackRussellTerrier:

miles = JackRussellTerrier("Miles", 4)
print
(miles.speak())  # 输出: Miles says Arf
print
(miles.speak("Grrr"))  # 输出: Miles says Grrr

要点总结:

  1. 1. 默认参数:sound="Arf" 设置了默认值
  2. 2. 调用方式:
miles.speak()          # 使用默认值 → "Arf"
miles.speak("Grrr")    # 使用传入值 → "Grrr"
  1. 3. 原理:如果不传参数,Python自动使用默认值"Arf";如果传参,则覆盖默认值

对比父类:

  • • 父类Dog.speak(self, sound):必须传参
  • • 子类JackRussellTerrier.speak(self, sound="Arf"):可不传(用默认),也可传参覆盖

默认参数让方法调用更灵活。

4.2 使用 super() 调用父类方法

如果希望在子类中保留父类方法的行为,又想做些扩展,可以使用 super():

class JackRussellTerrier(Dog):
    def
 speak(self, sound="Arf"):
        return
 super().speak(sound)

这样,父类 Dog 的 speak 方法被调用,格式保持一致。

两种方式对比:

1. 不用 super()(自己重写):

def speak(self, sound="Arf"):
    return
 f"{self.name} says {sound}"  # 完全自己重写
  • • 效果:得到和父类一样的结果
  • • 问题:如果父类改了(比如改成shouts:),这里不会自动更新

2. 用 super()(复用父类):

def speak(self, sound="Arf"):
    return
 super().speak(sound)  # 调用父类方法
  • • 效果:完全复用父类逻辑
  • • 好处:父类改了,这里自动跟着改

举个例子:

初始情况:

class Dog:
    def
 speak(self, sound):
        return
 f"{self.name} says {sound}"  # 父类版本1

class
 JackRussellTerrier(Dog):
    def
 speak(self, sound="Arf"):
        return
 super().speak(sound)  # 调用父类方法

测试:

dog = JackRussellTerrier("Miles", 4)
print
(dog.speak("汪"))  # 输出: Miles says 汪

父类修改后:

class Dog:
    def
 speak(self, sound):
        # 产品经理要求:所有狗说话都要加表情!

        return
 f"{self.name} says: {sound} 🐶"  # 父类版本2(修改了!)

看看子类的变化:

情况1:用 super() 的子类

dog = JackRussellTerrier("Miles", 4)
print
(dog.speak("汪"))
# 输出: Miles says: 汪 🐶  ← 自动继承父类新格式!

# super() 自动调用新版的 Dog.speak()

情况2:不用 super() 的子类

class BadJackRussellTerrier(Dog):
    def
 speak(self, sound="Arf"):
        # 自己重写的,没用 super()

        return
 f"{self.name} says {sound}"

bad_dog = BadJackRussellTerrier("Miles", 4)
print
(bad_dog.speak("汪"))
# 输出: Miles says 汪  ← 还是旧格式!没有表情!

# 需要手动修改代码才能更新

对比表格:

时间点
父类 Dog.speak()
用 super() 的子类
不用 super() 的子类
初始"{name} says {sound}"Miles says 汪Miles says 汪
父类修改后"{name} says: {sound} 🐶"Miles says: 汪 🐶
 ✅
Miles says 汪
 ❌
需要手动修改吗?
-
不需要
,自动更新
需要
,必须改代码

3. 用 super() 添加功能(最常见用法):

def speak(self, sound="Arf"):
    print
("摇尾巴!")            # 1. 自己加的动作
    result = super().speak(sound) # 2. 父类的核心逻辑
    print
("跳起来!")            # 3. 自己加的动作
    return
 result
  • • 效果:父类逻辑 + 自己新增逻辑

现实类比:

  • • 用 super():大家在我的代码上搭建新功能,我更新基础代码时,大家的扩展会自动升级
  • • 不用 super():大家把我的代码复制一份自己改,我更新时,大家的复制版不会自动更新

总结要点:

  1. 1. super() = 调用父类方法
  2. 2. 主要用途:复用父类代码,避免重复
  3. 3. 常见模式:父类基础功能 + 子类额外功能

4.3 检查对象类型

print(type(miles))  # 输出: <class '__main__.JackRussellTerrier'>
print
(isinstance(miles, Dog))  # 输出: True
print
(isinstance(miles, Bulldog))  # 输出: False
  • • type() 返回对象的类。
  • • isinstance() 检查对象是否属于某个类或其子类。

五、练习巩固

练习1:为Dog类添加颜色属性

class Dog:
    def
 __init__(self, name, age, coat_color):
        self
.name = name
        self
.age = age
        self
.coat_color = coat_color

philo = Dog("Philo", 5, "brown")
print
(f"{philo.name}'s coat is {philo.coat_color}.")

输出:

Philo's coat is brown.

练习2:创建Car类

class Car:
    def
 __init__(self, color, mileage):
        self
.color = color
        self
.mileage = mileage

blue_car = Car("blue", 20000)
red_car = Car("red", 30000)

print
(f"The {blue_car.color} car has {blue_car.mileage:,} miles.")
print
(f"The {red_car.color} car has {red_car.mileage:,} miles.")

输出:

The blue car has 20,000 miles.
The red car has 30,000 miles.

练习3:为Car类添加行驶方法

class Car:
    def
 __init__(self, color, mileage):
        self
.color = color
        self
.mileage = mileage

    def
 drive(self, miles):
        self
.mileage += miles

my_car = Car("white", 0)
my_car.drive(100)
print
(my_car.mileage)  # 输出: 100

六、总结

今天我们学习了:

  1. 1. 类和对象:类是蓝图,对象是实例
  2. 2. 属性:实例属性(每对象特有)和类属性(所有对象共享)
  3. 3. 方法:定义对象的行为,self 参数自动传递
  4. 4. 特殊方法:__init__(初始化)、__str__(字符串表示)
  5. 5. 继承:子类继承父类特性,可重写或扩展方法
  6. 6. super():调用父类方法,提高代码复用性和维护性

请记住:面向对象编程的核心思想是将数据和操作数据的方法封装在一起,让代码更加模块化、可复用、易维护。


📦 资源获取提示

关注「码农自习室」,后台回复关键词 Python学习,即可获取本文完整代码,一起动手掌握高效编程的核心技巧!


❤️ 支持我们

如果觉得本文对你有帮助,欢迎点赞 + 关注,您的支持是我们持续创作优质内容的最大动力!


📚 学习资源说明

本文内容是基于《Python Basics: A Practical Introduction to Python 3》(Real Python)一书的学习笔记整理。
这本书是一本非常优秀的Python入门教材,推荐给所有想要系统学习Python的朋友们。

这本书的特点:

  • • ✅ 完全面向编程新手
  • • ✅ 每个概念都有代码示例
  • • ✅ 包含大量练习题
  • • ✅ 由经验丰富的Python开发者编写

跟着这本书学习,配合我的笔记整理,相信你能更快掌握Python编程!

让我们一起坚持学习,每天进步一点点!💪

最新文章

随机文章