减少位置参数上的干扰


接受可选择的位置参数(通常被称为星型参数的常规名称*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的函数添加新的位置参数可以产生难于发现的问题,应该谨慎使用。

results matching ""

    No results matching ""