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 类, 减少了Customervalidae_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()

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

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

相关文章

latex论文写作学习

首先无论是什么文章&#xff0c;最基础的格式都是这个&#xff1a; 如何修改字体呢&#xff1f;&#xff1a; 这样一来就可以在写完之后统一改掉了&#xff0c;用来做标记 最后记得\par 插图&#xff1a; 命令后面的中括号一般就是option

uniapp微信小程序分包

一、创建分包文件夹subPack 二、将页面文件放入分包文件夹中 启动页面和导航tabBar页面不要放入分包文件夹中 三、配置pages.json 四、效果

腾讯EdgeOne产品测评体验—腾讯云Edgeone为我的网站保驾护航

前言 众所周知&#xff0c;网站如果没有安全防护&#xff0c;极易遭受恶意攻击&#xff0c;导致运行受阻甚至瘫痪。为保障网站的安全稳定运行&#xff0c;必须采取诸如CDN加速与DDoS防护等措施&#xff0c;为用户提供安全、可靠的服务。 本文将基于腾讯云边缘安全加速平台&am…

CGKRK-PEG-DSPE 细胞穿透肽CGKRK修饰脂质体纳米粒

CGKRK-PEG-DSPE 细胞穿透肽CGKRK修饰脂质体纳米粒 【中文名称】细胞穿透肽CGKRK-聚乙二醇-磷脂 【英文名称】DSPE-PEG-CGKRK 【结 构】 【品 牌】碳水科技&#xff08;Tanshtech&#xff09; 【纯 度】95%以上 【保 存】-20 【规 格】50mg,100mg,500mg,1g …

RocketMQ 09 SpringBoot 整合

RocketMQ 09 SpringBoot 整合 目前还没有官方的starter pom.xml <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-common</artifactId><version>4.6.1</version></dependency><!-- https://mvn…

小红书2020校招算法笔试题卷一 编程题no.2 笔记精选

题目 薯队长写了n篇笔记&#xff0c;编号从1~n,每篇笔记都获得了不少点赞数。 薯队长想从中选出一些笔记&#xff0c;作一个精选集合。挑选的时候有两个规则&#xff1a; 1.不能出现连续编号的笔记。 2.总点赞总数最多 如果满足1&#xff0c;2条件有多种方案&#xff0c;…

视频怎么用二维码来分享?手机扫码看视频更方便

怎么把制作的视频快速以二维码的形式分享给其他人呢&#xff1f;视频是很多内容重要的一种展示方式&#xff0c;为了能够让其他人更加方便的查看视频内容&#xff0c;现在很多人会选择视频转二维码的方式来提供预览。将视频存储到云端然后通过扫码来调取&#xff0c;不仅减少了…

DHCP小实验

实验要求&#xff1a; 看拓扑有两个网段则我们首先需要对200.1.1.0/26进行子网划分&#xff0c;划分为两个子网&#xff0c;为200.1.1.0/27和200.1.1.32/27 我门就可以一边一个网段了&#xff0c;左边为200.1.1.0/27&#xff0c;右边为200.1.1.32/27 1、配置PC1&#xff0c;2…

2024蓝桥杯——宝石问题

先展示题目 声明 以下代码仅是我的个人看法&#xff0c;在自己考试过程中的优化版&#xff0c;本人考试就踩了很多坑&#xff0c;我会—一列举出来。代码可能很多&#xff0c;但是总体时间复杂度不高只有0(N) 函数里面的动态数组我没有写开辟判断和free&#xff0c;这里我忽略…

LinuxShell编程中source和export命令

目录 1.source命令1.1.POSIX模式1.1.1.验证POSIX模式执行情况 1.2.source命令表示形式的历史由来1.3.source命令解读1.3.1.在当前的shell环境中1.3.2.source命令的常用用途1.3.3.从文件名中读取并执行命令 2.export命令2.1.显示当前终端已经导出的函数和环境变量2.2.验证变量和…

Python异常处理机制详解及示例

Python异常处理机制详解及示例 在编程过程中&#xff0c;异常处理是一项至关重要的技能。Python作为一种功能强大的编程语言&#xff0c;提供了一套完善的异常处理机制&#xff0c;使得程序在遇到错误或异常情况时能够优雅地处理&#xff0c;而不是直接崩溃。本文将详细介绍Py…

MySQL——基础

SQL 全称 Structured Query Language&#xff0c;结构化查询语言。操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一标准 。 SQL 通用语法 SQL语句可以单行或多行书写&#xff0c;以分号结尾。SQL语句可以使用空格/缩进来增强语句的可读性。MySQL数据库…

一文速览铁威马TOS 6全新“文件管理”

TOS 6 Beta已经上线一段时间了&#xff0c;各位铁粉用着怎么样呢&#xff1f;今天就和大家分享&#xff0c;TOS 6全新文件管理。 为了向用户提供更流畅、更便捷的文件管理体验&#xff0c;铁威马的研发团队积极借鉴了Windows OS和Mac OS在文件管理方面的优点&#xff0c;投入巨…

【LeetCode刷题记录】41.缺失的第一个正数

41 缺失的第一个正数 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xff1a;范围 [1…

设置coredump存储路径

设置coredump存储路径需要配置/proc/sys/kernel/core_pattern中的路径&#xff0c;一般情况下如下操作&#xff1a; echo /home/core-%e-%p >/proc/sys/kernel/core_pattern 但是这种方法在有的系统中会设置失败。例如ubuntu中。 因为ubuntu中默认开启了系统错误诊断&#x…

Redis实现持久化和集群的搭建

一、Redis的持久化方案 1&#xff09;RDB持久化 RDB&#xff08;Redis Database Backup file&#xff09;&#xff0c;Redsi数据备份文件或Redis数据快照。 把内存中的所有快照文件称为RDB文件&#xff0c;默认是保存在当前运行目录。 RDB持久化在四种情况下会执行&#xff…

[gvim] 操作

1. 删除操作 behaviorcode删除高亮内容:%s//g删除高亮内容所在行:g//d只删除匹配内容:%s/<pattern>//删除匹配内容所在的行:g/<pattern>/d只保留匹配内容的行:v/<pattern>/d 2. 替换操作 behaviorcode全局替换:%s/src_word/tgt_word/g替换n-m行关键字:n,m…

【echarts】echarts入门教程,学会如何编写echarts代码

echarts模板 使用&#xff01;为html来创建一个模板。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…

Open3D (C++) 点云投影至主成分空间

目录 一、算法原理二、代码实现三、结果展示四、相关连接Open3D (C++) 点云投影至主成分空间由CSDN点云侠原创,爬虫自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 p r o j

windeployqt工具

windeployqt是Qt SDK提供的一个实用工具&#xff0c;专为Windows平台设计&#xff0c;用于自动部署Qt应用程序所需的所有依赖库和组件。当你构建了一个Qt应用程序并希望在没有安装Qt开发环境的计算机上运行时&#xff0c;windeployqt工具能够帮你复制和收集应用程序运行所需的动…