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 …...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考
目录 lua脚本 记录流水 记录流水的作用 流水什么时候删除 我们在做库存扣减的时候,显示基于Lua脚本和Redis实现的预扣减 这样可以在秒杀扣减的时候保证操作的原子性和高效性 lua脚本 // ... 已有代码 ...Overridepublic InventoryResponse decrease(Inventor…...

未授权访问事件频发,我们应当如何应对?
在当下,数据已成为企业和组织的核心资产,是推动业务发展、决策制定以及创新的关键驱动力。然而,未授权访问这一隐匿的安全威胁,正如同高悬的达摩克利斯之剑,时刻威胁着数据的安全,一旦触发,便可…...
大模型真的像人一样“思考”和“理解”吗?
Yann LeCun 新研究的核心探讨:大语言模型(LLM)的“理解”和“思考”方式与人类认知的根本差异。 核心问题:大模型真的像人一样“思考”和“理解”吗? 人类的思考方式: 你的大脑是个超级整理师。面对海量信…...

Qt/C++学习系列之列表使用记录
Qt/C学习系列之列表使用记录 前言列表的初始化界面初始化设置名称获取简单设置 单元格存储总结 前言 列表的使用主要基于QTableWidget控件,同步使用QTableWidgetItem进行单元格的设置,最后可以使用QAxObject进行单元格的数据读出将数据进行存储。接下来…...