19. 不要把函数返回的多个数值拆分到三个以上的变量中
拆包机制允许 Python 函数返回一个以上的值:
def get_stats(numbers):
minimum = min(numbers)
maximum = max(numbers)
return minimum, maximum
lengths = [63, 73, 72, 60, 67, 66, 71, 61, 72, 70]
minimum, maximum = get_stats(lengths)
print(f'Min: {minimum}, Max: {maximum}')
>>>
Min: 60, Max: 73
在返回多个值的时候,可以用带星号的表达式接收那些没有被普通变量捕获到的值。
def get_avg_ratio(numbers):
average = sum(numbers) / len(numbers)
scaled = [x / average for x in numbers]
scaled.sort(reverse=True)
return scaled
longest, *middle, shortest = get_avg_ratio(lengths)
print(f'Longest: {longest:>4.0%}')
print(f'Shortest: {shortest:>4.0%}')
>>>
Longest: 108%
Shortest: 89%
20. 遇到意外情况时应该抛出异常,不要返回 None
def careful_devide(a, b):
try:
return a / b
except ZeroDivisionError:
raise ValueError('Invalid inputs')
x, y = 5, 2
try:
result = careful_devide(x, y)
except ValueError:
print('Invalid inputs')
else:
print('Result is %.1f' % result)
>>>
Result is 2.5
21. 了解如何在闭包里面使用外围作用域中的变量
Python 支持闭包,这让定义在大函数中的小函数也能应用大函数的变量。函数在 Python 中时一等对象,所以可以像操作其他对象那样直接引用它们、把它们赋给变量、将他们当成参数传给其他函数。Python 在判断两个序列(包括元组)的大小时,首先比较 0 号位置的两个元素,如果相等,就比较 1 号位置的两个元素,如果还相等,就比较 2 号位置的两个元素,依此类推,直到得出结论为止。
Python 有一种特殊的写法,可以把闭包中的数据赋给闭包外面的变量。
def sort_priority(numbers, group):
found = False
def helper(x):
nonlocal found
if x in group:
found = True
return (0, x)
return (1, x)
numbers.sort(key=helper)
return found
22. 用数量可变的位置参数给函数设计清晰的参数列表
def log(message, *values):
if not values:
print(message)
else:
values_str = ', '.join(str(x) for x in values)
print(f'{message}: {values_str}')
log('My numbers are', 1, 2)
log('Hi there')
>>>
My numbers are: 1, 2
Hi there
favorites = [7, 33, 99]
log('Favorite colors', *favorites)
>>>
Favorite colors: 7, 33, 99
23. 用关键字参数来表示可选的行为
def remainder(number, divisor):
return number % divisor
# 以下四种效果相同
remainder(20, 7)
remainder(20, divisor=7)
remainder(number=20, divisor=7)
remainder(divisor=7, number=20)
如果位置参数和关键字参数混用,位置参数必须出现在关键字参数之前。
如果有一个字典,且 remainder 函数可以调用这个字典,那么可以把 ** 加在字典前面,这会让 Python 把字典里的键值对以关键字参数的形式传给函数。
my_kwargs = {
'number': 20,
'divisor': 7,
}
remainder(**my_kwargs)
my_kwargs = {
'divisor': 7,
}
remainder(number=20, **my_kwargs)
my_kwargs = {
'number': 20,
}
other_kwargs = {
'divisor': 7,
}
remainder(**my_kwargs, **other_kwargs)
用关键字参数调用函数可以让初次阅读代码的人更容易看懂。可以带有默认值,该值是在定义函数时指定的。可以灵活地扩充函数的参数。
24. 用 None 和 docstring 来描述默认值会变的参数
def log(message, when=None):
if when is None:
when = datatime.now()
print(f'{when}: {message}')
def decode(data, default=None):
try:
return json.loads(data)
except ValueError:
if default is None:
default = {}
return default
25. 用只能以关键字指定和只能按位置传入的参数来设计清晰的参数列表
def safe_division_e(numerator, denominator, /, ndigits=10, *, ignore_overflow=False,
ignore_zero_division=False):
try:
fraction = numerator / denominator
return round(fraction, ndigits)
except OverflowError:
if ignore_overflow:
return 0
else:
raise
except ZeroDivisionError:
if ignore_zero_division:
return float('inf')
else:
raise
在函数的参数列表中,/ 符号左侧的参数是只能按位置指定的参数,* 符号右侧的参数则是只能按关键字形式指定的参数。这两个符号之间的参数既可以按位置提供,又可以用关键字形式指定。
26. 用 functools.wraps 定义函数修饰器
这是个很有用的机制,能够确保用户以正确的方式使用函数,也能够用来调试程序或实现函数注册功能,此外还有许多用途。
from functools import wraps
def trace(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print(f'{func.__name__}({args!r}, {kwargs!r}) '
f'-> {result!r}')
return result
return wrapper
@trace
def fibonacci(n):
"""Return the n-th Fibonacci number"""
if n in (0, 1):
return n
return (fibonacci(n - 2) + fibonacci(n - 1))
help(fibonacci)
>>>
Help on function fibonacci in module __main__:
fibonacci(n)
Return the n-th Fibonacci number