欢迎来到魔幻小生的博客

python 函数、异常处理、文件操作、模块、包

函数

函数就是能实现一定功能的代码语句的集合。

def 函数名(参数):
	函数体
# 关键字参数(可以不按顺序传参)
def add(a, b):
    return a + b

add(b=1, a=2)

# 默认参数(不传参使用默认值)
def add(a, b, c = 3):
    return a + b + c

add(1, 2)
add(1, 2, 4)

跟其他语言一样, Python 函数定义同样支持无形参、有形参、可变参数等;而函数可以有返回值,也可以没有返回值。

  • 无形参—不需要参数输入
# 无形参,无返回值
def print_log():
    print(''' Welcome to Kevin's class ! ''')

if __name__ == "__main__":
    print_log()
  • 有形参—函数接受用户参数
# 有一个形参,有返回值
def is_true(x):
    return x > 0

# 有两个形参,有返回值
def min_number(x, y):
    if x>=y:
       x,y = y, x
    return x 

# 可变参数
# 有可变个形参, 有返回值
def sum_number(*args):
    total = 0
    for k in args:
        total +=k
    return total

# 可变个形参,接受key-value的形式, 无返回值
def count_student(**kwargs):
    for k, v in kwargs.items():
        print('{0} - {1}'.format(k, v))


if __name__ == "__main__":
    print(is_true(-2))
    total = sum_number(1, 2)
    print(total)
    min = min_number(1, 2)
    print(min)
    count_student(math='kevin', logic='emily')

image

函数的作用域

  • 全局变量:在函数外面定义,整个文件都可以读取

  • 局部变量:函数内部定义,函数执行完销毁

g_num = 100  # 全局变量

def func():
    g_num = 10
    return g_num  # ✅可以读全局

func()
print(g_num)  # 100

global 关键字

告诉 Python:这个变量是全局变量,函数内修改全局

g_num = 100  # 全局变量

def func():
    global g_num
    g_num = 10
    return g_num  # ✅可以读全局

func()
print(g_num)  # 10

嵌套函数

函数里面嵌套函数,内层可以读取外层函数的局部变量,外层不能读取内层的局部变量。

def outer():
    x = 20
    def inner():
        x = 999
    inner()
    print(x)

outer()  # 20

nonlocal 关键字

修改使外层能够读取内部的局部变量

def outer():
    x = 20
    def inner():
        nonlocal x
        x = 999
    inner()
    print(x)

outer()  # 999

异常处理

异常就是程序运行时发生错误,若不捕获,程序直接崩溃退出。如果做了异常处理,程序就不会中断,保证程序继续正常执行。

try:
    # 可能出错的代码
    代码块
except 异常类型1:
    # 捕获对应异常后的处理
    处理代码
except 异常类型2 as e:
    # e 拿到异常对象,可打印错误信息
    处理代码
else:
    # ✅ try中没有发生异常才执行
    正常执行代码
finally:
    # 无论是否异常,一定执行(常用于关闭文件、释放资源)
    必执行代码

简单示例:

try:
    a = 1 / 0
except ZeroDivisionError as err:
    print(f"捕获到错误:{err}")
else:
    print("没有异常才走到这里")
finally:
    print("永远执行,做资源清理")

主动抛出异常: raise

def check_age(age):
    if age < 0:
        raise ValueError("年龄不能是负数")
    return age

try:
    check_age(-5)
except ValueError as e:
    print(e)

自定义异常

class MyBusinessError(Exception):
    """自定义业务异常"""
    pass

try:
    raise MyBusinessError("业务逻辑出错啦")
except MyBusinessError as e:
    print(f"捕获自定义异常:{e}")

文件操作

Python 使用内置 open() 函数完成文件读写,不需要额外安装库,支持文本文件、二进制文件。传统的文件操作示例如下:

file = open('example.txt', 'r')
try:
    # 处理文件内容
    content = file.read()
finally:
    # 关闭文件释放资源
    file.close()

这种写法存在几个问题:

  1. 容易忘记关闭资源:如果没有 try-finally 块,可能会忘记调用 close()
  2. 代码冗长:简单的文件操作需要多行代码
  3. 异常处理复杂:需要手动处理可能出现的异常

with 语句

with 语句通过上下文管理协议(Context Management Protocol)解决了这些问题:

  1. 自动资源释放:确保资源在使用后被正确关闭
  2. 代码简洁:减少样板代码
  3. 异常安全:即使在代码块中发生异常,资源也会被正确释放
  4. 可读性强:明确标识资源的作用域
with open('example.txt', 'r') as file:
    content = file.read()
    print(content)
# 文件已自动关闭
模式 说明
r read 只读,文件不存在报错,默认模式
w write 只写,文件不存在创建;存在则清空原有内容
a append 追加,在文件末尾写入,不会清空旧内容
r+ 读写,文件必须存在,可读可写
w+ 读写,不存在创建,存在直接清空
a+ 追加读写,指针在末尾
b 二进制模式:rbwbab,用于图片、视频、exe

读取文件

with open('example.txt', 'r', encoding='utf-8') as file:
    # 读取全部
	context = file.read()
	
	# 读取一行
	line = file.readline()
	
	# 读取所有行
	lines = f.readlines()

写入文件

with open('example.txt', 'w', encoding='utf-8') as file:
    file.write('hello world')
	
	# 写入多行
	lines = ["苹果\n", "香蕉\n", "橙子\n"]
	f.writelines(lines)

二进制读写(图片、视频)

不要加 encoding 参数

with open('test.jpg', 'rb') as file:
    data = file.read()
	
with open('copy.jpg', 'wb') as file:
    file.write(data)

文件指针 seek /tell (了解)

with open("test.txt", "r", encoding="utf-8") as f:
    print(f.tell())  # 获取当前文件指针位置
    f.seek(5)   # 跳到第5字节
    print(f.read())

模块

模块是为了编写可维护的代码,而把函数分组放到不同文件里的行为。在 Python 中,一个 .py文件 就是一个模块,一个模块可以包括一个或多个功能,模块又可以被一个或多个其他模块引用。

  • 提高编程效率,增强代码可维护性

把模块导入当前模块,当前模块即可拥有模块已经实现的功能。如果模块的功能本身需要更改,我们只需要更改模块定义的地方即可,其他地方都无须更改。

  • 不同模块的函数名和变量名可以重名

有了模块,避免了函数名和变量名之间的冲突,例如如下的文件结构:

myproject
  |--module1.py
  |--module2.py

假设我在 module1.py 里和 module2.py 里,同时定义一个名字为 take_picture() 的函数。这两个不同模块的函数虽然都叫 take_picutre,但其行为可以不相同,也不会相互影响。

包(Package)可以用于解决模块名称相同的问题。包就是一个目录文件,它必须包含一个名为 __init__.py 的文件。

myproject
  |-- web
    |-- module.py
    |-- __init__.py
  |-- API
    |-- module.py
    |-- __init__.py
  |-- __init__.py

在 web 层级和 API 层级它们都包含着名字相同的模块 module.py,以下列出不同包下的模块引用方式:

# 引入web目录下的包, 假设此目录下module.py里有类变量Module
from web.module import Module

# 引入API目录下的包,假设此目录下module.py里有类变量Module
from API.module import Module

模块的导入

  • 直接导入
import module1

module1.take_picture()
  • from...import 方式导入

有时候我们并不想把一个模块的所有功能都导入进来,假设我只想使用 take_picture 这一个方法,那么我可以使用 from...import 的方式:

from module1 import take_picture

take_picture()
  • from...import* 方式导入(不推荐使用)

如果你想一次性地导入一个模块下的所有函数, 你可以使用如下方式:

from module1 import *
  • 动态导入

但在实际应用中,也会有在程序运行时才知道要具体导入哪个模块的情况(例如,测试框架自动查找测试用例并导入测试用例所属的模块),这时就需要动态导入。

动态导入常常用 importlib 来完成,常用的动态导入有以下两种方式。

  1. 从模块直接导入
import importlib 
#导入a.b模块
mod = importlib.import_module( "a.b")
  1. 根据模块名,文件名导入
import importlib.util

spec = importlib.util.spec_from_file_location("a.b", "/path/to/file.py")
md = importlib.util.module_from_spec(spec)
spec.loader.exec_module(md)

下面我来举个具体的例子, 假设现在我们的项目目录情况如下:

myproject
  |-- tests
    |-- a.py
    |-- __init__.py
  |-- b.py

在模块 a.py 里,我定义了一个函数:

def hello():
    print('i am module a!')

然后在模块 b.py 中使用hello()函数

#b.py
import os
import glob
import importlib.util

def find_modules_from_folder(folder):
    absolute_f = os.path.abspath(folder)
    md = glob.glob(os.path.join(absolute_f, "**/*.py"))
    return [(os.path.basename(f)[:-3], f) for f in md if os.path.isfile(f) and not f.endswith('__init__.py')]

def import_modules_dynamically(mod, file_path):
    spec = importlib.util.spec_from_file_location(mod, file_path)
    md = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(md)
    return md

if __name__ == "__main__":
    module = find_modules_from_folder('.')
    for m in module:
        mod = import_modules_dynamically(m[0], m[1])
        mod.hello()

这个代码有点复杂,我先给定一个文件夹,然后通过函数 find_modules_from_folder 来得到这个文件夹下的模块,及其对应的文件路径,然后我再通过 spec_from_file_location 来动态加载。

常用内置模块

os 模块

用于与操作系统进行交互,提供了许多操作系统相关的功能,比如文件和目录操作、获取系统信息等。

# 导入了os模块
import os

# 打印当前命令所在的目录
print("当前命令所在的目录:", os.getcwd())
# 获取当前文件所在工作目录
print("__file__", __file__)

print("os.path.dirname(__file__): ", os.path.dirname(__file__))

# 创建一个新目录
new_dir_name = "new_directory"
if not os.path.exists(new_dir_name):
    os.makedirs(new_dir_name)
    print("创建文件成功")

# 列出当前目录下的所有文件和目录
files_and_dirs = os.listdir('.')
print("当前目录下的文件和目录:", files_and_dirs)

# 删除刚才创建的目录(需确保目录为空)
if os.path.exists(new_dir_name):
    os.rmdir(new_dir_name)

sys 模块

提供了对 Python 解释器相关的一些变量和函数的访问,比如获取命令行参数、模块搜索路径等。

print("__file__", __file__)

import sys

# 获取命令行参数(不包括脚本名本身)
# 常见命令行参数的参数名称一般是以 - 开头,如 -h、-v 等,值就不是以 - 开头
arguments = sys.argv[1:] # 获取除了脚本名本身以外的所有参数
print("命令行参数:", arguments)

# 查看模块搜索路径,了解即可
module_paths = sys.path
print("模块搜索路径:", module_paths)

math 模块

包含了各种数学函数和常量,用于数学计算,比如三角函数、对数函数、幂函数等。

import math

# 计算圆的面积,半径为5
radius = 5
area = math.pi * radius ** 2
# area = math.pi * math.pow(radius, 2)
print("圆的面积:", area)

# 向上取整
print("向上取整:", math.ceil(3.15))
# 向下取整
print("向上取整:", math.floor(3.15))
# 四舍五入
print("四舍五入:", round(3.15), round(3.5))

random 模块

用于生成随机数,可以生成随机整数、随机浮点数、随机序列等。

import random

# 生成一个0到1之间的随机浮点数
random_float = random.random()
print("随机浮点数:", random_float)

# 一个随机整数 N,满足 a <= N <= b
random_int = random.randint(1, 100)
print("随机整数:", random_int)

# 随机打乱一个列表的顺序
my_list = [1, 2, 3, 4, 5]
random.shuffle(my_list)
print("打乱后的列表:", my_list)

# 随机取出列表中的一个数据
random_item = random.choice(my_list)
print("随机取出的元素:", random_item)

# 从字符串中随机选择一个字符
random_char = random.choice("hello")
print("随机字符:", random_char)

time 模块

用于处理时间相关的操作,比如获取当前时间、设置定时器、时间格式化等。

import time

# 获取当前时间的时间戳(从1970年1月1日00:00:00 UTC到当前时刻的秒数)
start_time = time.time()
print("当前时间戳:", start_time)
# 了解一段程序的执行时间,思路就是在程序开始前获取时间戳,在程序结束后获取时间戳,然后计算时间差

# 将时间戳转换为结构化的时间对象
struct_time = time.localtime(start_time)
print("结构化时间对象:", struct_time)
# 如果获取时间中年月日和时分秒
print("年:", struct_time.tm_year)
print("月:", struct_time.tm_mon)
print("日:", struct_time.tm_mday)
print("时:", struct_time.tm_hour)
print("分:", struct_time.tm_min)
print("秒:", struct_time.tm_sec)
print("周:", struct_time.tm_wday)

# 格式化时间,例如按照"年-月-日 时:分:秒"的格式
formatted_time = time.strftime("%Y-%m-%d %H:%M:%S", struct_time)
print("格式化时间:", formatted_time)

# 程序执行暂停3秒,在web自动化开发时比较常用
time.sleep(3)
print("程序执行暂停3秒")

end_time = time.time()
print("程序执行结束,总耗时:", end_time - start_time, "秒")

datetime 模块

是 time 模块的增强版,提供了更方便的日期和时间处理功能,比如创建日期时间对象、计算时间间隔等。

from datetime import datetime, timedelta

# 获取当前日期和时间
now = datetime.now()
print("当前日期和时间:", now)

# 创建一个指定日期和时间的对象
specific_date = datetime(2024, 12, 31, 23, 59, 59)
print("指定日期和时间:", specific_date)
d1 = datetime.fromisoformat("2024-12-31T23:59:59")
print("d1", d1)
d2 = datetime.strptime("2024年12月31日 23时59分59秒", "%Y年%m月%d日 %H时%M分%S秒")
print("d2", d2)

# 计算两个日期时间之间的时间间隔
time_difference = specific_date - now
print("时间间隔:", time_difference)

# 将时间间隔转换为秒
seconds = time_difference.total_seconds()
print("时间间隔(秒):", seconds)
# abs取绝对值
print("time_difference.days", time_difference.days, abs(time_difference.days))

# 在当前时间基础上增加1小时
new_time = now + timedelta(hours=1)
print("增加1小时后的时间:", new_time)
a = timedelta(hours=1, minutes=1, seconds=1, milliseconds=1, microseconds=1, days=1, weeks=1)
print("a", a)

json 模块

用于处理 JSON 数据,包括 JSON 数据的编码(将 Python 对象转换为 JSON 字符串)和解码(将 JSON 字符串转换为 Python 对象)。很多接口返回的都是 json 数据,如何去取出对应数据呢,就需要对 json 数据进行转化,一般是转换成字典。

json 格式的字符串,与 python 中的字典很相似,key 只能是字符串。

import json

# 将Python字典转换为JSON字符串
my_dict = {"name": "Alice", "age": 30}
json_string = json.dumps(my_dict)
print("JSON字符串:", json_string, type(json_string))

# 将JSON字符串转换为Python字典
new_dict = json.loads(json_string)
print("转换后的字典:", new_dict, type(new_dict))

# 字符串,长得像字典
my_dict_str = '{"name": "Alice", "age": 30}'
print("my_dict_str的类型", type(my_dict_str))
# 错误的将字符串当成字典来使用
# print("错误的将字符串当成字典来使用", my_dict_str['name'])
# 此时可以使用,json.loads只能符合json格式的字符串进行转换
new_dict1 = json.loads(my_dict_str)
print("转换后的字典new_dict1:", new_dict1)
print("转换后的字典new_dict1['name']:", new_dict1['name'])

# 这是一个非json格式的字符串
str = "acb"
# 无法转换
j = json.loads(str)
print(j)

# 将一个列表字符串转成成列表对象。
str = '["a", "b", "c"]'
lst = json.loads(str)
print("lst", lst, lst[0])

re 模块

用于正则表达式的处理,包括匹配、搜索、替换等操作,在文本处理中非常有用。

import re

# 匹配字符串中是否存在数字
string = "ab2c123de1f"
match_result = re.search(r'\d', string)  # \d表示数字,是正则表达式的写法
if match_result:
    print("字符串中存在数字")
else:
    print("字符串中不存在数字")

# 替换字符串中的所有数字为"*"
new_string = re.sub(r'\d', '*', string)
print("替换后的字符串:", new_string)
posted @ 2025-02-18 23:24  魔幻小生  阅读(64)  评论(0)    收藏  举报