从Python代码到诗

在这里插入图片描述

🐳序言

  在Python社区,没有强制的编码标准,这虽然赋予了开发者更多的自由,但也导致代码风格不一致性。使得部分代码变得晦涩难懂,本文将探讨一系列的开发技巧和最佳实践,开发出优雅的Python脚本。

1、参数接收与校验

argparse模块可以使用标准的命令行界面风格解析命令行参数,支持位置参数和可选参数,使用-h可查看脚本使用说明。

1.1 ArgumentParser常用属性

字段说明备注
name参数名称或选项例如:-file、–file
default默认值
type类型
choices可选值列表
required是否必传默认为False
nargs参数的数量默认为None,支持int,+,*
metavar帮助信息显示参数的名字
action动作支持store_true、store_false等
help帮助信息

1.2 获取位置参数

使用位置参数我们可以处理n多个值,输出列表

# python main.py 3 5import argparseparser = argparse.ArgumentParser(description="位置参数测试")
parser.add_argument("number", type=int, nargs="*", help="传入数字")args = parser.parse_args()# [3, 5]
print(args.number)

1.3 获取可选参数

使用可选参数可以避免传入位置不准确的问题

# python main.py --name '欢颜' --datatime '2023-08-18 13:14:52' --is_worktimeimport argparsefrom datetime import datetimeclass ParserArgs:def __init__(self):parser = argparse.ArgumentParser(description='从Python代码到诗')parser.add_argument('--name', type=str, required=True, help='输入名称')parser.add_argument('--datetime', type=self.validate_dt, required=True, help='日期时间(YYYY-MM-DD HH:MM:SS)')parser.add_argument('--is_worktime', action='store_true', help='是否在工作时间')self.args = parser.parse_args()@staticmethoddef validate_dt(dt_str, fmt_dt='%Y-%m-%d %H:%M:%S'):try:dt_obj = datetime.strptime(dt_str, fmt_dt)except ValueError:raise argparse.ArgumentTypeError("Invalid datetime format.")else:return dt_obj# Namespace(name='欢颜', datetime=datetime.datetime(2023, 8, 17, 0, 0), is_worktime=True)
Args = ParseArgs().args

2、日志记录器

​ 项目开发中应避免直接使用print函数,日志记录能够更好地管理和调试代码、故障排除以及监控程序的运行状态。Python内置了logging 模块,用于创建日志记录器,实现灵活的日志记录和输出控制,通常将日志直接写入文件,输出更加优雅。

2.1 日志简单使用

#!/usr/bin/env python3
# -*-coding:utf-8 -*-import loggingdef get_logger():# 实例化日志对象logger = logging.getLogger()logger.setLevel(logging.INFO)ch = logging.StreamHandler()formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] [%(filename)s] [%(funcName)s:%(lineno)d] %(message)s", "%Y-%m-%d %H:%M:%S")ch.setFormatter(formatter)logger.addHandler(ch)return loggerlogger = get_logger()# [2023-08-18 16:40:07] [INFO] [main.py] [main:27] hello world!
logger.info("hello world!")

3、进度条的使用

  在命令行界面中展示进度条,以增强长时间运行的循环、任务或操作的可视化体验。Python的tqdm库,可以非常方便地应用于各种迭代操作,从文件读写到数据处理,都能通过进度条的方式直观地显示进度。

3.1 tqdm方法常用参数

参数含义类型必填备注
iterableIterable 用进度条装饰iterable可选可迭代对象
total预计迭代的次数int/float可选默认为迭代器的长度
ncols整个输出消息的宽度int可选
mininterval最小进度显示更新间隔float可选默认0.1s
ascii进度填充bool可选默认unicode平滑块来填充仪表
unit单位str可选默认为it
color条形颜色str可选
initial初始计数器值int/float可选

3.2 trange方法

trange方法是封装了range方法的tqdm, 它会在循环中显示进度条、多用于循环次数固定的情况。

import timefrom tqdm import trangefor _ in trange(10):time.sleep(0.1)

3.3 tqdm方法

tqdm是tqdm模块最常用的方法,用于在循环中显示进度条。传递一个迭代对象给它,然后迭代这个对象,会产生一个进度条。

import timefrom tqdm import tqdm
from colorama import Fore, Styleiterable = ['Python', 'GoLang', 'Java', 'JavaScript']
progress_bar = tqdm(iterable, ncols=100, ascii=False)
for element in progress_bar:progress_bar.set_description("{}正在处理:{}".format(Fore.LIGHTBLUE_EX, element))time.sleep(0.5)

4、测试函数执行时间

  Python关于计时比较有代表性的两个库是timetimeittime库中有time()perf_counter()以及process_time()三个函数可用来计时(以秒为单位),加后缀_ns表示以纳秒计时。上述三者的区别如下:

  • time()精度上相对没有那么高,而且受系统的影响,适合表示日期时间或者大程序的计时。
  • perf_counter()适合小一点的程序测试,会计算sleep()时间。
  • process_time()适合小一点的程序测试,不计算sleep()时间。

time库相比,timeit 有两个优点:

  • timeit 会根据您的操作系统和 Python 版本选择最佳计时器。
  • timeit 在计时期间会暂时禁用垃圾回收。

4.1 使用time计时

import timedef statistic_cost(func):def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)cost = time.time() - start_timelogger.info(f"execute {func.__name__} cost {cost:.3f} 秒")return resultreturn wrapperdef test():time.sleep(3)if __name__ == '__main__':test()

4.2 使用timeit

"""
timeit方法参数:stmt: 需要计时的语句或者函数
setup: 执行stmt之前要运行的代码。通常,它用于导入一些模块或声明一些必要的变量。
timer: 计时器函数,默认为time.perf_counter()。
number: 执行计时语句的次数,默认为一百万次。
globals: 指定执行代码的命名空间。
"""import time
from timeit import timeitdef test():time.sleep(3)if __name__ == '__main__':cost = timeit(test, number=1)print(cost)

5、使用装饰器缓存

  Python支持装饰器缓存,在内存中维护特定类型的缓存,以实现最佳软件驱动速度,使用lru_cache来实现。lru_cache 的性能优势在于重复性高、计算密集型的函数。在一些情况下,使用缓存可能会导致额外的内存使用,因为缓存会保留一部分函数调用的结果。因此,应根据具体情况权衡是否使用缓存。对于需要实时更新或频繁变动的数据,lru_cache 并不适用。它适用于那些函数的计算结果在短时间内保持一致的场景。

5.1 缓存简单使用

#!/usr/bin/env python3
# -*-coding:utf-8 -*-
# maxsize: 指定缓存中保留的最大函数调用结果数量。当超过数量,最早的结果将被移除,默认为128。
# typed: 如果设置为True,则不同类型的参数会被视为不同的参数。import functools@functools.lru_cache(maxsize=128)
def fibonacci(n):if n == 0:return 0elif n == 1:return 1return fibonacci(n - 1) + fibonacci(n - 2)# 从获取相同
for _ in range(10):fibonacci(10)

5.2 缓存强制刷新

  lru_cache函数不支持主动刷新的功能, 缓存的更新是由缓存装饰器内部的缓存策略自动管理的,通常是根据函数的参数和调用次数来决定哪些缓存条目会被保留或淘汰。如果需要在特定时刻主动刷新缓存, 可以使用cache_clear() 方法强制清除缓存中的所有条目,从而让缓存失效,下次访问时会重新计算。你可以在数据发生变动时调用此方法。

# 以上面计算斐波那契函数为例# 强制清除缓存
fibonacci.cache_clear()

6、单元测试

本文使用unittest编写单元测试

  单元测试是软件开发过程中的一个关键环节,旨在验证代码中的最小功能单元是否按预期工作。通过编写单元测试,你可以有效地发现和修复代码中的错误,确保代码在不同情况下都能正确运行。在Python中有多个测试框架可以选择unittest、pytest和nose等。

6.1 单元测试简单使用

6.1.1 编写一个函数用于测试
import functools@functools.lru_cache(maxsize=128)
def fibonacci(n):if n < 0:raise ValueError('Invalid Value')elif n in (0, 1):return nreturn fibonacci(n - 1) + fibonacci(n - 2)
6.1.2 使用unittest完成测试
import unittestfrom script import main as algclass TestFibonacci(unittest.TestCase):def test_fibonacci(self):output = alg.fibonacci(10)self.assertEqual(output, 55)def test_fibonacci_invalid(self):with self.assertRaises(ValueError):alg.fibonacci(-1)if __name__ == '__main__':unittest.main()

6.2.1 模拟对象

  模块unittest.mock提供了两个主要的类来进行模拟和断言行为(Mock和MagicMock),Mock通常用于模拟普通方法和属性,MagicMock具有更多的魔法方法,允许一些特殊的Python行为,如迭代,上下文管理等,返回自身。

对以下函数进行测试

ParserArgs对象在参数接收于校验模块

import time
from colorama import Forefrom logger import logger
from parser_args import ParserArgsdef launch():args = ParserArgs().argslogger.info(f"input args: {args.__dict__}")iterable = ['Python', 'GoLang', 'Java', 'JavaScript']progress_bar = tqdm(iterable, ncols=100, ascii=False)for element in progress_bar:progress_bar.set_description("{}正在处理:{}".format(Fore.LIGHTBLUE_EX, element))return Trueif __name__ == '__main__':launch()

模拟获取参数的行为

替换ParserArgs的参数为mock对象

import argparse
import unittest
from unittest.mock import patchfrom script import main as algclass TestParserArgs(unittest.TestCase):def setUp(self) -> None:self.mock_args = argparse.Namespace(name='欢颜',datetime='2023-05-20 13:14:00',is_worktime=True)@patch('script.main.ParserArgs')def test_launch(self, mock_parser_args):parser_args_obj = mock_parser_args.return_valueparser_args_obj.args = self.mock_argsoutput = alg.launch()expect = Trueself.assertEqual(output, expect)if __name__ == '__main__':unittest.main()

7、包的构建与分发

7.1 打包程序为可执行文件(EXE)

  可以使用pyinstaller将单文件应用或多文件应用,打包为exe可执行文件,需要编译作为程序入口的文件即可。

7.1.1 打包应用程序

pip3 install pyinstaller

# pyinstaller -F app.pydef main():print('程序开始执行')if __name__ == '__main__':main()
7.2.2 Pyinstaller常用选项
-h帮助信息
-F产生单个的可执行文件
-D产生一个目录(包含多个文件)作为可执行程序
-a不包含 Unicode 字符集支持
-d产生 debug 版本的可执行文件
-w指定程序运行时不显示命令行窗口(仅对 Windows 有效)
-c指定使用命令行窗口运行程序(仅对 Windows 有效)
-o指定 spec 文件的生成目录。如果没有指定,则默认使用当前目录来生成 spec 文件
-p设置 Python 导入模块的路径
-n指定项目名字。如果省略该选项,那么第一个脚本的主文件名将作为 spec 的名字

7.2 使用setuptools构建包

7.2.1 创建合适的项目结构
# 一个最简单的项目结构
pkgs/
├── LICENSE       # 许可证文件
├── main.py       # 入口文件
├── app           # 存放子文件等
├── README.md     # 应用文档
└── setup.py      # 用于构建、安装和分发包的脚本文件
7.2.2 编写setup文件

一个最简单的setup示例

from setuptools import setup, find_packagessetup(name='PkgName',version='0.1',description='描述信息',author='Your Name',packages=find_packages(),install_requires=[# 依赖项列表],
)
7.2.3 构建包

  在项目根目录中运行python setup.py sdist生成源分发包,也可以生成二进制分发包bdist_wheel。然后可以将包发布到Python包索引(PyPI),这需要在PyPI上创建一个帐户,并使用工具如twine来上传包(twine upload dist/*)。

7.2.4 分发包

  您可以通过以下方式分享生成的分发包,将其发布到代码托管平台如GitHub,上传至内部或外部的PyPI服务器,或者使用pip来进行安装包。

🧀 小结

  编写一个优雅的Python脚本关键在于遵循Python的最佳实践和编码规范(PEP8),以确保代码风格一致性和可读性。使用有意义的命名、拆分代码块成小函数、适度添加注释和文档、合理处理异常、充分利用Python的内置函数和模块、避免滥用全局变量、保持模块化和可重用性、采用测试驱动开发等方法,有助于编写出清晰、可维护、优雅的Python脚本,提高代码质量和可维护性。以上是笔者开发过程中经常使用的一些小Tips,在这里记录分享,当然,我没还可以使用pandas,numpy等工具处理数据,提高脚本执行效率等…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/116244.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于AVR128单片机智能电风扇控制系统

一、系统方案 模拟的电风扇的工作状态有3种&#xff1a;自然风、常风及睡眠风。使用三个按键S1-S3设置自然风、常风及睡眠风。 再使用两个按键S4和S5&#xff0c;S4用于定时电风扇定时时间长短的设置&#xff0c;每按一次S4键&#xff0c;定时时间增加10秒&#xff0c;最长60秒…

iOS——present相关属性以及dismiss多级的方法

push和present 两者的区别 push: push由视图栈控制&#xff0c;每一个视图都入栈&#xff0c;调用之前的视图则需要出栈&#xff0c;可返回任意一层&#xff0c;一般用于同一业务不同界面之间的切换。 push是由UINavigationController管理的视图控制器堆栈&#xff0c;在wind…

vue项目打包优化

首先第一步通过浏览器看首次加载的问题大小&#xff0c;时间跨度等方面入手 1. Coverage观察 Coverage是chrome开发者工具的一个新功能&#xff0c;从字面意思上可以知道它是可以用来检测代码在网站运行时有哪些js和css是已经在运行&#xff0c;而哪些js和css是还没有用到的&a…

PID之Simulink仿真

昨天&#xff0c;在中南大学面试的老师&#xff0c;老师突然问到如何调PID&#xff0c;关于PID&#xff0c;我只知道一些基本概念&#xff0c;实际中并没有做过相关PID的项目&#xff0c;就连仿真也没搞过&#xff0c;所以今天就照着网上的教程做了PID的仿真&#xff0c;调PID的…

实现人工智能的去中心化,权力下放是最佳途径!

Web3和人工智能&#xff08;AI&#xff09;的交集&#xff0c;已成为加密社区中最热门的争论话题之一。毕竟&#xff0c;生成式AI正在彻底改变传统软件堆栈的所有领域&#xff0c;Web3也不例外。鉴于去中心化是Web3的核心价值主张&#xff0c;许多新兴的Web3生成AI项目和场景都…

KT142C语音芯片flash型用户如何更新固件的说明_V2

目录 一、简介 2.1 让芯片进入PC模式 2.2 双击提供的exe程序即可 一、简介 正常的情况下&#xff0c;用户肯定是不需要更新固件的&#xff0c;因为芯片出厂默认就烧录了对应的程序固件&#xff0c;但是有客户可能需要小修小改&#xff0c;或者订制一下某些功能&#xff0c…

全国职业技能大赛云计算--高职组赛题卷②(容器云)

全国职业技能大赛云计算--高职组赛题卷②&#xff08;容器云&#xff09; 第二场次题目&#xff1a;容器云平台部署与运维任务1 Docker CE及私有仓库安装任务&#xff08;5分&#xff09;任务2 基于容器的web应用系统部署任务&#xff08;15分&#xff09;任务3 基于容器的持续…

浅谈SpringMVC的请求流程

目录标题 浅谈SpringMVC的请求流程SpringMVC的介绍SpringMVC的逻辑概念运行图解知识总结 浅谈SpringMVC的请求流程 对于SpringMVC而言重点是了解它的底层运行逻辑&#xff0c;从而可以根据其逻辑来进行实际业务的操作或者是利用原理增强业务的功能性&#xff0c;最终达到项目预…

FPGA:卷积编码及维特比译码仿真

FPGA&#xff1a;卷积编码及维特比译码仿真 本篇记录一下在FPGA中完成卷积编码和维特比译码的过程&#xff0c;通过代码解释编码的过程和译码的过程&#xff0c;便于理解&#xff0c;同时也方便移植到其他工程中。 1. 准备工作 卷积编译码IP核—convolutionIP核和viterbiIP核…

以太网协议

以太网 以太网协议格式&#x1f3a8;目的地址,源地址mac地址格式 以太网协议格式&#x1f3a8; 目的地址,源地址 此处的地址,叫做mac地址(物理地址),长度是6个字节 mac地址的作用也是用来区分不同的主机 IP地址的长度是4字节 IP地址负责网络层(整体)转发,mac地址负责数据链路层…

CHATGPT中国免费网页版有哪些-CHATGPT中文版网页

CHATGPT中国免费网页版&#xff0c;一个强大的人工智能聊天机器人。如果你曾经感到困惑、寻求答案&#xff0c;或者需要一些灵感&#xff0c;那么CHATGPT国内网页版可能会成为你的好朋友。 CHATGPT国内免费网页版&#xff1a;你的多面“好朋友” 随着人工智能技术的不断发展&a…