Python高质量函数编写指南

news2025/2/7 17:54:18

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: str
    phone: str
    cc_number: str
    cc_exp_month: int
    cc_exp_year: int
    cc_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 = 0
    checksum += sum(odd_digits)
    for digit in even_digits:
        checksum += sum(digits_of(str(digit * 2)))

    customer.cc_valid = (
        checksum % 10 == 0
        and datetime(customer.cc_exp_year, customer.cc_exp_month, 1) > datetime.now()
    )
    return customer.cc_valid


def 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 = 0
    checksum += sum(odd_digits)
    for digit in even_digits:
        checksum += sum(digits_of(str(digit * 2)))
    return checksum % 10 == 0


@dataclass
class Customer:
    name: str
    phone: 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 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 = 0
    checksum += sum(odd_digits)
    for digit in even_digits:
        checksum += sum(digits_of(str(digit * 2)))
    return checksum % 10 == 0


@dataclass
class Customer:
    name: str
    phone: str
    cc_number: str
    cc_exp_month: int
    cc_exp_year: int
    cc_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 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 = 0
    checksum += sum(odd_digits)
    for digit in even_digits:
        checksum += sum(digits_of(str(digit * 2)))
    return checksum % 10 == 0


@dataclass
class Customer:
    name: str
    phone: str
    cc_number: str
    cc_exp_month: int
    cc_exp_year: int
    cc_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 Protocol


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 = 0
    checksum += sum(odd_digits)
    for digit in even_digits:
        checksum += sum(digits_of(str(digit * 2)))
    return checksum % 10 == 0


@dataclass
class Card:
    number: str
    exp_month: int
    exp_year: int
    valid: bool = False


@dataclass
class Customer:
    name: str
    phone: str
    card: Card
    card_valid: bool = False


class CardInfo(Protocol):
    @property
    def number(self) -> str:
        ...

    @property
    def exp_month(self) -> int:
        ...

    @property
    def 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) # 传入card
    print(f"Is Alice's card valid? {card.valid}")
    print(alice)


if __name__ == "__main__":
    main()

5. 不要在同一个地方创建并使用对象

不要再函数内创建对象并使用,更好的方式是在外面创建对象并作为参数传递给函数。

import logging


class 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 Protocol


class 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, auto

FIXED_VACATION_DAYS_PAYOUT = 5


class Role(StrEnum):
    PRESIDENT = auto()
    VICEPRESIDENT = auto()
    MANAGER = auto()
    LEAD = auto()
    ENGINEER = auto()
    INTERN = auto()


@dataclass
class Employee:
    name: str
    role: Role
    vacation_days: int = 25

    def 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_PAYOUT
            print(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_days
            print("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, auto

FIXED_VACATION_DAYS_PAYOUT = 5


class Role(StrEnum):
    PRESIDENT = auto()
    VICEPRESIDENT = auto()
    MANAGER = auto()
    LEAD = auto()
    ENGINEER = auto()
    INTERN = auto()


@dataclass
class Employee:
    name: str
    role: Role
    vacation_days: int = 25

    def 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_PAYOUT
        print(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_days
        print("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 Callable


def 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.coloradmin.cn/o/1598325.html

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

相关文章

如何解决selenium无头浏览器访问页面失败问题!!

无头浏览器简介 无头浏览器&#xff08;Headless browser&#xff09;是一种没有图形用户界面&#xff08;GUI&#xff09;的网络浏览器。它可以在后台运行&#xff0c;并通过编程接口进行控制和操作&#xff0c;而不需要显示界面。通常&#xff0c;传统的浏览器如 Chrome、Fi…

生产控制台厂家的技术要求深度解读

随着科技的不断进步和工业的快速发展&#xff0c;生产控制台在现代化生产线中的作用日益凸显。生产控制台作为生产线的“大脑”&#xff0c;要求厂家不仅具备高超的制造技术&#xff0c;还需对技术要求有深入的理解和掌握。本文将对生产控制台厂家的技术要求进行浅析。 生产控制…

vmware安装ubuntu-18.04系统

一、软件下载 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1fK2kygRdSux1Sr1sOKOtJQ 提取码&#xff1a;twsb 二、安装ubuntu系统 1、把ubuntu-18.04的压缩包下载下来&#xff0c;并且解压 2、打开vmware软件&#xff0c;点击文件-打开 3、选择我们刚刚解…

4.15 网络编程

思维导图 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> #include <semaphore.h> #inclu…

白盒测试之分支-条件覆盖

白盒测试之分支-条件覆盖&#xff08;蓝桥课学习笔记&#xff09; 实验介绍 分支&#xff08;判定&#xff09;覆盖是设计一定量的测试用例使程序中的每个判断语句的真假分支都得到覆盖&#xff0c;但是分支覆盖不能保证判断语句中每个条件的真、假分支都得到覆盖。那么&…

Linux的学习之路:5、粘滞位与vim

摘要 这里主要是把上章没说完的权限的粘滞位说一下&#xff0c;然后就是vim的一些操作。 目录 摘要 一、粘滞位 二、权限总结 三、vim的基本概念 四、vim的基本操作 五、vim正常模式命令集 1、插入模式 2、从插入模式切换为命令模式 3、移动光标 4、删除文字 5、复…

详解UART通信协议以及FPGA实现

文章目录 一、UART概述二、UART协议帧格式2.1 波特率2.2 奇校验ODD2.3 偶校验EVEN 三、UART接收器设计3.1 接收时序图3.2 Verilog代码3.3 仿真文件测试3.4 仿真结果3.5 上版测试 四、UART发送器设计4.1 发送时序图4.2 Verilog代码4.3 仿真文件测试4.4 仿真结果4.5 上板测试 五、…

温湿度传感器(DHT11)以及光照强度传感器(BH1750)的使用

前言 对于一些单片机类的环境检测或者智能家居小项目中&#xff0c;温湿度传感器&#xff08;DHT11&#xff09;以及光照强度传感器&#xff08;BH1750&#xff09;往往是必不可少的两个外设&#xff0c;下面我们来剖析这两个外设的原理&#xff0c;以及使用。 1. 温湿度传感…

仿真服务器介绍及应用

仿真服务器是一种高性能的计算设备&#xff0c;专门用于运行复杂的仿真软件和处理大量的计算任务。 仿真服务器通常具备以下特点&#xff1a; 1. 高性能硬件配置&#xff1a;为了满足仿真软件对计算能力的要求&#xff0c;仿真服务器通常配备高性能的CPU、大量的内存以及高速的…

Ollama教程——使用langchain:ollama与langchain的强强联合

相关文章: Ollama教程——入门&#xff1a;开启本地大型语言模型开发之旅 Ollama教程——模型&#xff1a;如何将模型高效导入到ollama框架 Ollama教程——兼容OpenAI API&#xff1a;高效利用兼容OpenAI的API进行AI项目开发 Ollama教程——使用langchain&#xff1a;ollama与…

【Windows】SecureCRT v9.2.3永久使用教程亲测可用

​ 软件介绍 SecureCRT 是一款常用的 SSH&#xff08;安全外壳协议&#xff09;和 Telnet 客户端软件&#xff0c;主要用于远程服务器管理和网络设备配置。它具有以下主要功能&#xff1a; 1.安全连接&#xff1a;SecureCRT 提供了强大的加密算法&#xff0c;保障用户与服务…

线性表概念及顺序表的实现

文章目录 前言一、线性表1.定义2.特点3.一般线性表的抽象数据类型定义 二、线性表的顺序存储&#xff08;顺序表&#xff09;1.基本概念2.数组实现顺序表3.顺序表中基本操作的具体实现4.顺序表总结 总结 前言 T_T此专栏用于记录数据结构及算法的&#xff08;痛苦&#xff09;学…

计算机虚拟机服务器中了mallox勒索病毒怎么办Mallox勒索病毒解密流程工具

在当今社会&#xff0c;人们的工作生活离不开网络&#xff0c;尤其企业离不开网络办公&#xff0c;网络为企业提供了极大便利&#xff0c;大大提升了企业的生产效率与办公水平&#xff0c;但网络是一把双刃剑&#xff0c;在为企业提供便利的同时也为企业的数据带来严重威胁。近…

第10章 物理安全要求

10.1 站点与设施设计的安全原则 假如没有对物理环境的控制&#xff0c;任何管理的、技术的或逻辑的访问控制技术都无法提供足够的安全性。 如果怀有恶意的人员获取了对设施及设备的物理访问权&#xff0c;那么他们几乎可以为所欲为&#xff0c;包括肆意破坏或窃取、更改数据。…

管理和维护索引

本文接上一篇创建、更改、删除索引继续讨论如何管理和维护SinoDB数据库的B树索引。 1. 索引的好处 ① 基于索引的排序 一列或多列的索引可用于按序取回数据。使用索引读取数据可以让数据库服务器按照请求的顺序直接返回数据&#xff0c;免去了排序的操作。 ② 强制保证唯一性…

使用matlab GUI设计离线问卷

很久没有更新了。没有更新的日子&#xff0c;也在努力学习&#xff0c;坚定了继续搞科研的决心&#xff0c;至死不渝。希望我们每个人都能有远大的理想&#xff0c;并为之付出不懈的奋斗。 目录 一、为什么 二、怎么设计 1. 问题和答案的显示 2. 答案的记录和保存 三、代…

V神演讲展望Web3发展并认为可引入人工智能

前言 2024 香港 Web3 嘉年华期间&#xff0c;以太坊联合创始人 Vitalik Buterin 发表主旨演讲《Reaching the Limits of Protocol Design》。以下是演讲内容&#xff1a; 区块链与ZK-SNARKS 我们用来构建协议的技术类型在过去 10 年里发生了很大变化。那么&#xff0c;当 2…

德国高速主轴SycoTec代理 高精度高转速主轴一览

速科德电机科技&#xff0c;作为德国高速主轴品牌SycoTec代理以及亚太服务中心&#xff0c;一直致力于将最先进、最可靠的高速电主轴技术带给广大机械加工企业。在现代精密机械制造业中&#xff0c;高速电主轴的应用已经变得不可或缺&#xff0c;而SycoTec高速电主轴以其卓越的…

【七 (1)FineBI FCP模拟试卷-股票收盘价分析】

目录 文章导航一、字段解释二、需求三、操作步骤1、添加计算字段&#xff08;每月最后一天的收盘价&#xff09;2、绘制折线图 文章导航 【一 简明数据分析进阶路径介绍&#xff08;文章导航&#xff09;】 一、字段解释 Company Name&#xff1a;公司名称 Date&#xff1a;…

UE5 C++ 创建3DWidgete 血条 再造成伤害

一&#xff0e;创建 二&#xff0e;&#xff35;&#xff29;里声明变量 创建类 public:UPROPERTY(EditAnywhere,BlueprintReadWrite,Category "MyWidget")float CurrentHealth 100.0f;UPROPERTY(EditAnywhere,BlueprintReadWrite,Category "MyWidget"…