在开发过程,我们开发的API如果需要修改,这个修改会影响已经在使用API的开发者,那么他们需要修改调用这个API的方式。当API修改后,我们要怎么通知他们那?
我们可以使用python内置的warnings模块来通知开发者API已修改,需要尽快修改代码。
下面我们来开发一个模块,计算一辆汽车在一定的平均速度和一段时长下,能够运行的距离。
speed默认的单位是英里/小时,时长的单位是小时。
#根据传入的速度和时长计算能够运行多远的距离
def print_distance(speed, duration):
distance = speed * duration
print(f'{distance} miles')
print_distance(5, 2.5)
# 调用这个功能计算运行距离
print_distance(1000, 3)
上面是第一个版本,现在要为这个模块的速度和时长增加不同的单位,让他计算的功能得到扩展。我们可以让调用者通过可选的关键字参数指定速度和时长的单位。
# 将单位对应的换算公式定义到字典常量
CONVERSIONS = {
'mph': 1.60934 / 3600 * 1000, # m/s
'hours': 3600, # seconds
'miles': 1.60934 * 1000, # m
'meters': 1, # m
'm/s': 1, # m
'seconds': 1, # s
}
def convert(value, units):
rate = CONVERSIONS[units]
return rate * value
def localize(value, units):
rate = CONVERSIONS[units]
return value / rate
# 参数设计为可选的关键字参数
def print_distance(speed, duration, *,
speed_units='mph',
time_units='hours',
distance_units='miles'):
norm_speed = convert(speed, speed_units)
norm_duration = convert(duration, time_units)
norm_distance = norm_speed * norm_duration
distance = localize(norm_distance, distance_units)
print(f'{distance} {distance_units}')
# Example 4
print_distance(1000, 3,
speed_units='meters',
time_units='seconds')
上面的API可以支持的单位了,但是还无法通知开发者这个API已修改,下面加入warning提醒开发者这个API已修改。
在每个可选的关键字参数加一个判断,如果开发者未指定单位,那么告诉他API可以通过指定单位调用。如果没有传递单位也是可以用的,告诉开发者一直不修改,可能以后就会发生调用错误。
import warnings
def print_distance(speed, duration, *,
speed_units=None,
time_units=None,
distance_units=None):
# 在每个参数上添加判断,如果未传入值则提醒调用者,这个API已经发生了改变
if speed_units is None:
warnings.warn(
'speed_units required', DeprecationWarning)
speed_units = 'mph'
if time_units is None:
warnings.warn(
'time_units required', DeprecationWarning)
time_units = 'hours'
if distance_units is None:
warnings.warn(
'distance_units required', DeprecationWarning)
distance_units = 'miles'
norm_speed = convert(speed, speed_units)
norm_duration = convert(duration, time_units)
norm_distance = norm_speed * norm_duration
distance = localize(norm_distance, distance_units)
print(f'{distance} {distance_units}')
# 调用时不指定单位
print_distance(1000, 3,)
运行上面的代码,下面输出了正确的结果,同时给出了API修改的提示。
3000.0 miles
DeprecationWarning: speed_units required
warnings.warn(
DeprecationWarning: time_units required
warnings.warn(
DeprecationWarning: distance_units required
warnings.warn(
上面的代码虽然实现了提醒开发者API已修改,但是它有两个缺陷。
warings.war()语句位置输出的并不是调用print_distance函数位置输出,其实调用print_distance的位置更加重要,因为它能够提醒作者开发者那行语句没有给关键字参数传值。waring.warn函数提供了一个名为stacklevel参数,让我们可以根据栈的深度指出触发这条警告的位置,这项功能可以封装成辅助函数。
def require(name, value, default):
if value is not None:
return value
warnings.warn(
f'{name} will be required soon, update your code',
DeprecationWarning,
stacklevel=3)
return default
def print_distance(speed, duration, *,
speed_units=None,
time_units=None,
distance_units=None):
speed_units = require('speed_units', speed_units, 'mph')
time_units = require('time_units', time_units, 'hours')
distance_units = require(
'distance_units', distance_units, 'miles')
norm_speed = convert(speed, speed_units)
norm_duration = convert(duration, time_units)
norm_distance = norm_speed * norm_duration
distance = localize(norm_distance, distance_units)
print(f'{distance} {distance_units}')
print_distance(1000, 3,)
运行上面的代码,下面输出的提示信息位置都是在调用print_distance位置
3000.0 miles
106: DeprecationWarning: speed_units will be required soon, update your code
print_distance(1000, 3,)
106: DeprecationWarning: time_units will be required soon, update your code
print_distance(1000, 3,)
106: DeprecationWarning: distance_units will be required soon, update your code
print_distance(1000, 3,)