Skip to content

面向对象 Python 编程实践

本节目标

以为课程设计作业为例,通过一系列的编程练习操作:

  1. 掌握Python中类的定义,对象的创建
  2. Python类的继承
  3. Python类库和包的概念与调用

Python 编程 3 种范式: 面向过程 vs. 函数式编程 vs. 面向对象中,我们了解到 Python 有三种编程 范式:

  • 面向过程的编程
  • 面向对象的编程
  • 函数式编程

其中,面向对象编程是一种将数据和操作数据的函数结合在一起的编程范式。它通过类和对象的概念来组织代码,使得代码更易于维护和扩展。而面向过程编程则强调通过过程和函数来组织代码,通常更关注于步骤和逻辑的顺序。从某种程度上来说,基于函数的面向过程是面向对象编程的基础,掌握好前者有助于在面向对象编程时更好的组织类的定义和使用。

类的创建和使用

在面向对象编程中,类是对象的蓝图,而对象是类的实例。通过类,我们可以创建多个对象,每个对象都可以拥有自己的属性和方法。面向对象编程的核心概念包括封装、继承和多态。从实用主义的角度出发,我们本节重点讲解类的定义、对象的创建及其使用(类的实例化)、类的继承,其它内容请在实际需求中自行学习。

类的定义

Python定义类的基本格式:

Python
class ClassName:
    
    # 类属性(类库开发者要掌握;调包侠/新手用的较少)
    class_variable = "这是一个类变量"

    # 类的静态方法(类库开发者)
    @staticmethod
    def static_method():
        return "这是一个静态方法"
    
    # 类的类方法(类库开发者)
    @classmethod
    def class_method(cls):
        return "这是一个类方法"

    # 构造函数(重点掌握)
    def __init__(self, instance_variable):
        # 类的实例属性(重点掌握)
        self.instance_variable = instance_variable

    # 类的实例方法(重点掌握).
    # 注:“实例”方法不是标准名字。此处这么称呼是为了说明:类的实例属性和方法多由实例化的对象调用。
    def method(self):
        return f"实例属性: {self.instance_variable}"

新手都是从调包侠做起,所以我们目前仅关注类的定义中如下三个知识点:

  1. 构造函数__init__(self,...)
  2. 类的实例变量self.instance_variable
  3. 类的(实例)方法def method(self,...)

课程设计作业中的相关任务:

  1. 定义一个长方体的类Cuboid,包含实例属性length、width和height
  2. 在类Cuboid中定义一个构造函数__init__(),初始化实例属性length、width和height;
  3. 在类Cuboid中定义一个方法volume(),计算长方体的体积;
  4. 在类Cuboid中定义一个方法surface_area(),计算长方体的表面积;
Python
class Cuboid:
    # 构造函数
    def __init__(self, length, width, height):
        # 实例属性
        self.length = length
        self.width = width
        self.height = height

    # 实例方法
    def volume(self):
        return self.length * self.width * self.height

    # 实例方法
    def surface_area(self):
        return 2 * (self.length * self.width + self.width * self.height + self.length * self.height)

上述代码中,新手编程易出错的点:

  1. 忘记在类中定义构造函数(__init__)
  2. 忘记在类中定义实例方法(__init__, volume, surface_area)中使用self参数
  3. 如果要在创建类的对象时,对对象的属性进行正确的初始化,则需要在构造函数__init__(self,...)中,添加变量名(进行赋值)
  4. 类的实例化时参数传递错误

对象的创建(类的实例化)

课程设计作业中的相关任务:

8. 分别创建Cuboid和Cube类的实例,调用它们的方法,输出结果。

shell
# 进入Python的REPL交互式命令环境
python3

然后,在此Python的REPL交互式命令环境中,执行如下Python代码:

shell
# 从`lesson0116.py`中,导入类`Cuboid`
from lesson0116 import Cuboid

# 创建Cuboid类的实例`cuboid1`
cuboid1 = Cuboid(2, 3, 4)

# 调用Cuboid类的方法`volume()`和`surface_area()`.
# 注意:调用“类的(实例)方法”的主体是“类经过实例化”的对象(此处是`cuboid1`)
volume = cuboid1.volume()
surface_area = cuboid1.surface_area()

通过类的实例化来创建对象的原理

cuboid1=Cuboid(2,3,4)为例:

  1. cuboid1是一个对象的变量名,
  2. 调用Cuboid(2,3,4)时,自动调用构造函数__init__
  3. Cuboid类的构造函数__init__(self, length, width, height)实质上只有三个参数:lengthwidthheight
  4. Cuboid(2,3,4)按照位置与__init__(self, length, width, height)中的参数一一对应,得到length=2, width=3, height=4
  5. 查看__init__函数的内部实现如下,这是一个通过__init__函数的形式参数对类的实例变量self.length等进行初始化的过程:
Python
def __init__(self, length, width, height):
    # 类内部的实例变量通过`self`进行标记;
    self.length = length
    self.width = width
    self.height = height
# 通过赋值,类的实例变量被初始化为对应的参数值, 
# 即`self.length=2, self.width=3, self.height=4`

通过self关键字,实例变量self.length等可以在类Cuboid的其他所有方法(本例是volume()surface_area())中被共享。

类的方法的调用原理

  1. 实例化的对象cuboid1的属性值为self.length=2self.width=3self.height=4
  2. 并在内部的类方法volume()surface_area()中共享属性self.lengthself.widthself.height

    注意:类的实例变量(属性,也可称之为成员变量)通过关键字self标记和访问。

  3. 通过调用cuboid1.volume()可以计算长方体的体积。
  4. 通过调用cuboid1.surface_area()可以计算长方体的表面积。

拓展知识(自学)

  1. 除了第一个参数必须是self之外,类的(实例)方法从某种程度上来说就是定义在类内部的“函数”,那么,Python的普通函数如何定义?
  2. Python的函数定义中,其参数有几种定义方法?

    在“位置参数、关键字参数、默认参数和可变参数”中,重点掌握”位置参数“和”关键字参数“。

  3. 调用Python函数时,位置参数、关键字参数有什么讲究?
  4. Python函数的关键字参数和**”带有默认值“的位置参数**如何区分?

类的继承

课程设计作业中的相关任务:

  1. 创建一个立方体的类Cube,继承自Cuboid类,重写__init__()方法,初始化length、width和height为相等的值;
  2. 计算立方体的体积和表面积,调用volume()和surface_area()方法;
  3. 在Cube类中定义一个方法diagonal(),计算立方体的对角线长度;
Python
class Cube(Cuboid):

    # 重写构造函数
    def __init__(self, side_length):
        self.side_length = side_length
        super().__init__(side_length, side_length, side_length)

    # 拓展父类Cuboid的方法    
    def diagonal(self):
        return (self.side_length ** 2 + self.side_length ** 2 + self.side_length ** 2) ** 0.5

心得:类的继承,其精髓就是“萧规曹随,从善如流;有过改之,无则加勉”。

类的继承的规律

Python
# 基本语法
class ParentClass:
    # 父类的属性和方法
    pass

class ChildClass(ParentClass):  # ParentClass是基类
    # 派生类的属性和方法
    pass

Python
# 示例1:
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print(f"{self.name} makes a sound.")

class Dog(Animal):  # Dog是Animal的派生类
    pass

# 使用派生类
dog = Dog("Buddy")
dog.speak()  # 输出: Buddy makes a sound.

Python
# 示例2:
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        print(f"Hello, I'm {self.name}.")

class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)      # 调用父类的__init__
        self.student_id = student_id     # 扩展新属性

    def study(self):
        print(f"{self.name} is studying.")

    def greet(self):                    # 重写父类方法
        super().greet()                 # 保留父类的逻辑
        print(f"I'm a student with ID {self.student_id}.")

# 使用派生类
student = Student("Alice", 20, "S12345")
student.greet()
# Hello, I'm Alice.
# I'm a student with ID S12345.
student.study()      # Alice is studying.

君子性非异也,善假于物也:利用现成的工具类

数学类库使用

使用标准库 math:

Python
import math

# √16 = ?
result = math.sqrt(16)
print("平方根结果:", result)  

# √2 ≈ ?
print("√2 ≈", math.sqrt(2))  

# √负数会报错?用复数库cmath!
import cmath
print("√(-4) =", cmath.sqrt(-4))

文本文件的写入和读取

Python
# (1)写入文件
with open("test.txt", "w", encoding="utf-8") as f:
    f.write("君子性非异也\n善假于物也\n")

Python
# (2)读取文件
with open("test.txt", "r", encoding="utf-8") as f:
    content = f.read()
print("文件内容:\n", content)

Python
# (3)逐行读取(适合大文件)
with open("test.txt", "r") as f:
    for line in f:
        print("行内容:", line.strip())

互联网资源的下载

使用 requests + os保存文件:

Python
import requests

# def download_image1(url, save_path="downloaded_image1.jpg"):
#     response = requests.get(url, stream=True)
#     if response.status_code == 200:
#         with open(save_path, "wb") as f:
#             for chunk in response.iter_content(1024):
#                 f.write(chunk)
#         print(f"图片已保存到: {save_path}")
#     else:
#         print("下载失败!")

# # Example:下载GitHub的Octocat图标
# download_image1("http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028&app=3028&f=JPEG&fmt=auto?w=960&h=1280")

def download_image2(url, save_path="downloaded_image2.jpg"):
    try:
        response = requests.get(url)
        response.raise_for_status()  # 检查请求是否成功
        
        with open(save_path, "wb") as f:
            f.write(response.content)  # ⭐️关键:直接写入完整二进制内容
        
        print(f"图片已保存到: {save_path}")
    
    except Exception as e:
        print(f"下载失败: {e}")

# Example:下载GitHub的Octocat图标
download_image2("http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028&app=3028&f=JPEG&fmt=auto?w=960&h=1280")