Python高质量函数编写指南
The Ultimate Guide to Writing Functions
1.视频 https://www.youtube.com/watch?v=yatgY4NpZXE
2.代码 https://github.com/ArjanCodes/2022-funcguide
Python高质量函数编写指南

1. 一次做好一件事
from dataclasses import dataclass
from datetime import datetime@dataclass
class Customer:name: strphone: strcc_number: strcc_exp_month: intcc_exp_year: intcc_valid: bool = False# validate_card函数做了太多事情
def validate_card(customer: Customer) -> bool:def digits_of(number: str) -> list[int]:return [int(d) for d in number]digits = digits_of(customer.cc_number)odd_digits = digits[-1::-2]even_digits = digits[-2::-2]checksum = 0checksum += sum(odd_digits)for digit in even_digits:checksum += sum(digits_of(str(digit * 2)))customer.cc_valid = (checksum % 10 == 0and datetime(customer.cc_exp_year, customer.cc_exp_month, 1) > datetime.now())return customer.cc_validdef main() -> None:alice = Customer(name="Alice",phone="2341",cc_number="1249190007575069",cc_exp_month=1,cc_exp_year=2024,)is_valid = validate_card(alice)print(f"Is Alice's card valid? {is_valid}")print(alice)if __name__ == "__main__":main()
我们发现validate_card函数做了两件事:验证数字和有效、验证时间有效。
我们把验证数字和拆分出来一个函数luhn_checksum, 并在validate_card中调用。
修改后:
from dataclasses import dataclass
from datetime import datetime# 验证和 函数
def luhn_checksum(card_number: str) -> bool:def digits_of(number: str) -> list[int]:return [int(d) for d in number]digits = digits_of(card_number)odd_digits = digits[-1::-2]even_digits = digits[-2::-2]checksum = 0checksum += sum(odd_digits)for digit in even_digits:checksum += sum(digits_of(str(digit * 2)))return checksum % 10 == 0@dataclass
class Customer:name: strphone: str...def validate_card(customer: Customer) -> bool:customer.cc_valid = (luhn_checksum(customer.cc_number)and datetime(customer.cc_exp_year, customer.cc_exp_month, 1) > datetime.now())return customer.cc_valid
2. 分离命令和查询(command and query)
validate_card 中同时进行了查询和赋值两个操作,这样不好。
我们将查询和赋值拆分成两个步骤。
validate_card只返回卡是否有效,而赋值操作alice.cc_valid = validate_card(alice) 移动到了主函数中。
from dataclasses import dataclass
from datetime import datetimedef luhn_checksum(card_number: str) -> bool:def digits_of(number: str) -> list[int]:return [int(d) for d in number]digits = digits_of(card_number)odd_digits = digits[-1::-2]even_digits = digits[-2::-2]checksum = 0checksum += sum(odd_digits)for digit in even_digits:checksum += sum(digits_of(str(digit * 2)))return checksum % 10 == 0@dataclass
class Customer:name: strphone: strcc_number: strcc_exp_month: intcc_exp_year: intcc_valid: bool = False# 查询
def validate_card(customer: Customer) -> bool:return (luhn_checksum(customer.cc_number)and datetime(customer.cc_exp_year, customer.cc_exp_month, 1) > datetime.now())def main() -> None:alice = Customer(name="Alice",phone="2341",cc_number="1249190007575069",cc_exp_month=1,cc_exp_year=2024,)# 赋值alice.cc_valid = validate_card(alice) print(f"Is Alice's card valid? {alice.cc_valid}")print(alice)if __name__ == "__main__":main()
3. 只请求你需要的
函数validate_card实际上只需要3个参数(而不需要整个Customer对象)。
因此只请求3个参数:def validate_card(*, number: str, exp_month: int, exp_year: int) -> bool:
``
from dataclasses import dataclass
from datetime import datetimedef luhn_checksum(card_number: str) -> bool:def digits_of(number: str) -> list[int]:return [int(d) for d in number]digits = digits_of(card_number)odd_digits = digits[-1::-2]even_digits = digits[-2::-2]checksum = 0checksum += sum(odd_digits)for digit in even_digits:checksum += sum(digits_of(str(digit * 2)))return checksum % 10 == 0@dataclass
class Customer:name: strphone: strcc_number: strcc_exp_month: intcc_exp_year: intcc_valid: bool = False# 只请求你需要的参数
def validate_card(*, number: str, exp_month: int, exp_year: int) -> bool:return luhn_checksum(number) and datetime(exp_year, exp_month, 1) > datetime.now()def main() -> None:alice = Customer(name="Alice",phone="2341",cc_number="1249190007575069",cc_exp_month=1,cc_exp_year=2024,)alice.cc_valid = validate_card(number=alice.cc_number,exp_month=alice.cc_exp_month,exp_year=alice.cc_exp_year,)print(f"Is Alice's card valid? {alice.cc_valid}")print(alice)if __name__ == "__main__":main()
4. 保持最小参数量
参数量很多时,调用时传参会比较麻烦。另一方面,函数需要很多参数,则暗示该函数可能做了很多事情。
下面我们抽象出Card 类, 减少了Customer和validae_card的参数量。
from dataclasses import dataclass
from datetime import datetime
from typing import Protocoldef luhn_checksum(card_number: str) -> bool:def digits_of(number: str) -> list[int]:return [int(d) for d in number]digits = digits_of(card_number)odd_digits = digits[-1::-2]even_digits = digits[-2::-2]checksum = 0checksum += sum(odd_digits)for digit in even_digits:checksum += sum(digits_of(str(digit * 2)))return checksum % 10 == 0@dataclass
class Card:number: strexp_month: intexp_year: intvalid: bool = False@dataclass
class Customer:name: strphone: strcard: Cardcard_valid: bool = Falseclass CardInfo(Protocol):@propertydef number(self) -> str:...@propertydef exp_month(self) -> int:...@propertydef exp_year(self) -> int:...def validate_card(card: CardInfo) -> bool:return (luhn_checksum(card.number)and datetime(card.exp_year, card.exp_month, 1) > datetime.now())def main() -> None:card = Card(number="1249190007575069", exp_month=1, exp_year=2024)alice = Customer(name="Alice", phone="2341", card=card) # 现在传入card,而不是3个参数card.valid = validate_card(card) # 传入cardprint(f"Is Alice's card valid? {card.valid}")print(alice)if __name__ == "__main__":main()
5. 不要在同一个地方创建并使用对象
不要再函数内创建对象并使用,更好的方式是在外面创建对象并作为参数传递给函数。
import loggingclass StripePaymentHandler:def handle_payment(self, amount: int) -> None:logging.info(f"Charging ${amount/100:.2f} using Stripe")PRICES = {"burger": 10_00,"fries": 5_00,"drink": 2_00,"salad": 15_00,
}# !!
def order_food(items: list[str]) -> None:total = sum(PRICES[item] for item in items)logging.info(f"Order total is ${total/100:.2f}.")payment_handler = StripePaymentHandler() # ... 创建对象payment_handler.handle_payment(total) # 使用对象logging.info("Order completed.")def main() -> None:logging.basicConfig(level=logging.INFO)order_food(["burger", "fries", "drink"])if __name__ == "__main__":main()
修改后:
import logging
from typing import Protocolclass StripePaymentHandler:def handle_payment(self, amount: int) -> None:logging.info(f"Charging ${amount/100:.2f} using Stripe")PRICES = {"burger": 10_00,"fries": 5_00,"drink": 2_00,"salad": 15_00,
}class PaymentHandler(Protocol):def handle_payment(self, amount: int) -> None:...# !! 现在通过参数传入对象
def order_food(items: list[str], payment_handler: PaymentHandler) -> None:total = sum(PRICES[item] for item in items)logging.info(f"Order total is ${total/100:.2f}.")payment_handler.handle_payment(total) # logging.info("Order completed.")def main() -> None:logging.basicConfig(level=logging.INFO)order_food(["burger", "salad", "drink"], StripePaymentHandler())if __name__ == "__main__":main()
6. 不要用flag参数
flag参数意味着函数处理两种情况,函数会变得复杂。建议将两者情况拆分成单独的函数。
from dataclasses import dataclass
from enum import StrEnum, autoFIXED_VACATION_DAYS_PAYOUT = 5class Role(StrEnum):PRESIDENT = auto()VICEPRESIDENT = auto()MANAGER = auto()LEAD = auto()ENGINEER = auto()INTERN = auto()@dataclass
class Employee:name: strrole: Rolevacation_days: int = 25def take_a_holiday(self, payout: bool, nr_days: int = 1) -> None:if payout:if self.vacation_days < FIXED_VACATION_DAYS_PAYOUT:raise ValueError(f"You don't have enough holidays left over for a payout.\Remaining holidays: {self.vacation_days}.")self.vacation_days -= FIXED_VACATION_DAYS_PAYOUTprint(f"Paying out a holiday. Holidays left: {self.vacation_days}")else:if self.vacation_days < nr_days:raise ValueError("You don't have any holidays left. Now back to work, you!")self.vacation_days -= nr_daysprint("Have fun on your holiday. Don't forget to check your emails!")def main() -> None:employee = Employee(name="John Doe", role=Role.ENGINEER)employee.take_a_holiday(True)if __name__ == "__main__":main()
修改后:
from dataclasses import dataclass
from enum import StrEnum, autoFIXED_VACATION_DAYS_PAYOUT = 5class Role(StrEnum):PRESIDENT = auto()VICEPRESIDENT = auto()MANAGER = auto()LEAD = auto()ENGINEER = auto()INTERN = auto()@dataclass
class Employee:name: strrole: Rolevacation_days: int = 25def payout_holiday(self) -> None:if self.vacation_days < FIXED_VACATION_DAYS_PAYOUT:raise ValueError(f"You don't have enough holidays left over for a payout.\Remaining holidays: {self.vacation_days}.")self.vacation_days -= FIXED_VACATION_DAYS_PAYOUTprint(f"Paying out a holiday. Holidays left: {self.vacation_days}")def take_holiday(self, nr_days: int = 1) -> None:if self.vacation_days < nr_days:raise ValueError("You don't have any holidays left. Now back to work, you!")self.vacation_days -= nr_daysprint("Have fun on your holiday. Don't forget to check your emails!")def main() -> None:employee = Employee(name="John Doe", role=Role.ENGINEER)employee.payout_holiday()if __name__ == "__main__":main()
7. 函数也是对象
函数也是对象,因此可以作为参数传递,作为函数返回值。
import logging
from functools import partial
from typing import Callabledef handle_payment_stripe(amount: int) -> None:logging.info(f"Charging ${amount/100:.2f} using Stripe")PRICES = {"burger": 10_00,"fries": 5_00,"drink": 2_00,"salad": 15_00,
}HandlePaymentFn = Callable[[int], None]# 函数作为参数
def order_food(items: list[str], payment_handler: HandlePaymentFn) -> None:total = sum(PRICES[item] for item in items)logging.info(f"Order total is ${total/100:.2f}.")payment_handler(total)logging.info("Order completed.")order_food_stripe = partial(order_food, payment_handler=handle_payment_stripe)def main() -> None:logging.basicConfig(level=logging.INFO)# order_food(["burger", "salad", "drink"], handle_payment_stripe)order_food_stripe(["burger", "salad", "drink"])if __name__ == "__main__":main()
相关文章:
Python高质量函数编写指南
The Ultimate Guide to Writing Functions 1.视频 https://www.youtube.com/watch?vyatgY4NpZXE 2.代码 https://github.com/ArjanCodes/2022-funcguide Python高质量函数编写指南 1. 一次做好一件事 from dataclasses import dataclass from datetime import datetimedatacl…...
探索Spring、Spring Boot和Spring Cloud的奇妙关系(二)
本系列文章简介: 在当今快节奏、高竞争的软件开发世界中,构建可靠、高效的应用程序是至关重要的。而Spring框架一直以来都是业界领先的Java开发框架之一,帮助开发者简化了复杂的任务,并提供了丰富的功能和强大的支持。 然而&#…...
Mysql的事务隔离级别以及事务的四大特性。
MySQL 的事务隔离级别是数据库管理系统中的一个重要概念,它决定了事务如何隔离和影响其他并发事务。MySQL 支持四种事务隔离级别,分别是:读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)…...
人工智能_大模型023_AssistantsAPI_01_OpenAI助手的创建_API的调用_生命周期管理_对话服务创建---人工智能工作笔记0159
先来说一下一些问题: 尽量不要微调,很麻烦,而且效果需要自己不断的去测试. 如果文档中有图表,大量的图片去分析就不合适了. 是否用RAG搜索,这个可以这样来弄,首先去es库去搜能直接找到答案可以就不用去RAG检索了,也可以设置一个分,如果低于60分,那么就可以去进行RAG检索 微…...
锁策略总结
锁策略 悲观锁和乐观锁 乐观锁和悲观锁不是具体类型的锁而是指两种不同的对待加锁的态度,这两个锁面对锁冲突的态度是相反的。 乐观锁:认为不存在很多的并发操作,因此不需要加锁。悲观锁:认为存在很多并发操作,因此需…...
蓝桥杯备考day2
1.1 map及其函数 map 提供一对一的数据处理能力,由于这个特性,它完成有可 能在我们处理一对一数据的时候,在编程上提供快速通道。map 中的第一 个值称为关键字(key),每个关键字只能在 map 中出现一次,第二个称为该 关…...
Mac电脑安装蚁剑
1: github 下载源码和加载器:https://github.com/AntSwordProjectAntSwordProject GitHubAntSwordProject has 12 repositories available. Follow their code on GitHub.https://github.com/AntSwordProject 以该图为主页面:antSword为源码…...
品牌百度百科词条创建多少钱?
百度百科作为国内最具权威和影响力的知识型平台,吸引了无数品牌和企业争相入驻。一个品牌的百度百科词条,不仅是对品牌形象的一种提升,更是增加品牌曝光度、提高品牌知名度的重要途径。品牌百度百科词条创建多少钱,这成为了许多企…...
Linux安装及管理程序
目录 一.Linux应用程序基础 1.应用程序与系统命令的关系 2.典型应用程序的目录结构 3.常见的Linux软件包封装类型 二.RPM 软件包管理工具 1.RPM 软件包管理器 Red-Hat Package Manger 2.RPM软件包 3.RPM命令 三.源代码编译安装 1. yum 软件包管理器: 配…...
Mybatis generate xml 没有被覆盖
添加插件即可 <plugin type"org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>...
MercadoLibre(美客多)入仓预约系统操作流程-自动化约号(开篇)
目录 一、添加货件信息 二、输入货件信息 三、选择发货 四、填写交货日期 五、注意事项 MercadoLibre(美客多)于2021年10月18号上线了新预约入仓系统,在MercadoLibre美客多平台上,新入仓预约系统是一项非常重要的功能&#x…...
Unity TextMeshProUGUI 获取文本尺寸·大小
一般使用ContentSizeFitter组件自动变更大小 API 渲染前 Vector2 GetPreferredValues(string text)Vector2 GetPreferredValues(string text, float width, float height)Vector2 GetPreferredValues(float width, float height) 渲染后 Vector2 GetRenderedValues()Vector…...
Sonar下启动发生错误,elasticsearch启动错误
Download | SonarQube | Sonar (sonarsource.com) 1.首先我的sonar版本为 10.4.1 ,java版本为17 2.sonar启动需要数据库,我先安装了mysql, 但是目前sonar从7.9开始不支持mysql,且java版本要最少11,推荐使用java17 3.安装postsql,创建sonar数据库 4.启…...
Git常用命令以及异常信息汇总
常用命令: 查看本地分支: git branch 创建一个新仓库 git clone 仓库地址xxxxx cd 目标目录 git switch -c main touch README.md git add README.md git commit -m "add README" git push -u origin main 推送现有文件夹 cd 目标目录 git in…...
解释Python中的RESTful API设计和实现
解释Python中的RESTful API设计和实现 RESTful API,即符合REST(Representational State Transfer,表述性状态转移)架构风格的Web服务接口,已成为现代Web应用程序通信的标准。Python作为一种灵活且强大的编程语言&…...
一、Nginx部署
Nginx部署 一、Docker部署1.复制Nginx配置文件2.启动Nginx容器 一、Docker部署 1.复制Nginx配置文件 # 1.拉取镜像 docker pull nginx # 2.启动nginx容器 docker run --restartalways --namenginx -p 80:80 -d nginx # 3.宿主机创建挂载目录 mkdir /root/docker/nginx -p # 4…...
C语言基础---指针的基本语法
概述 内存地址 在计算机内存中,每个存储单元都有一个唯一的地址(内存编号)。通俗理解,内存就是房间,地址就是门牌号 指针和指针变量 指针(Pointer)是一种特殊的变量类型,它用于存储内存地址。指针的实…...
记录--病理切片图像处理
简介 数字病理切片,也称为全幻灯片成像(Whole Slide Imaging,WSI)或数字切片扫描,是将传统的玻片病理切片通过高分辨率扫描仪转换为数字图像的技术。这种技术对病理学领域具有革命性的意义,因为它允许病理…...
Android使用shape属性绘制边框内渐变色
目录 先上效果图实现方法shape属性介绍代码结果 先上效果图 这是使用AndroidStudio绘制的带有渐变色的边框背景色 实现方法 项目中由于UI设计需求,需要给按钮、控件设置带有背景色效果的。以下是UI效果图。 这里我们使用shape属性来绘制背景效果。 shape属性介…...
分类算法(数据挖掘)
目录 1. 逻辑回归(Logistic Regression) 2. 支持向量机(Support Vector Machine, SVM) 3. 决策树(Decision Tree) 4. 随机森林(Random Forest) 5. K近邻(K-Nearest …...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...
VSCode 使用CMake 构建 Qt 5 窗口程序
首先,目录结构如下图: 运行效果: cmake -B build cmake --build build 运行: windeployqt.exe F:\testQt5\build\Debug\app.exe main.cpp #include "mainwindow.h"#include <QAppli...
