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 …...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
webpack面试题
面试题:webpack介绍和简单使用 一、webpack(模块化打包工具)1. webpack是把项目当作一个整体,通过给定的一个主文件,webpack将从这个主文件开始找到你项目当中的所有依赖文件,使用loaders来处理它们&#x…...
【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法
使用 ROS1-Noetic 和 mavros v1.20.1, 携带经纬度海拔的话题主要有三个: /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码,来分析他们的发布过程。发现前两个话题都对应了同一…...
