Python 中的可变函数参数

PythonPythonBeginner
立即练习

This tutorial is from open-source community. Access the source code

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

本节介绍可变参数函数参数,有时也称为 *args**kwargs

位置可变参数(*args)

一个接受任意数量参数的函数被称为使用了可变参数。例如:

def f(x, *args):
 ...

函数调用:

f(1,2,3,4,5)

额外的参数会作为一个元组传递。

def f(x, *args):
    ## x -> 1
    ## args -> (2,3,4,5)

关键字可变参数(**kwargs)

一个函数也可以接受任意数量的关键字参数。例如:

def f(x, y, **kwargs):
...

函数调用:

f(2, 3, flag=True, mode='fast', header='debug')

额外的关键字参数会以字典的形式传递。

def f(x, y, **kwargs):
    ## x -> 2
    ## y -> 3
    ## kwargs -> { 'flag': True,'mode': 'fast', 'header': 'debug' }

两者结合

一个函数也可以接受任意数量的可变关键字参数和非关键字参数。

def f(*args, **kwargs):
  ...

函数调用:

f(2, 3, flag=True, mode='fast', header='debug')

参数被分为位置参数和关键字参数两部分

def f(*args, **kwargs):
    ## args = (2, 3)
    ## kwargs -> { 'flag': True,'mode': 'fast', 'header': 'debug' }
  ...

这个函数可以接受任意组合的位置参数或关键字参数。它有时用于编写包装器,或者当你想要将参数传递给另一个函数时使用。

传递元组和字典

元组可以展开为可变参数。

numbers = (2,3,4)
f(1, *numbers)      ## 等同于 f(1,2,3,4)

字典也可以展开为关键字参数。

options = {
    'color' : 'red',
    'delimiter' : ',',
    'width' : 400
}
f(data, **options)
## 等同于 f(data, color='red', delimiter=',', width=400)

练习 7.1:可变参数的简单示例

尝试定义以下函数:

>>> def avg(x,*more):
        return float(x+sum(more))/(1+len(more))

>>> avg(10,11)
10.5
>>> avg(3,4,5)
4.0
>>> avg(1,2,3,4,5,6)
3.5
>>>

注意参数 *more 是如何收集所有额外参数的。

练习 7.2:将元组和字典作为参数传递

假设你从一个文件中读取了一些数据,并得到了一个这样的元组:

>>> data = ('GOOG', 100, 490.1)
>>>

现在,假设你想用这些数据创建一个 Stock 对象。如果你试图直接传递 data,它是不起作用的:

>>> from stock import Stock
>>> s = Stock(data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Stock.__init__() missing 2 required positional arguments:'shares' and 'price'
>>>

使用 *data 可以轻松解决这个问题。试试这个:

>>> s = Stock(*data)
>>> s
Stock('GOOG', 100, 490.1)
>>>

如果你有一个字典,你可以使用 ** 来代替。例如:

>>> data = { 'name': 'GOOG','shares': 100, 'price': 490.1 }
>>> s = Stock(**data)
Stock('GOOG', 100, 490.1)
>>>

练习 7.3:创建实例列表

在你的 report.py 程序中,你使用如下代码创建了一个实例列表:

def read_portfolio(filename):
    '''
    将股票投资组合文件读取为一个字典列表,字典包含键
    name、shares 和 price。
    '''
    with open(filename) as lines:
        portdicts = fileparse.parse_csv(lines,
                               select=['name','shares','price'],
                               types=[str,int,float])

    portfolio = [ Stock(d['name'], d['shares'], d['price'])
                  for d in portdicts ]
    return Portfolio(portfolio)

你可以使用 Stock(**d) 来简化这段代码。进行这个更改。

✨ 查看解决方案并练习

练习 7.4:参数传递

fileparse.parse_csv() 函数有一些用于更改文件分隔符和错误报告的选项。也许你想将这些选项暴露给上面的 read_portfolio() 函数。进行如下更改:

def read_portfolio(filename, **opts):
    '''
    将股票投资组合文件读取为一个字典列表,字典包含键
    name、shares 和 price。
    '''
    with open(filename) as lines:
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

    portfolio = [ Stock(**d) for d in portdicts ]
    return Portfolio(portfolio)

做出更改后,尝试读取一个包含一些错误的文件:

>>> import report
>>> port = report.read_portfolio('missing.csv')
第4行:无法转换 ['MSFT', '', '51.23']
第4行:原因是 int() 无法将空字符串转换为十进制整数
第7行:无法转换 ['IBM', '', '70.44']
第7行:原因是 int() 无法将空字符串转换为十进制整数
>>>

现在,尝试抑制错误:

>>> import report
>>> port = report.read_portfolio('missing.csv', silence_errors=True)
>>>
✨ 查看解决方案并练习

总结

恭喜你!你已经完成了可变参数实验。你可以在 LabEx 中练习更多实验来提升你的技能。