减少位置参数上的干扰
接受可选择的位置参数(通常被称为星型参数的常规名称*args
)可以使得一个函数调用起来更加的简洁,并能明显减少可视化的“噪音”。例如: 你想打印一些调试信息。通过一个固定的参数数字,你需要一个带有信息和一串值的集合的函数。
def log(message, values):
if not values:
print(message)
else:
values_str = ', '.join(str(x) for x in values)
print('%s: %s '%(message, values_str))
log('My numbers are', [1, 2])
log('Hi there', [])
>>>
My numbers are: 1, 2
Hi There
但是如果没有values
要传递的时候必须跟着传递一个空集合,这样使得代码看起来不是很简洁。如果可以忽略第二个参数的话就会很好了。在Python
中可以通过在参数前面添加*号
来实现,这样就变成了可有可无的参数了。此时log
函数的第一个参数是必不可少的,而其他的参数就是可选的了。函数体部分我们也不需要进行改变,唯一需要改变的就是调用这个函数的代码即可。
def log(message, *values):
if not values:
print(message)
else:
values_str = ', '.join(str(x) for x in values)
print('%s: %s '%(message, values_str))
log('My numbers are', [1, 2])
log('Hi there')
>>>
My numbers are: 1, 2
Hi There
如果你已经有一个集合了,并且向调用一个像log
这样的可变参数函数。你就可以通过使用*
操作符来实现。在Python
中可以通过这样的方式把序列中的元素值当做位置参数来使用。
favorites = [7, 33, 99]
log('Favorite colors', *favorites)
>>>
Favorite colors: 7, 33, 99
位置参数的两个问题
虽然位置参数的使用使得我们的程序变得更加的灵活,但是同样有其自身的一些问题。
- 第一个是可变参数在被传递给函数的时候经常会被转变成元组。这意味着如果调用方对生成器使用了
*
操作符,程序将会遍历整个序列直至完毕。如果结果集元组包含很多数据的话,同样会因为内存危机而导致宕机的可能。
def my_generator():
for i in range(10):
yield i
def my__func(*agrs):
print(*args)
it my_generator()
my_func(*it)
>>>
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
当你明确的指导传进来的列表数目已知是很小的时候,接收*args
参数的函数会很合适。如果函数可以指定变量名称进行传值的话,那将是完美的。彼时代码的简洁性和可读性都将得到大大的提高。
- 第二个问题就是带有
*args
参数的话,你的函数以后就不能再新增其他的位置参数了,这还涉及到今后的函数移植问题。如果你想在函数的前面部分添加一个位置参数的话,已存在的调用者就不能正常的处理了。
def log(sequence, message, *values):
if not values:
print('%s: %s' % (sequence, message))
else:
values_str = ', '.join(str(x) for x in values)
print('%s: %s:%s' %(sequence, message, values_str))
# 新的调用可以正常的工作
log(1, 'Favorites', 7, 33)
# 原来的函数调用方式不能正常的工作
log('Favorite numbers', 7, 33)
>>>
1: Favorites: 7, 33
Favorite numbers: 7: 33
这里出现的问题:由于sequence
变量未给出而导致了第二个函数调用把7
作为了message
变量来用了。像这样的问题很难通过漏洞追踪来发现,因为其可以并没有引发异常信息。当你想拓展函数来接受*args
这种变量的时候,为了避免这种问题发生的可能性,你应该尽量的使用关键字变量(详见第21:使用关键字变量来增强简洁性)。
备忘录
- 通过使用
*args
定义语句,函数可以接收可变数量的位置参数。 - 你可以通过
*
操作符来将序列中的元素作为位置变量。 - 带有
*
操作符的生成器变量可能会引起程序的内存溢出,或者机器宕机。 - 为可以接受
*args
的函数添加新的位置参数可以产生难于发现的问题,应该谨慎使用。