🧱 18. 面向对象编程
本章概述
学习 Python 面向对象编程:类、对象、继承、多态等。 预计学习时间:80 分钟
18.1 什么是面向对象
面向对象编程(OOP)是一种编程思想,把数据和操作数据的方法封装在一起,形成「对象」。
类和对象
- 类(class):模板,描述一类事物的共同特征和行为
- 对象(object):根据类创建的具体实例
示例
# 定义类
class Person:
def __init__(self, name, age):
self.name = name # 属性
self.age = age # 属性
def say_hello(self): # 方法
print(f"你好,我是{self.name},今年{self.age}岁")
# 创建对象
p1 = Person("张三", 18)
p2 = Person("李四", 20)
# 调用方法
p1.say_hello() # 你好,我是张三,今年18岁
p2.say_hello() # 你好,我是李四,今年20岁
# 访问属性
print(p1.name) # 张三
print(p2.age) # 20类命名规范
- 大驼峰命名法(每个单词首字母大写)
- 例如:
Person,Student,MyClass
18.2 init 构造方法
创建对象时自动调用的方法,用来初始化属性。
self 参数
self 代表当前对象本身,类似其他语言的 this。
class Person:
def __init__(self, name, age):
self.name = name # self.name 是对象的属性
self.age = age
# 创建对象时,参数传给 __init__
p = Person("Tom", 18)注意
- 方法的第一个参数必须是
self- 调用方法时不用传
self,Python 自动传self名字可以改,但约定俗成用self
18.3 属性和方法
实例属性
每个对象都有自己的属性,互不影响:
p1 = Person("张三", 18)
p2 = Person("李四", 20)
print(p1.name) # 张三
print(p2.name) # 李四(各自的属性)类属性
所有对象共享的属性,定义在类里面、方法外面:
class Person:
count = 0 # 类属性,统计创建了多少个对象
def __init__(self, name):
self.name = name
Person.count += 1 # 访问类属性
p1 = Person("张三")
p2 = Person("李四")
print(Person.count) # 2实例方法
最常见的方法,第一个参数是 self,可以访问实例属性。
class Person:
def __init__(self, name):
self.name = name
def say_hello(self): # 实例方法
print(f"你好,我是{self.name}")类方法
用 @classmethod 装饰器,第一个参数是 cls(类本身)。
class Person:
count = 0
def __init__(self, name):
self.name = name
Person.count += 1
@classmethod
def get_count(cls): # 类方法
return cls.count
print(Person.get_count()) # 0静态方法
用 @staticmethod 装饰器,不需要 self 或 cls 参数。
class MathUtils:
@staticmethod
def add(a, b): # 静态方法
return a + b
print(MathUtils.add(3, 5)) # 8三种方法的区别
- 实例方法:需要访问实例属性时用
- 类方法:需要访问类属性或创建对象时用
- 静态方法:和类相关但不需要访问类/实例属性时用
18.4 访问控制
Python 没有真正的私有属性,靠命名约定。
公有属性/方法
正常命名,外部可以直接访问:
class Person:
def __init__(self, name):
self.name = name # 公有属性
p = Person("Tom")
print(p.name) # 可以直接访问受保护的 _xxx
单下划线开头,表示「受保护的」,建议不要外部访问,但还是能访问到:
class Person:
def __init__(self, name):
self._name = name # 受保护的
p = Person("Tom")
print(p._name) # 能访问,但不建议私有的 __xxx
双下划线开头,Python 会改名(名称修饰),外部不能直接访问:
class Person:
def __init__(self, name):
self.__name = name # 私有的
p = Person("Tom")
# print(p.__name) # ❌ 报错,访问不到
# 其实可以通过改名后的名字访问(不推荐)
print(p._Person__name) # TomPython 的哲学
Python 信奉「我们都是成年人」,靠约定而不是强制。 单下划线是「请勿打扰」,双下划线是「请勿擅入」,但都不是绝对的。
18.5 属性装饰器 @property
把方法变成属性一样调用。
基本用法
class Person:
def __init__(self, name):
self._name = name
@property
def name(self): # getter
return self._name
@name.setter
def name(self, value): # setter
if len(value) >= 2:
self._name = value
else:
raise ValueError("名字太短")
p = Person("Tom")
print(p.name) # 像属性一样访问
p.name = "Jerry" # 像属性一样修改为什么用 property?
- 可以加验证逻辑
- 可以做计算属性
- 接口兼容(原来是属性,后来改成方法不影响调用方)
只读属性
只写 getter,不写 setter,就是只读的:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self): # 只读属性
return 3.14 * self.radius ** 2
c = Circle(5)
print(c.area) # 78.5
# c.area = 100 # ❌ 报错,不能修改18.6 继承
一个类继承另一个类,拥有父类的属性和方法。
基本用法
# 父类(基类)
class Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name}在吃东西")
# 子类(派生类),继承 Animal
class Dog(Animal):
def bark(self):
print(f"{self.name}在汪汪叫")
dog = Dog("旺财")
dog.eat() # 继承来的方法
dog.bark() # 自己的方法方法重写
子类可以重写父类的方法:
class Animal:
def speak(self):
print("动物在叫")
class Dog(Animal):
def speak(self): # 重写父类方法
print("汪汪汪")
class Cat(Animal):
def speak(self): # 重写父类方法
print("喵喵喵")
Dog().speak() # 汪汪汪
Cat().speak() # 喵喵喵super() 调用父类方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, name, age, grade):
super().__init__(name, age) # 调用父类的__init__
self.grade = grade
s = Student("张三", 18, 3)
print(s.name, s.age, s.grade)多层继承
class A:
pass
class B(A): # B继承A
pass
class C(B): # C继承B,间接继承A
pass多重继承
一个类继承多个父类:
class A:
def method_a(self):
print("A的方法")
class B:
def method_b(self):
print("B的方法")
class C(A, B): # 同时继承A和B
pass
c = C()
c.method_a() # A的方法
c.method_b() # B的方法多重继承注意
- 多重继承会让代码变复杂,尽量少用
- 方法名冲突时,按继承顺序查找(MRO)
- 可以用
C.__mro__查看方法解析顺序
18.7 多态
同一个方法,不同对象调用有不同的表现。
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("汪汪汪")
class Cat(Animal):
def speak(self):
print("喵喵喵")
def make_speak(animal):
animal.speak() # 同一个方法,不同表现
make_speak(Dog()) # 汪汪汪
make_speak(Cat()) # 喵喵喵Python 的多态
Python 是动态语言,不要求继承关系,只要有同名方法就行(鸭子类型)。
class Dog: def speak(self): print("汪汪汪") class Cat: def speak(self): print("喵喵喵") # 不用继承,只要有speak方法就行
18.8 特殊方法(魔术方法)
双下划线开头和结尾的方法,有特殊用途。
str 字符串表示
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person(name={self.name}, age={self.age})"
p = Person("Tom", 18)
print(p) # Person(name=Tom, age=18)repr 官方表示
def __repr__(self):
return f"Person('{self.name}', {self.age})"str vs repr
__str__:给人看的,友好的字符串__repr__:给程序看的,能还原对象的字符串- 没有
__str__时会用__repr__
len 长度
class MyList:
def __init__(self, data):
self.data = data
def __len__(self):
return len(self.data)
lst = MyList([1, 2, 3])
print(len(lst)) # 3运算符重载
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # + 运算符
return Point(self.x + other.x, self.y + other.y)
def __str__(self):
return f"({self.x}, {self.y})"
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1 + p2) # (4, 6)常用特殊方法
| 方法 | 说明 | 调用方式 |
|---|---|---|
__init__ | 构造方法 | obj = Class() |
__str__ | 字符串表示 | str(obj), print(obj) |
__repr__ | 官方表示 | repr(obj) |
__len__ | 长度 | len(obj) |
__add__ | 加法 | a + b |
__sub__ | 减法 | a - b |
__eq__ | 等于 | a == b |
__lt__ | 小于 | a < b |
__getitem__ | 索引取值 | obj[key] |
__setitem__ | 索引赋值 | obj[key] = value |
__call__ | 调用对象 | obj() |
18.9 常用概念
封装
把数据和操作数据的方法放在一起,隐藏内部实现,只暴露接口。
class BankAccount:
def __init__(self, balance):
self.__balance = balance # 私有,外部不能直接改
def deposit(self, amount): # 公开方法
if amount > 0:
self.__balance += amount
def withdraw(self, amount): # 公开方法
if 0 < amount <= self.__balance:
self.__balance -= amount
def get_balance(self): # 公开方法
return self.__balance继承
子类复用父类的代码,见 18.6 节。
多态
同一个接口,不同实现,见 18.7 节。
面向对象三大特性
封装、继承、多态
🔗 相关章节
📝 我的笔记
在这里记录你的理解、疑问和练习代码
# 你的练习代码
✅ 本章检查清单
- 理解类和对象的概念
- 会定义类和创建对象
- 掌握 init 构造方法
- 理解实例属性和类属性
- 理解实例方法、类方法、静态方法
- 了解访问控制(公有、受保护、私有)
- 会使用 @property 装饰器
- 掌握继承和方法重写
- 理解多态
- 了解常用特殊方法