
要点 | 描述 |
|---|---|
痛点 | 不理解面向对象编程?不知道如何在Python中创建和使用类? |
方案 | 本教程详细讲解Python面向对象编程的核心概念和实践方法 |
驱动 | 掌握面向对象编程是2025年成为Python高手的必经之路! |
在上一篇教程中,我们学习了Python的模块和包。今天,我们将深入学习Python的面向对象编程(Object-Oriented Programming,简称OOP)。面向对象编程是一种编程范式,它使用"对象"来设计应用程序和计算机程序。Python是一种面向对象的编程语言,理解面向对象编程将帮助你编写更加模块化、可维护和可扩展的代码。
章节 | 内容 |
|---|---|
1 | 面向对象编程基础概念 |
2 | 类和对象 |
3 | 类的定义与使用 |
4 | 继承与多态 |
5 | 封装与访问控制 |
6 | 特殊方法与运算符重载 |
7 | 抽象类与接口 |
8 | 组合与聚合 |
9 | 设计模式基础 |
10 | 面向对象编程最佳实践 |
面向对象编程是一种编程范式,它将数据和操作数据的方法组合成一个称为对象的单元。与面向过程编程(关注完成任务的步骤)不同,面向对象编程关注的是如何将现实世界中的事物抽象为程序中的对象。
面向对象编程有四个核心概念:
面向对象编程有许多优点:
类是创建对象的蓝图或模板,它定义了对象的属性(数据)和方法(行为)。在Python中,我们使用class关键字来定义一个类。
# 类的定义示例
class Person:
pass对象是类的实例,是根据类创建出来的具体实体。一个类可以创建多个对象,每个对象都有自己的属性值,但共享类定义的方法。
# 创建对象示例
person1 = Person()
person2 = Person()类和对象的关系类似于蓝图和建筑物的关系:
在Python中,我们使用class关键字来定义一个类,并可以在类中定义属性和方法:
# 一个简单的类定义
class Person:
# 类变量,所有实例共享
species = "Homo sapiens"
# 初始化方法
def __init__(self, name, age):
# 实例变量,每个实例独有
self.name = name
self.age = age
# 实例方法
def greet(self):
return f"Hello, my name is {self.name}"
# 类方法
@classmethod
def from_birth_year(cls, name, birth_year):
current_year = 2025
age = current_year - birth_year
return cls(name, age)
# 静态方法
@staticmethod
def is_adult(age):
return age >= 18创建类的实例(对象)的过程称为实例化。在Python中,我们通过调用类名并传入必要的参数来实例化一个对象:
# 实例化对象
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)
# 使用类方法创建对象
person3 = Person.from_birth_year("Charlie", 1995)
# 访问对象的属性
print(person1.name) # 输出: Alice
print(person1.age) # 输出: 25
# 调用对象的方法
print(person1.greet()) # 输出: Hello, my name is Alice
# 调用类方法
print(Person.from_birth_year("David", 2000).age) # 输出: 25
# 调用静态方法
print(Person.is_adult(25)) # 输出: True
print(Person.is_adult(15)) # 输出: False__init__方法是Python中的特殊方法,用于初始化对象的属性。当我们创建一个新对象时,Python会自动调用这个方法:
class Car:
def __init__(self, brand, model, year):
self.brand = brand
self.model = model
self.year = year
# 设置默认值
self.mileage = 0
def drive(self, distance):
self.mileage += distance
return f"Drove {distance} miles. Total mileage: {self.mileage}"
# 创建对象时,__init__方法会自动调用
my_car = Car("Toyota", "Camry", 2025)
print(my_car.brand) # 输出: Toyota
print(my_car.mileage) # 输出: 0
print(my_car.drive(100)) # 输出: Drove 100 miles. Total mileage: 100在Python中,有三种类型的方法:
self)
@classmethod装饰器定义,自动接收类作为第一个参数(通常命名为cls)
@staticmethod装饰器定义,不自动接收任何特殊参数
class Calculator:
# 类变量
calculation_type = "Arithmetic Operations"
def __init__(self, name):
self.name = name
# 实例方法
def add(self, a, b):
return a + b
# 类方法
@classmethod
def get_calculation_type(cls):
return cls.calculation_type
# 静态方法
@staticmethod
def multiply(a, b):
return a * b
# 创建计算器对象
calc = Calculator("My Calculator")
# 调用实例方法
print(calc.add(5, 3)) # 输出: 8
# 调用类方法
print(Calculator.get_calculation_type()) # 输出: Arithmetic Operations
print(calc.get_calculation_type()) # 输出: Arithmetic Operations
# 调用静态方法
print(Calculator.multiply(5, 3)) # 输出: 15
print(calc.multiply(5, 3)) # 输出: 15继承是面向对象编程的一个重要特性,它允许一个类(子类)继承另一个类(父类)的属性和方法。继承可以提高代码的重用性和可维护性。
# 父类定义
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "Some generic sound"
def eat(self):
return f"{self.name} is eating"
# 子类定义,继承自Animal
class Dog(Animal):
def __init__(self, name, breed):
# 调用父类的初始化方法
super().__init__(name)
self.breed = breed
# 重写父类的方法
def speak(self):
return "Woof! Woof!"
# 子类定义,继承自Animal
class Cat(Animal):
def speak(self):
return "Meow!"
# 创建子类对象
dog = Dog("Buddy", "Golden Retriever")
cat = Cat("Whiskers")
# 调用继承的方法
print(dog.eat()) # 输出: Buddy is eating
print(cat.eat()) # 输出: Whiskers is eating
# 调用重写的方法
print(dog.speak()) # 输出: Woof! Woof!
print(cat.speak()) # 输出: Meow!
# 访问子类特有的属性
print(dog.breed) # 输出: Golden RetrieverPython支持多重继承,即一个类可以继承多个父类:
# 定义父类1
class Flyable:
def fly(self):
return "Flying high"
# 定义父类2
class Swimmable:
def swim(self):
return "Swimming deep"
# 子类继承多个父类
class Duck(Flyable, Swimmable):
def speak(self):
return "Quack!"
# 创建子类对象
duck = Duck()
# 调用从不同父类继承的方法
print(duck.fly()) # 输出: Flying high
print(duck.swim()) # 输出: Swimming deep
print(duck.speak()) # 输出: Quack!子类可以重写父类的方法,以提供特定于子类的实现:
class Shape:
def area(self):
return "Area calculation depends on the shape"
def perimeter(self):
return "Perimeter calculation depends on the shape"
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
# 重写area方法
def area(self):
return self.width * self.height
# 重写perimeter方法
def perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
# 重写area方法
def area(self):
import math
return math.pi * self.radius ** 2
# 重写perimeter方法
def perimeter(self):
import math
return 2 * math.pi * self.radius
# 创建对象
rect = Rectangle(5, 3)
circle = Circle(4)
# 调用重写的方法
print(f"Rectangle area: {rect.area()}") # 输出: Rectangle area: 15
print(f"Rectangle perimeter: {rect.perimeter()}") # 输出: Rectangle perimeter: 16
print(f"Circle area: {circle.area():.2f}") # 输出: Circle area: 50.27
print(f"Circle perimeter: {circle.perimeter():.2f}") # 输出: Circle perimeter: 25.13多态是指不同的对象对同一消息作出不同的响应。在Python中,多态主要通过方法重写来实现:
# 多态示例
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class Cow(Animal):
def speak(self):
return "Moo!"
# 多态函数,接受任何Animal类型的对象
def animal_sound(animal):
return animal.speak()
# 创建不同的动物对象
dog = Dog()
cat = Cat()
cow = Cow()
# 对不同的对象调用相同的函数
print(animal_sound(dog)) # 输出: Woof!
print(animal_sound(cat)) # 输出: Meow!
print(animal_sound(cow)) # 输出: Moo!封装是将数据和操作数据的方法组合在一个单元中,并控制对数据的访问。在Python中,我们可以通过命名约定来实现封装。
Python没有严格的访问控制机制,但它通过命名约定来表明属性或方法的访问级别:
_name),表示不应该从类外部直接访问__name),会触发名称修饰(name mangling),更难从类外部直接访问class Person:
def __init__(self, name, age, salary):
# 公共成员
self.name = name
# 保护成员
self._age = age
# 私有成员
self.__salary = salary
def get_salary(self):
# 提供访问私有成员的公共方法
return self.__salary
def set_salary(self, new_salary):
# 提供修改私有成员的公共方法
if new_salary > 0:
self.__salary = new_salary
else:
print("Invalid salary")
# 创建对象
person = Person("Alice", 30, 50000)
# 访问公共成员
print(person.name) # 输出: Alice
# 访问保护成员(不推荐,但技术上可行)
print(person._age) # 输出: 30
# 尝试直接访问私有成员(会出错)
# print(person.__salary) # AttributeError
# 通过名称修饰后的名称访问(不推荐)
print(person._Person__salary) # 输出: 50000
# 通过公共方法访问私有成员
print(person.get_salary()) # 输出: 50000
# 通过公共方法修改私有成员
person.set_salary(55000)
print(person.get_salary()) # 输出: 55000
# 尝试设置无效工资
person.set_salary(-1000)
print(person.get_salary()) # 输出: 55000(没有改变)Python提供了@property装饰器,允许我们将类方法转换为类属性,从而提供更好的封装:
class Circle:
def __init__(self, radius):
self._radius = radius
# 使用property装饰器定义getter方法
@property
def radius(self):
return self._radius
# 使用装饰器定义setter方法
@radius.setter
def radius(self, value):
if value > 0:
self._radius = value
else:
print("Radius must be positive")
# 只读属性
@property
def area(self):
import math
return math.pi * self._radius ** 2
# 创建对象
circle = Circle(5)
# 访问属性(实际上调用了radius方法)
print(circle.radius) # 输出: 5
# 修改属性(实际上调用了radius.setter方法)
circle.radius = 10
print(circle.radius) # 输出: 10
# 尝试设置无效值
circle.radius = -1
print(circle.radius) # 输出: 10(没有改变)
# 访问只读属性
print(f"Area: {circle.area:.2f}") # 输出: Area: 314.16
# 尝试修改只读属性(会出错)
# circle.area = 100 # AttributeErrorPython中有许多特殊方法(也称为魔术方法或双下划线方法),它们以双下划线开始和结束(如__init__、__str__等)。这些方法允许我们自定义类的行为。
以下是一些常用的特殊方法:
__init__:初始化方法__str__:返回对象的字符串表示,用于print()和str()函数__repr__:返回对象的"官方"字符串表示,用于repr()函数__len__:返回对象的长度,用于len()函数__eq__:定义相等比较,用于==运算符__lt__:定义小于比较,用于<运算符__add__:定义加法操作,用于+运算符class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
# 定义字符串表示
def __str__(self):
return f"'{self.title}' by {self.author}"
# 定义官方字符串表示
def __repr__(self):
return f"Book(title='{self.title}', author='{self.author}', pages={self.pages})"
# 定义长度(页数)
def __len__(self):
return self.pages
# 定义相等比较
def __eq__(self, other):
if isinstance(other, Book):
return (self.title == other.title and
self.author == other.author and
self.pages == other.pages)
return False
# 创建对象
book1 = Book("Python Crash Course", "Eric Matthes", 544)
book2 = Book("Python Crash Course", "Eric Matthes", 544)
book3 = Book("Fluent Python", "Luciano Ramalho", 792)
# 使用__str__方法
print(book1) # 输出: 'Python Crash Course' by Eric Matthes
print(str(book1)) # 输出: 'Python Crash Course' by Eric Matthes
# 使用__repr__方法
print(repr(book1)) # 输出: Book(title='Python Crash Course', author='Eric Matthes', pages=544)
# 使用__len__方法
print(len(book1)) # 输出: 544
# 使用__eq__方法
print(book1 == book2) # 输出: True
print(book1 == book3) # 输出: False通过实现特殊方法,我们可以重载Python的运算符,使自定义类的对象支持这些运算符:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector({self.x}, {self.y})"
# 重载加法运算符
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
return NotImplemented
# 重载减法运算符
def __sub__(self, other):
if isinstance(other, Vector):
return Vector(self.x - other.x, self.y - other.y)
return NotImplemented
# 重载乘法运算符(向量与标量相乘)
def __mul__(self, scalar):
if isinstance(scalar, (int, float)):
return Vector(self.x * scalar, self.y * scalar)
return NotImplemented
# 重载长度计算(向量的模)
def __abs__(self):
import math
return math.sqrt(self.x ** 2 + self.y ** 2)
# 重载小于运算符(比较向量的模)
def __lt__(self, other):
if isinstance(other, Vector):
return abs(self) < abs(other)
return NotImplemented
# 创建向量对象
v1 = Vector(3, 4)
v2 = Vector(1, 2)
# 测试加法
v3 = v1 + v2
print(f"v1 + v2 = {v3}") # 输出: v1 + v2 = Vector(4, 6)
# 测试减法
v4 = v1 - v2
print(f"v1 - v2 = {v4}") # 输出: v1 - v2 = Vector(2, 2)
# 测试乘法
v5 = v1 * 2
print(f"v1 * 2 = {v5}") # 输出: v1 * 2 = Vector(6, 8)
# 测试绝对值(模)
print(f"|v1| = {abs(v1)}") # 输出: |v1| = 5.0
print(f"|v2| = {abs(v2):.2f}") # 输出: |v2| = 2.24
# 测试比较
print(f"v1 < v2: {v1 < v2}") # 输出: v1 < v2: False
print(f"v2 < v1: {v2 < v1}") # 输出: v2 < v1: True抽象类是一种不能被实例化的类,它定义了接口(方法签名),但可能不提供具体实现。抽象类的目的是为子类提供一个共同的接口。
在Python中,我们可以使用abc模块(Abstract Base Classes)来创建抽象类:
from abc import ABC, abstractmethod
# 定义抽象类
class Shape(ABC):
# 定义抽象方法
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
# 定义普通方法
def description(self):
return "This is a geometric shape"
# 尝试实例化抽象类(会出错)
# shape = Shape() # TypeError
# 实现抽象类
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
# 实现抽象方法
def area(self):
return self.width * self.height
# 实现抽象方法
def perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
# 实现抽象方法
def area(self):
import math
return math.pi * self.radius ** 2
# 实现抽象方法
def perimeter(self):
import math
return 2 * math.pi * self.radius
# 创建子类对象
rect = Rectangle(5, 3)
circle = Circle(4)
# 调用实现的方法
print(f"Rectangle area: {rect.area()}") # 输出: Rectangle area: 15
print(f"Rectangle perimeter: {rect.perimeter()}") # 输出: Rectangle perimeter: 16
print(f"Circle area: {circle.area():.2f}") # 输出: Circle area: 50.27
print(f"Circle perimeter: {circle.perimeter():.2f}") # 输出: Circle perimeter: 25.13
# 调用继承的普通方法
print(rect.description()) # 输出: This is a geometric shape
print(circle.description()) # 输出: This is a geometric shape在Python中,并没有严格意义上的接口定义,因为Python使用的是"鸭子类型"(Duck Typing):如果一个对象走路像鸭子,叫声像鸭子,那么它就是鸭子。也就是说,Python不检查对象的类型,而是检查对象是否具有所需的方法和属性。
虽然Python没有内置的接口机制,但我们可以使用抽象类来模拟接口:
from abc import ABC, abstractmethod
# 定义接口(使用抽象类)
class Playable(ABC):
@abstractmethod
def play(self):
pass
@abstractmethod
def pause(self):
pass
@abstractmethod
def stop(self):
pass
# 实现接口
class MusicPlayer(Playable):
def play(self):
return "Playing music"
def pause(self):
return "Music paused"
def stop(self):
return "Music stopped"
class VideoPlayer(Playable):
def play(self):
return "Playing video"
def pause(self):
return "Video paused"
def stop(self):
return "Video stopped"
# 创建实现类的对象
music_player = MusicPlayer()
video_player = VideoPlayer()
# 使用接口方法
print(music_player.play()) # 输出: Playing music
print(video_player.play()) # 输出: Playing video
# 演示鸭子类型
class FakePlayer:
def play(self):
return "Fake playing"
def pause(self):
return "Fake paused"
def stop(self):
return "Fake stopped"
# FakePlayer并没有继承Playable,但它具有相同的方法,所以可以被当作Playable使用
def use_player(player):
print(player.play())
print(player.pause())
print(player.stop())
fake_player = FakePlayer()
use_player(fake_player) # 正常工作组合是一种"拥有"(has-a)关系,其中一个类包含另一个类的对象作为其属性。在组合关系中,被包含的对象的生命周期由包含它的对象控制(如果包含对象被销毁,被包含的对象也会被销毁)。
class Engine:
def __init__(self, horsepower):
self.horsepower = horsepower
def start(self):
return f"Engine with {self.horsepower} HP started"
class Car:
def __init__(self, brand, model):
self.brand = brand
self.model = model
# 组合:Car包含Engine对象
self.engine = Engine(200) # 直接创建Engine对象
def start(self):
return f"{self.brand} {self.model}: {self.engine.start()}"
# 创建Car对象
car = Car("Toyota", "Camry")
# 使用组合的对象
print(car.start()) # 输出: Toyota Camry: Engine with 200 HP started聚合也是一种"拥有"(has-a)关系,但在聚合关系中,被包含的对象的生命周期不由包含它的对象控制(如果包含对象被销毁,被包含的对象仍然可以存在)。
class Author:
def __init__(self, name, bio):
self.name = name
self.bio = bio
def __str__(self):
return f"{self.name}: {self.bio}"
class Book:
def __init__(self, title, pages, author):
self.title = title
self.pages = pages
# 聚合:Book包含Author对象,但不负责创建它
self.author = author
def __str__(self):
return f"'{self.title}' by {self.author.name}"
# 先创建Author对象
author = Author("J.K. Rowling", "British author best known for Harry Potter series")
# 然后将Author对象传递给Book构造函数
book1 = Book("Harry Potter and the Philosopher's Stone", 223, author)
book2 = Book("Harry Potter and the Chamber of Secrets", 251, author)
# 即使没有Book对象,Author对象仍然存在
print(book1) # 输出: 'Harry Potter and the Philosopher's Stone' by J.K. Rowling
print(book2) # 输出: 'Harry Potter and the Chamber of Secrets' by J.K. Rowling
print(author) # 输出: J.K. Rowling: British author best known for Harry Potter series在面向对象编程中,我们经常需要在继承和组合之间做出选择。一般来说:
组合通常比继承更加灵活,因为它允许我们在运行时更换组件,而继承则在编译时就确定了类的关系。
设计模式是解决软件设计中常见问题的可复用解决方案。它们是经过验证的最佳实践,可以帮助我们编写更加可维护、可扩展和可重用的代码。
以下是一些在Python中常用的设计模式:
确保一个类只有一个实例,并提供一个全局访问点:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls)
# 初始化代码
cls._instance.data = []
return cls._instance
# 测试单例模式
s1 = Singleton()
s2 = Singleton()
# 两个变量引用同一个实例
print(s1 is s2) # 输出: True
# 修改一个实例的数据,另一个实例的数据也会改变
s1.data.append(1)
print(s2.data) # 输出: [1]提供一个创建对象的接口,但由子类决定实例化的类:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def draw(self):
pass
class Rectangle(Shape):
def draw(self):
return "Drawing a rectangle"
class Circle(Shape):
def draw(self):
return "Drawing a circle"
class Triangle(Shape):
def draw(self):
return "Drawing a triangle"
# 工厂类
class ShapeFactory:
@staticmethod
def get_shape(shape_type):
if shape_type == "rectangle":
return Rectangle()
elif shape_type == "circle":
return Circle()
elif shape_type == "triangle":
return Triangle()
else:
raise ValueError(f"Unknown shape type: {shape_type}")
# 使用工厂创建对象
shape_factory = ShapeFactory()
rect = shape_factory.get_shape("rectangle")
circle = shape_factory.get_shape("circle")
triangle = shape_factory.get_shape("triangle")
print(rect.draw()) # 输出: Drawing a rectangle
print(circle.draw()) # 输出: Drawing a circle
print(triangle.draw()) # 输出: Drawing a triangle定义对象间的一种一对多依赖关系,使得当一个对象状态发生改变时,所有依赖于它的对象都会得到通知并自动更新:
class Observer:
def update(self, subject):
pass
class Subject:
def __init__(self):
self._observers = []
self._state = None
@property
def state(self):
return self._state
@state.setter
def state(self, state):
self._state = state
self.notify_observers()
def attach(self, observer):
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer):
if observer in self._observers:
self._observers.remove(observer)
def notify_observers(self):
for observer in self._observers:
observer.update(self)
# 具体观察者
class ConcreteObserverA(Observer):
def update(self, subject):
if subject.state < 3:
print("ConcreteObserverA: Reacted to the state change")
class ConcreteObserverB(Observer):
def update(self, subject):
if subject.state >= 3:
print("ConcreteObserverB: Reacted to the state change")
# 测试观察者模式
subject = Subject()
observer_a = ConcreteObserverA()
observer_b = ConcreteObserverB()
subject.attach(observer_a)
subject.attach(observer_b)
subject.state = 2 # 输出: ConcreteObserverA: Reacted to the state change
subject.state = 4 # 输出: ConcreteObserverB: Reacted to the state change
subject.detach(observer_a)
subject.state = 1 # 没有输出MyClassmyMethod或my_method__private_memberMAX_VALUEStudent的类,包含姓名、年龄、成绩等属性Shape的基类,包含计算面积和周长的抽象方法Rectangle、Circle、Triangle),实现这些抽象方法Vector3D的类,代表三维向量__add__、__sub__、__mul__等方法以支持向量运算__str__和__repr__方法以提供良好的字符串表示要点 | 描述 |
|---|---|
价值 | 掌握Python面向对象编程的核心概念和实践方法,编写更加模块化和可维护的代码 |
行动 | 继续学习Python高级特性,如装饰器、上下文管理器和生成器等,进一步提升编程技能 |
恭喜你完成了Python面向对象编程的学习!通过本教程,你已经了解了面向对象编程的核心概念,包括类和对象、继承与多态、封装与访问控制、特殊方法与运算符重载、抽象类与接口、组合与聚合以及设计模式基础等内容。
面向对象编程是Python编程的重要组成部分,掌握好它将帮助你编写更加模块化、可维护和可扩展的代码。记住,编程是一门需要实践的技能,所以请务必多写代码、多做练习,巩固所学的知识!
在下一篇教程中,我们将学习Python的异常处理,这是编写健壮程序的重要技能。
来源 | 描述 |
|---|---|
Python官方文档 | 提供权威的Python面向对象编程说明 |
菜鸟教程 | 适合初学者的Python面向对象编程教程 |
W3School | 提供Python面向对象编程的详细讲解和实例 |
Real Python | Python面向对象编程的高级教程 |
GeeksforGeeks | Python设计模式详解 |