# 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: 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()


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. 只请求你需要的



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. 保持最小参数量

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()


|
1天前
|
Python
Python中使用函数参数
【7月更文挑战第23天】
10 2
|
10天前
|

【7月更文挑战第15天】Python的协程与异步函数优化Web性能，通过非阻塞I/O提升并发处理能力。使用aiohttp库构建异步服务器，示例代码展示如何处理GET请求。异步处理减少资源消耗，提高响应速度和吞吐量，适用于高并发场景。掌握这项技术对提升Web应用性能至关重要。
36 10
|
6天前
|

python中那些双下划线开头得函数和变量
python中那些双下划线开头得函数和变量
13 2
|
8天前
|
Python
Python函数：函数的定义和调用

15 4
|
10天前
|

【7月更文挑战第15天】Python 3.5+引入的协程和异步函数革新了并发编程。协程，轻量级线程，由程序控制切换，降低开销。异步函数是协程的高级形式，允许等待异步操作。通过asyncio库，如示例所示，能并发执行任务，提高I/O密集型任务效率，实现并发而非并行，优化CPU利用率。理解和掌握这些工具对于构建高效网络应用至关重要。
22 6
|
10天前
|

【7月更文挑战第15天】在大数据时代，Python的协程和异步函数解决了同步编程的性能瓶颈问题。同步编程在处理I/O密集型任务时效率低下，而Python的asyncio库支持的异步编程利用协程实现并发，通过async def和await避免了不必要的等待，提升了CPU利用率。例如，从多个API获取数据，异步方式使用aiohttp并发请求，显著提高了效率。掌握异步编程对于高效处理大规模数据至关重要。
20 4
|
10天前
|

【7月更文挑战第15天】Python异步编程借助协程和async/await提升并发性能，减少资源消耗。协程（async def）轻量级、用户态，便于控制。事件循环，如asyncio.get_event_loop()，调度任务执行。异步函数内的await关键词用于协程间切换。回调和Future对象简化异步结果处理。理解这些概念能写出高效、易维护的异步代码。
16 2
|
10天前
|
Python

【7月更文挑战第15天】Python异步编程提升效率，通过协程与异步函数实现并发。从async def定义异步函数，如say_hello()，使用await等待异步操作。asyncio.run()驱动事件循环。并发执行任务，如asyncio.gather()同时处理fetch_data()任务，降低总体耗时。入门异步编程，解锁高效代码。
17 1
|
13天前
|

python操作符或函数与数据类型不兼容
【7月更文挑战第11天】
17 1
|
16天前
|

Python函数参数定义中的这两个分隔符，还有人不知道吗？
python 函数的参数定义想必大家应该是非常熟悉的，有两种： • 位置参数（positional argument）：根据函数在参数列表中的位置传递给函数的参数。 • 关键词参数（keyword argument）：通过指定参数名称及其对应值传参的参数。
18 1