背景
在接口测试中,接口存在分页参数start,limit,为了获取到符合条件的所有数据,编写一个函数去循环发起接口请求,为了减少代码量,函数声明时采用了默认参数的方式,模拟代码如下
class Client():
def get_tenants(self, param={}):
res_data = []
param['start'] = param.get('start', 0)
param['limit'] = param.get('limit', 100)
print('1', param, end='\t')
num = 0
while num < 5:
num += 1
res_data.append(num)
param['start'] += param['limit']
print('2', param)
return res_data
if __name__ == '__main__':
client = Client()
client.get_tenants() # 1 {'start': 0, 'limit': 100} 2 {'start': 500, 'limit': 100}
client.get_tenants() # 1 {'start': 500, 'limit': 100} 2 {'start': 1000, 'limit': 100}
Client().get_tenants() # 1 {'start': 1000, 'limit': 100} 2 {'start': 1500, 'limit': 100}
Client().get_tenants() # 1 {'start': 1500, 'limit': 100} 2 {'start': 2000, 'limit': 100}
但是在实际使用中,首次调用函数时能够正常获取到数据,但是再次调用是却陷入死循环,断点调试发现,不传递参数再次调用函数时,进入函数内部参数不是默认值而是上一次调用函数执行返回是的值。从示例代码中发现,无论是同一个实例重复调用还是重新实例化对象,params的值都是服用的上一次的值。
原因
在 Python 中,在函数中使用可变类型(如字典、列表等)作为默认参数时,这个默认参数只会在函数定义时被创建一次,而不是每次调用函数时都会重新创建。因此,如果你在函数中修改了这个可变参数,后续的调用将会使用修改后的值,而不是初始的默认值。
param 参数的默认值是一个空字典 {}。第一次调用 get_tenants() 时,如果没有传递 param 参数,它会使用这个默认的空字典。然后在函数内部,对 param 字典进行了修改(例如添加了 start 和 limit 键)。这会影响到后续的调用,因为所有的调用都共享同一个字典实例
解决方案
为了避免这种情况,通常的做法是将默认参数设置为 None,然后在函数内部创建一个新的字典。这样每次调用函数时都会使用一个新的字典实例。
class Client():
def get_tenants(self, param=None):
if param is None:
param = {}
res_data = []
param['start'] = param.get('start', 0)
param['limit'] = param.get('limit', 100)
print('1', param, end='\t')
num = 0
while num < 5:
num += 1
res_data.append(num)
param['start'] += param['limit']
print('2', param)
return res_data
if __name__ == '__main__':
client = Client()
client.get_tenants() # 1 {'start': 0, 'limit': 100} 2 {'start': 500, 'limit': 100}
client.get_tenants() # 1 {'start': 0, 'limit': 100} 2 {'start': 500, 'limit': 100}
Client().get_tenants() # 1 {'start': 0, 'limit': 100} 2 {'start': 500, 'limit': 100}
Client().get_tenants() # 1 {'start': 0, 'limit': 100} 2 {'start': 500, 'limit': 100}
:
每次调用 get_tenants() 时,都会创建一个新的字典实例,避免了不同调用之间的状态共享。
通过检查 param 是否为 None,可以清晰地知道何时需要初始化一个新的字典。