2024-05-15 · 阅读约 14 分钟 · 阅读

5. 异常处理

  • 在编写代码时,可能会遇到各种错误和异常。为了确保程序能够优雅地处理这些错误,Python 提供了异常处理机制。我们将详细介绍如何使用 tryexceptelsefinally 块来处理异常。

基本的异常处理

  • 使用 tryexcept 块可以捕获和处理异常。
# 示例代码
try:
    x = int(input("请输入一个数字: "))
    y = 10 / x
    print(f"10 除以 {x} 的结果是 {y}")
except ZeroDivisionError:
    print("除零错误!不能用零作为除数。")
except ValueError:
    print("输入错误!请输入一个有效的数字。")
  • 在这个示例中,try 块包含可能引发异常的代码。如果发生 ZeroDivisionErrorValueError,相应的 except 块将捕获并处理异常。

elsefinally

  • else 块在没有发生异常时执行,finally 块无论是否发生异常都会执行。
# 示例代码
try:
    x = int(input("请输入一个数字: "))
    y = 10 / x
except ZeroDivisionError:
    print("除零错误!不能用零作为除数。")
except ValueError:
    print("输入错误!请输入一个有效的数字。")
else:
    print(f"10 除以 {x} 的结果是 {y}")
finally:
    print("执行结束。")
  • 在这个示例中,如果没有发生异常,else 块中的代码将执行。不论是否发生异常,finally 块中的代码都会执行。

自定义异常

  • 可以定义自己的异常类,以便更好地描述特定错误。
# 自定义异常类
class NegativeNumberError(Exception):
    pass

def check_positive(number):
    if number < 0:
        raise NegativeNumberError("数字不能为负数!")

# 使用自定义异常
try:
    num = int(input("请输入一个正数: "))
    check_positive(num)
except NegativeNumberError as e:
    print(e)
  • 在这个示例中,定义了一个自定义异常 NegativeNumberError,并在 check_positive 函数中使用。

捕获所有异常

  • 可以使用通用的 except 块来捕获所有异常。
# 示例代码
try:
    x = int(input("请输入一个数字: "))
    y = 10 / x
except Exception as e:
    print(f"发生异常: {e}")
  • 在这个示例中,except Exception 捕获所有类型的异常,并输出异常信息。

通过异常处理机制,我们可以确保程序在遇到错误时能够优雅地处理,而不会导致崩溃。掌握异常处理是编写健壮和可靠代码的重要技能。

抛出异常

assert

  • assert语句用于断言,如果断言失败,程序会抛出AssertionError异常。
# 示例代码
def divide_numbers(a, b):
    assert b != 0, "除数不能为零"
    return a / b

# 使用示例
try:
    result = divide_numbers(10, 0)
except AssertionError as e:
    print(f"断言错误:{e}")

raise

  • 使用raise语句可以主动抛出异常
# 基本用法
def check_age(age):
    if age < 0:
        raise ValueError("年龄不能为负数")
    if age > 150:
        raise ValueError("年龄不能超过150岁")
    return True

# 重新抛出异常
def process_age():
    try:
        age = int(input("请输入年龄:"))
        check_age(age)
    except ValueError as e:
        print("处理异常...")
        raise  # 重新抛出当前异常

异常链

  • 使用raise from可以在抛出新异常时保留原始异常信息
def convert_to_int(text):
    try:
        return int(text)
    except ValueError as e:
        raise ValueError("输入必须是数字") from e

# 使用示例
try:
    number = convert_to_int("abc")
except ValueError as e:
    print(f"错误:{e}")
    print(f"原始错误:{e.__cause__}")

这些抛出异常的方法让我们能够:

  • 使用assert进行调试和验证
  • 使用raise主动抛出异常
  • 通过异常链追踪异常的来源和传播过程

6. 模块和包

在编写较大程序时,将代码组织成模块和包有助于提高代码的可维护性和重用性。Python 提供了强大的模块和包机制。我们将详细介绍以下内容:

模块

模块是一个包含 Python 代码的文件,可以包含变量、函数和类。通过 import 语句可以在其他代码中使用模块。

创建和使用模块:

假设有一个名为 mymodule.py 的文件,内容如下:

# mymodule.py
def greet(name):
    return f"Hello, {name}!"

可以在另一个 Python 文件中导入并使用这个模块:

# main.py
import mymodule

print(mymodule.greet("Alice"))  # 输出 "Hello, Alice!"

也可以使用 from ... import ... 语句导入模块中的特定部分:

# main.py
from mymodule import greet

print(greet("Bob"))  # 输出 "Hello, Bob!"

注意 (不推荐)我们也可以使用from whichmodule import来导入模块中的所有内容,但是这样导入存在风险。倘若该模块中定义的对象与其他模块或python标准包存在冲突,可能导致不可预估的错误!

包是一个包含多个模块的目录,其中包含一个特殊的 __init__.py 文件,该文件可以是空的,也可以包含包的初始化代码。

创建和使用包:

假设有一个名为 mypackage 的目录,结构如下:

mypackage/
    __init__.py
    module1.py
    module2.py

module1.py 的内容如下:

# module1.py
def add(a, b):
    return a + b

module2.py 的内容如下:

# module2.py
def subtract(a, b):
    return a - b

可以在另一个 Python 文件中导入并使用这个包:

# main.py
from mypackage import module1, module2

print(module1.add(5, 3))        # 输出 8
print(module2.subtract(5, 3))   # 输出 2

使用标准库

Python 附带了一个丰富的标准库,提供了大量有用的模块,可以直接导入和使用。例如,使用 math 模块进行数学运算:

import math

print(math.sqrt(16))  # 输出 4.0

安装第三方包

可以使用 pip 安装第三方包。例如,安装和使用 requests 包来发送 HTTP 请求:

pip install requests

安装后,可以在代码中导入并使用 requests 包:

import requests

response = requests.get("https://api.github.com")
print(response.status_code)  # 输出 200

模块和包使得代码更加模块化和可重用,有助于组织和管理大型项目。

wheel

尽管我们可以通过pip安装第三方包,但有些包并不在pypi上,这时候就需要使用wheel文件来安装。

wheel文件是python的二进制包格式,通常用于分发和安装Python包。与源代码包不同,wheel包已经编译为二进制形式,可以直接在目标系统上安装,而无需编译源代码。

wheel文件通常以.whl为扩展名,例如requests-2.28.2-py3-none-any.whl。

使用wheel安装第三方包的方法如下:

  1. 从镜像平台其他途径获取wheel包
  2. 使用pip在自己环境中安装wheel包
    pip install requests-2.28.2-py3-none-any.whl
    

镜像

由于某些神秘力量,国内访问pypi的速度很慢,这时候就需要使用镜像来加速下载。 常用的镜像源有:

  • 清华:https://pypi.tuna.tsinghua.edu.cn/simple
  • 阿里云:http://mirrors.aliyun.com/pypi/simple/
  • ustc: https://pypi.mirrors.ustc.edu.cn/simple/
  • hust:http://pypi.hustunique.com/
  • sdut:http://pypi.sdutlinux.org/
  • 豆瓣:http://pypi.douban.com/simple/

如何配置镜像源?

1. 永久配置镜像源

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/
pip config set install.trusted-host pypi.tuna.tsinghua.edu.cn

2. 临时使用镜像源

使用-i

pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple 

参考资料

7. 字符串处理

字符串是Python中最常用的数据类型之一。理解和掌握字符串处理方法是编写高效Python代码的基础。

1. 字符串创建与基本操作

  • 字符串可以使用单引号、双引号或三引号创建。
# 创建字符串
str1 = 'Hello'
str2 = "World"
str3 = '''This is
a multiline
string'''

print(str1)  # 输出 Hello
print(str2)  # 输出 World
print(str3)  # 输出 This is\n a multiline\n string

2. 字符串拼接

  • 可以使用加号(+)拼接字符串,使用乘号(*)重复字符串。
# 字符串拼接
greeting = str1 + ' ' + str2
print(greeting)  # 输出 Hello World

# 字符串重复
repeat_str = str1 * 3
print(repeat_str)  # 输出 HelloHelloHello

注意 使用*重复数据的操作在其他数据类型同样可以使用。但是请注意,* 在Python中的逻辑是引用复制,即重复使用同一对象。这可能会导致用户在数值计算等可能出现引用类型套引用类型的情况下出现容易忽视的灾难性错误:

>> array = []
>> array.append([[1,1,1,1]]*2)
>> print(array)
[[[1, 1, 1, 1], [1, 1, 1, 1]]]
>> array[0][0][0]
1
>> array[0][0][0] *= 2 # 使用 array[0][0][0] = array[0][0][0] * 2 结果相同
>> array
[[[2, 1, 1, 1], [2, 1, 1, 1]]]

明明只修改了array[0][0][0], 但array[0][1][0]的值也被修改了! 这提醒我们在使用*时,应当注意不要对存在引用共享的数据进行操作。

3. 字符串格式化

  • Python提供了多种字符串格式化方法,包括百分号格式化、str.format()方法和f字符串(格式化字符串字面量)。
name = "Alice"
age = 25

# 百分号格式化
print("Hello, %s. You are %d years old." % (name, age))

# str.format()方法
print("Hello, {}. You are {} years old.".format(name, age))

# f字符串(Python 3.6及以上)
print(f"Hello, {name}. You are {age} years old.")

Python中,字符串格式化不仅可以将变量嵌入字符串中,还可以通过在格式化占位符中指定格式来控制显示效果。对于str.format()方法和f字符串f"...",你可以指定显示的精度、对齐方式、填充字符、宽度等。 下面是一个简单的表格,用于展示不同字符串格式化方法的对比和一些常见用法:

格式化方法 语法 说明 示例
百分号格式化 "Hello, %s. You are %d years old." % (name, age) 使用 % 占位符格式化,类似 C 语言中的 printf print("Hello, %s. You are %d years old." % ("Alice", 25))
str.format() "Hello, {}. You are {} years old.".format(name, age) 使用 {} 占位符并通过 format() 方法传递参数 print("Hello, {}. You are {} years old.".format("Alice", 25))
f字符串(Python 3.6+) f"Hello, {name}. You are {age} years old." 在字符串前加 f,直接在 {} 中插入变量或表达式 print(f"Hello, {name}. You are {age} years old.")
对齐与宽度控制 "{:<10}".format(name), "{:>10}".format(name) 设置最小宽度并指定对齐方式(< 左对齐,> 右对齐) print("{:<10} is {} years old.".format("Alice", 25))
填充字符 "{:*^10}".format("hello") 使用填充字符进行对齐(例如 * 填充) print("{:*^10}".format("hello"))
浮动点精度控制 "{:.2f}".format(pi) 设置小数位数精度 print("{:.2f}".format(3.141592653589793))
科学计数法 "{:.2e}".format(pi) 使用科学计数法表示数字 print("{:.2e}".format(3.141592653589793))
列表和字典格式化 "{name}, {age}".format(name="Alice", age=25) 可以通过命名字段来传递字典中的值 print("{name} is {age} years old.".format(name="Alice", age=25))
表达式计算 f"2 + 2 = {2 + 2}" f字符串支持嵌入表达式并计算 print(f"2 + 2 = {2 + 2}")

4. 字符串索引&切片

  • 可以使用索引访问字符串中的单个字符,使用切片获取子字符串。
# 字符串索引
s = "Python"
print(s[0])  # 输出 P
print(s[-1])  # 输出 n

# 字符串切片
print(s[1:4])  # 输出 yth
print(s[:3])  # 输出 Pyt
print(s[3:])  # 输出 hon

5. 常用字符串方法

  • Python提供了许多内置的字符串方法,用于各种字符串操作。
s = "hello, world"

# 大小写转换
print(s.upper())  # 输出 HELLO, WORLD
print(s.lower())  # 输出 hello, world
print(s.capitalize())  # 输出 Hello, world
print(s.title())  # 输出 Hello, World

# 去除空白字符
s2 = "  hello  "
print(s2.strip())  # 输出 hello
print(s2.lstrip())  # 输出 hello  
print(s2.rstrip())  # 输出   hello

# 查找和替换
print(s.find("world"))  # 输出 7
print(s.replace("world", "Python"))  # 输出 hello, Python

# 拆分和连接
words = s.split(", ")
print(words)  # 输出 ['hello', 'world']
print(", ".join(words))  # 输出 hello, world

6. 字符串的不可变性

  • 字符串在Python中是不可变的,一旦创建,就不能修改其内容。任何对字符串的操作都会创建一个新字符串。
s = "hello"
# s[0] = 'y' 错误!
s = s.replace("h", "y")
print(s)  # 输出 yello

7. 字符串的编码和解码

  • 可以使用encode()方法将字符串编码为字节,使用decode()方法将字节解码为字符串。
# 编码
s = "hello"
b = s.encode("utf-8")
print(b)  # 输出 b'hello'

# 解码
s2 = b.decode("utf-8")
print(s2)  # 输出 hello

8. 多行字符串和转义字符

  • 多行字符串可以使用三引号创建,转义字符用于表示特殊字符。
# 多行字符串
multi_line_str = """This is a
multiline
string"""
print(multi_line_str)

# 转义字符
escaped_str = "He said, \"Hello!\""
print(escaped_str)  # 输出 He said, "Hello!"

8. 文件处理

在实际编程中,经常需要读取和写入文件。Python 提供了强大的文件处理功能,允许我们轻松地操作文件。

打开和关闭文件

  • 可以使用 open 函数打开文件,使用 close 方法关闭文件。
# 打开文件
file = open("example.txt", "r")

# 读取文件内容
content = file.read()
print(content)

# 关闭文件
file.close()

使用 with 语句

  • with 语句可以自动处理文件的打开和关闭,确保文件在使用完后正确关闭。
# 使用 with 语句打开文件
with open("example.txt", "r") as file:
    content = file.read()
    print(content)

读取文件

  • 可以使用 readreadlinereadlines 方法读取文件内容。
# 读取整个文件
with open("example.txt", "r") as file:
    content = file.read()
    print(content)

# 逐行读取文件
with open("example.txt", "r") as file:
    for line in file:
        print(line.strip())

# 读取一行
with open("example.txt", "r") as file:
    line = file.readline()
    print(line)

# 读取所有行并返回列表
with open("example.txt", "r") as file:
    lines = file.readlines()
    print(lines)

写入文件

  • 可以使用 writewritelines 方法写入文件内容。
# 写入文件
with open("example.txt", "w") as file:
    file.write("Hello, World!\n")
    file.write("This is a new line.\n")

# 写入多行
lines = ["First line.\n", "Second line.\n", "Third line.\n"]
with open("example.txt", "w") as file:
    file.writelines(lines)

文件模式

可以使用不同的模式打开文件:

  • 'r':读取(默认模式)
  • 'w':写入(会覆盖文件)
  • 'a':追加(在文件末尾添加内容)
  • 'b':二进制模式(用于非文本文件)
# 以追加模式打开文件
with open("example.txt", "a") as file:
    file.write("This line is appended.\n")

# 以二进制模式读取文件
with open("example.jpg", "rb") as file:
    content = file.read()
    print(content)

文件和目录操作

  • 使用 os 模块可以进行文件和目录操作。
import os

# 检查文件是否存在
print(os.path.exists("example.txt"))

# 获取文件大小
print(os.path.getsize("example.txt"))

# 重命名文件
os.rename("example.txt", "new_example.txt")

# 删除文件
os.remove("new_example.txt")

# 创建目录
os.mkdir("example_dir")

# 删除目录
os.rmdir("example_dir")