>>> from env_helper import info; info()
页面更新时间: 2023-04-18 20:32:24
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-7-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

8.3. 参数魔法

参数,也叫参变量,是一个变量。在def语句中,位于函数名后面的变量通常称为形参,而调用函数时提供的值称为实参。

8.3.1. 我能修改参数吗

函数通过参数获得了一系列的值,你能对其进行修改吗?

>>> def try_to_change(n):
>>>     n = 'Mr. Gumby'
>>> name = 'Mrs. Entity'
>>> try_to_change(name)
>>> name
'Mrs. Entity'

与你预期的结果完全相同,在函数内部给参数赋值对外部没有任何影响。传递并修改参数的效果类似于下面这样:

>>> name = 'Mrs. Entity'
>>> n = name # 与传递参数的效果几乎相同
>>> n = 'Mr. Gumby' # 这是在函数内进行的
>>> name
'Mrs. Entity'

字符串(以及数和元组)是不可变的,这意味着你不能修改它们,而只能替换为新值。但如果参数为可变的数据结构(如列表)呢?

>>> def change(n):
>>>     n[0] = 'Mr. Gumby'
>>> names = ['Mrs. Entity', 'Mrs. Thing']
>>> change(names)
>>> names
['Mr. Gumby', 'Mrs. Thing']

这个示例也在函数内修改了参数,但是它修改了变量关联到的列表,而前一个示例只是给局部变量赋了新值。下面再这样做一次,但这次不使用函数调用。

>>> names = ['Mrs. Entity', 'Mrs. Thing']
>>> n = names # 再次假装传递名字作为参数
>>> n[0] = 'Mr. Gumby' # 修改列表
>>> names
['Mr. Gumby', 'Mrs. Thing']
>>> names = ['Mrs. Entity', 'Mrs. Thing']
>>> n = names[:]  #复制整个列表
>>> n
['Mrs. Entity', 'Mrs. Thing']

现在n和names包含两个相等但不同的列表。

>>> n is names
False
>>> n == names
True

现在如果修改n,将不会影响names。

>>> n[0] = 'Mr. Gumby'
>>> n
['Mr. Gumby', 'Mrs. Thing']
>>> names
['Mrs. Entity', 'Mrs. Thing']

下面来尝试结合使用这种技巧和函数change。

>>> change(names[:])
>>> names
['Mrs. Entity', 'Mrs. Thing']

注意 函数内的局部名称会与函数外的名称冲突吗? 答案是不会。

为何要修改参数

在提高程序的抽象程度方面,使用函数来修改数据结构是一种不错的方式。 假设你要编写一个程序,让它存储姓名,并让用户能够根据名字、中间名或姓找人。为此,你可 能使用一个类似于下面的数据结构:

>>> storage = {}
>>> storage['first'] = {}
>>> storage['middle'] = {}
>>> storage['last'] = {}
>>> storage
{'first': {}, 'last': {}, 'middle': {}}

数据结构storage是一个字典,包含3个键:‘first’、‘middle’和’last’。在每个键下都存储了一个字典。这些子字典的键为姓名(名字、中间名或姓),而值为人员列表。例如,要将作者加入这个数据结构中,可以像下面这样做:

>>> me = 'Magnus Lie Hetland'
>>> storage['first']['Magnus'] = [me]
>>> storage['middle']['Lie'] = [me]
>>> storage['last']['Hetland'] = [me]

每个键下都存储了一个人员列表。在这个例子里,这些列表只包含作者。 现在,要获取中间名为Lie的人员名单,可像下面这样做:

>>> storage['middle']['Lie']
['Magnus Lie Hetland']

如你所见,将人员添加到这个数据结构中有点繁琐,在多个人的名字、中间名或姓相同时尤 其如此,因为在这种情况下需要对存储在名字、中间名或姓下的列表进行扩展。下面来添加我的妹妹,并假设我们不知道数据库中存储了什么内容。

>>> my_sister = 'Anne Lie Hetland'
>>> storage['first'].setdefault('Anne', []).append(my_sister)
>>> storage['middle'].setdefault('Lie', []).append(my_sister)
>>> storage['last'].setdefault('Hetland', []).append(my_sister)
>>> storage['first']['Anne']
['Anne Lie Hetland']
>>> storage['middle']['Lie']
['Magnus Lie Hetland', 'Anne Lie Hetland']

下面首先来创建一个初始化数据结构的函数。

>>> def init(data):
>>>     data['first'] = {}
>>>     data['middle'] = {}
>>>     data['last'] = {}

这里只是将初始化语句移到了一个函数中。你可像下面这样使用这个函数:

>>> storage = {}
>>> init(storage)
>>> storage
{'first': {}, 'last': {}, 'middle': {}}

如果参数是不可变的

在python中,如果你需要给参数赋值,并让这种修改影响函数外部的变量,你应从函数中返回所有需要的值。实例如下:

>>> def inc(x):
>>>     return x + 1
>>> foo = 10
>>> foo = inc(foo)
>>> foo
11

如果一定要修改参数,可玩点花样,比如将值放在列表中,如下所示:

>>> def inc(x):
>>>     x[0] = x[0] + 1
>>> foo = [10]
>>> inc(foo)
>>> foo
[11]

8.3.2. 关键字参数和默认值

请看下面两个函数:

>>> def hello_1(greeting, name):
>>>     print('{}, {}!'.format(greeting, name))
>>>
>>> def hello_2(name, greeting):
>>>     print('{}, {}!'.format(name, greeting))

这两个函数的功能完全相同,只是参数的排列顺序相反。

>>> hello_1('Hello', 'world')
Hello, world!
>>> hello_2('Hello', 'world')
Hello, world!

为了简化调用工作,可指定参数的名称。

>>> hello_1(greeting='Hello', name='world')
Hello, world!

在这里,参数的顺序无关紧要。

>>> hello_1(name='world', greeting='Hello')
Hello, world!

不过名称很重要。

>>> hello_2(greeting='Hello', name='world')
world, Hello!

像这样使用名称指定的参数称为关键字参数,主要优点是有助于澄清各个参数的作用。

可以像下面这样做:

>>> def hello_3(greeting='Hello', name='world'):
>>>     print('{}, {}!'.format(greeting, name))
>>> hello_3()
Hello, world!
>>> hello_3('Greetings')
Greetings, world!
>>> hello_3('Greetings', 'universe')
Greetings, universe!
>>> hello_3(name='Gumby')
Hello, Gumby!

例如,函数hello可能要求必须指定姓名,而问候语和标点是可选的。

>>> def hello_4(name, greeting='Hello', punctuation='!'):
>>>     print('{}, {}{}'.format(greeting, name, punctuation))

调用这个函数的方式很多,下面是其中的一些:

>>> hello_4('Mars')
Hello, Mars!
>>> hello_4('Mars', 'Howdy')
Howdy, Mars!
>>> hello_4('Mars', 'Howdy', '...')
Howdy, Mars...
>>> hello_4('Mars', punctuation='.')
Hello, Mars.
>>> hello_4('Mars', greeting='Top of the morning to ya')
Top of the morning to ya, Mars!