仅强调关键字参数


Python中通过关键字来传递参数值是一个很强大的特征(详见第19项:使用关键字参数来提供可选行为操作)。关键字参数的灵活性可以帮助你书写更加简洁,易读的代码。

例如:你可能想用一个数除以另一个数,但是在特殊情况下,你需要非常的小心。你可能想忽略ZeroDivisionError异常,使用无穷值返回来替代。又有时,你想忽略OverflowError异常返回零来替代。

def safe_division(number, divisor, ignore_overflow, ignore_zero_division):
    try:
        return number / divisor
    except OverflowError:
        if ignore_overflow:
            return 0
        else:
            raise
    except ZeroDivisionError:
        if ignore_zero_division:
            return float('inf')
        else:
            raise

# 使用起来也是直截了当,调用的时候会忽略除数的float overflow,并返回0;或者忽略除数为零,返回无限值
result = safe_division(1, 10**500, True, False)
print(result)
result = safe_division(1, 0, False, True)
print(result)
>>>
0.0
inf

容易使人疑惑的问题就是两个布尔类型的控制异常忽略的参数的位置。改变这一现状的一个方式就是使用关键字参数来改善代码的可读性。默认地,这个函数可能过于谨慎了,并且可能重新触发异常。

def safe_division_b(number, divisior, ignore_overflow=False, ignore_zero_division=False)
    # ······

这样的话调用方就可以指定关键字来决定到底要忽略哪一个操作,或者来覆盖默认的行为。

safe_division_b(1, 10**500, ignore_overflow=True)
safe_division_b(1, 10**500, ignore_zer0_division=True)

然而问题是,当关键字参数变成了可选的时候,就不能够强迫调用者使用关键字参数来简化代码了。不过还好,新定义的这个safe_division_b函数仍然可以通过位置参数的调用来工作。

safe_division_b(1, 10*500, True, False)

当函数变得像上面代码中展示的那样复杂的时候,最好是需要调用者对他们自己的意图非常的了解。如果都不知道自己要做什么,又怎么能做得好呢?在Python3中,你可以只使用关键字参数来使你的代码变得更加的整洁。这些参数也仅仅只能通过关键字参数赋值的形式被使用,而不是通过位置参数赋值的方式。

def safe_division_c(number, division, *, ignore_overflow=False, ignore_zero_division=False):
    # ······

# 1.然后现在如果还是使用位置参数赋值的方式的话,函数就不会正常地工作了。
safe_division_c(1, 10**500, True, False)
>>>
TypeError: safe_division_c() takes 2 position arguments but 4 ware given.

# 2. 现在使用关键字参数赋值的方式的话,就可以得到正常的预期结果了。
safe_division_c(1, 0, ignore_zero_division=True)
try:
    safe_division_c(1, 0)
except ZeroDivisionError:
    pass

Python2 中的只使用关键字参数

不幸的是,我们家的Python2中也是没有明确的像Python3中的keyword-only语法。不过伟大的程序员们总是可以想到替代方案嘛。那就是通过在参数列表中使用**操作符来帮助无效调用来触发TypeError。其实**操作符和*操作符起到的作用类似(详见第18项:使用可变位置参数减少代码干扰),除了接收可变数量的位置参数以外,它还可以接收任意数量的关键字参数。

# Python2
def print_args(*args, **kwargs):
    print 'Positional:', args
    print 'Keyword:', kwargs

print_args(1, 2, foo='bar', stuff='meep')
>>>
Positional: (1, 2)
Keyword: {'foo': 'bar', 'stuff': 'meep'}

为了实现在Python2中的keyword-only 性质的safe_division函数,我们就需要使用**操作符了。然后从kwargs字典中去除我们预期的关键字参数,使用pop方法的第二个参数来指定默认的值,这对关键字对应的值出现丢失的情况非常的适用。最终,确保kwargs列表中没有所需的关键字的遗留来防止调用方提供无效的参数。

# Python 2
def safe_division(number, divisor, **kwargs):
    ignore_overflow = kwargs.pop('ignore_overflow', False)
    ignore_zero_division = kwargs.pop('ignore_zero_division', False)
    if kwargs:
        raise TypeError("Unexpected **kwargs: %r"%kwargs)
    # ···

# 测试
safe_division(1, 10)
safe_division(1, 0, ignore_zero_division=True)
safe_division(1, 10**500, ignore_overflow=True)
# 而想通过位置参数赋值,就不会正常的运行了
safe_division(1, 0, False, True)
>>>
TypeError:safe_division() takes 2 positional arguments but 4 were given.

# 而输入了非预期的关键字的时候,也会触发类型错误
safe_division(0, 0, unexpected=True)
>>>
TypeErroe: Unexpected **kwargs: {'unexpected': True}

备忘录

  • 关键字参数使得函数调用的意图更加的清晰,明显。
  • 适用keyword-only参数可以强迫函数调用者提供关键字来赋值,这样对于容易使人疑惑的函数参数很有效,尤其适用于接收多个布尔变量的情况。
  • Python3中有明确的keyword-only函数语法。
  • Python2中可以通过**kwargs模拟实现keyword-only函数语法,并且人工的触发TypeError异常。
  • keyword-only在函数参数列表中的位置很重要,这点大家尤其应该明白!

results matching ""

    No results matching ""