第18/19条 位置参数/关键字参数的应用

令函数接收可变的位置参数*args),能够让代码更加清晰
案例1:定义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))
 if __name__=='__main__':
    log('My numbers are',[1,3])

但是当没有参数输入时,也必须传入一个空列表[],比较麻烦。为此可以采用接收可变的位置参数。

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))
 if __name__=='__main__':
    log('My numbers are',[1,3,9])

Python对传入的列表在*values接收后,逐个解析,作为其输入的位置参数。

接收可变的位置参数,会带来以下两个问题:
第一个问题变长参数在传给函数时,总是要先转化为元组(tuple)。意味着,如果某个函数的*操作符参数为生成器,那么当调用该函数时,Python就必须将该生成器完整的迭代一轮,并把生成器所生成的每一个值,都放入元组中,可能会带来内存的大量消耗,导致程序崩溃。

def my_generator():
    for i in range(10):
        yield i

def my_func(*args):
    print(args)

if __name__=='__main__':
    it = my_generator()
    my_func(*it)

输出结果:

(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

因此,只有我们确认输入参数个数比较少时,才应该令函数接收*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))
  
  if __name__ == '__main__':
    log(1,"Favorites",7,33) #新代码
    log("Favorites",7,33) #旧代码

问题在于,之前写好的旧代码将7原本是values的一部分,却赋值给了message,还不会产生错误,导致代码异常很难追踪。

为了避免这种情况,应该使用只能以关键字形式指定的参数,来扩展这种接受*args的函数。

下面介绍关键字参数
Python的所有位置参数都可以按照关键字传递

def remainder(number,divisor):
    return number % divisor
 if __name__=='__main__':
    # 下面几种方式等效
    remainder(20,7)
    remainder(number=20,divisor=7)
    remainder(20,divisor=7)
    remainder(divisor=7,number=20)
    remainder(number=20,7)# error

需要注意的是:

  • 位置参数必须在关键字参数之前。
  • 每个参数只能指定一次 remainder(20,number=7) ## Error

使用关键字参数的优点如下:
1.可以使得函数调用者清晰的明确各传入参数的意义。

2.可以在函数定义中提供默认值。大部分情况下,函数调用者只需要使用这些默认值就够了,若要特别地指定某个参数,则可以指定相应的关键字参数,这样可以减少重复代码,使得代码变得简洁。

3.提供了一种扩充函数参数的有效方式,使得扩充之后的函数依然能与原有的那些相兼容。

##注意位置参数在关键字参数之前
def flow_rate(weight_diff,time_diff,period=1,units_per_kg=1): 
    return ((weight_diff * units_per_kg)/time_diff) * period
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章