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 …...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
JS红宝书笔记 - 3.3 变量
要定义变量,可以使用var操作符,后跟变量名 ES实现变量初始化,因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符,可以创建一个全局变量 如果需要定义…...
