首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Python基础教程6.4 参数魔法

6.4 参数魔法

函数使用起来很简单,创建起来也不那么复杂,但要习惯参数的工作原理就不那么容易了。先从简单的着手。

6.4.1 值从哪里来

定义函数时,你可能心存疑虑:参数的值是怎么来的呢?

通常,你不用为此操心。编写函数旨在为当前程序(甚至其他程序)提供服务,你的职责是确保它在提供的参数正确时完成任务,并在参数不对时以显而易见的方式失败。(为此,通常使用断言或异常。异常将在第8章详细介绍。)

注意 在def语句中,位于函数名后面的变量通常称为形参,而调用函数时提供的值称为实参,但本书基本不对此做严格的区分。在很重要的情况下,我会将实参称为值,以便将其与类似于变量的形参区分开来。

6.4.2 我能修改参数吗

函数通过参数获得了一系列的值,你能对其进行修改吗?如果这样做,结果将如何?参数不过是变量而已,行为与你预期的完全相同。在函数内部给参数赋值对外部没有任何影响。

>>> def try_to_change(n):

... n = 'Mr. Gumby'

...

>>> name = 'Mrs. Entity'

>>> try_to_change(name)

>>> name

'Mrs. Entity'

在try_to_change内,将新值赋给了参数n,但如你所见,这对变量name没有影响。说到底,这是一个完全不同的变量。传递并修改参数的效果类似于下面这样:

>>> name = 'Mrs. Entity'

注意 参数存储在局部作用域内。作用域将在本章稍后讨论。

字符串(以及数和元组)是不可变的(immutable),这意味着你不能修改它们(即只能替换为新值)。因此这些类型作为参数没什么可说的。但如果参数为可变的数据结构(如列表)呢?

>>> def change(n):

... n[0] = 'Mr. Gumby'

...

>>> names = ['Mrs. Entity', 'Mrs. Thing']

>>> change(names)

>>> names

['Mr. Gumby', 'Mrs. Thing']

在这个示例中,也在函数内修改了参数,但这个示例与前一个示例之间存在一个重要的不同。在前一个示例中,只是给局部变量赋了新值,而在这个示例中,修改了变量关联到的列表。这很奇怪吧?其实不那么奇怪。下面再这样做一次,但这次不使用函数调用。

注意 你可能会问,函数内的局部名称(包括参数)会与函数外的名称(即全局名称)冲突吗?答案是不会。有关这方面的详细信息,请参阅本章后面对作用域的讨论。

1. 为何要修改参数

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

storage = {}

storage['first'] = {}

storage['middle'] = {}

storage['last'] = {}

数据结构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

{'middle': {}, 'last': {}, 'first': {}}

如你所见,这个函数承担了初始化职责,让代码的可读性高了很多。

注意 在字典中,键的排列顺序是不固定的,因此打印字典时,每次的顺序都可能不同。如果你在解释器中打印出来的顺序不同,请不用担心。

下面先来编写获取人员姓名的函数,再接着编写存储人员姓名的函数。

def lookup(data, label, name):

return data[label].get(name)

函数lookup接受参数label(如'middle')和name(如'Lie'),并返回一个由全名组成的列表。换而言之,如果已经存储了作者的姓名,就可以像下面这样做:

>>> lookup(storage, 'middle', 'Lie')

['Magnus Lie Hetland']

请注意,返回的是存储在数据结构中的列表。因此如果对返回的列表进行修改,将影响数据结构。(未找到任何人时除外,因为在这种情况下返回的是None。)

下面来编写将人员存储到数据结构中的函数。(如果不能马上看懂这个函数,也不用担心。)

def store(data, full_name):

names = full_name.split()

if len(names) == 2: names.insert(1, '')

labels = 'first', 'middle', 'last'

for label, name in zip(labels, names):

people = lookup(data, label, name)

if people:

people.append(full_name)

else:

data[label][name] = [full_name]

函数store执行如下步骤。

(1) 将参数data和full_name提供给这个函数。这些参数被设置为从外部获得的值。

(2) 通过拆分full_name创建一个名为names的列表。

(3) 如果names的长度为2(只有名字和姓),就将中间名设置为空字符串。

(4) 将'first'、 'middle'和'last'存储在元组labels中(也可使用列表,这里使用元组只是为了省略方括号)。

(5) 使用函数zip将标签和对应的名字合并,以便对每个标签名字对执行如下操作:

 获取属于该标签和名字的列表;

 将full_name附加到该列表末尾或插入一个新列表。

下面来尝试运行该程序:

>>> MyNames = {}

>>> init(MyNames)

>>> store(MyNames, 'Magnus Lie Hetland')

>>> lookup(MyNames, 'middle', 'Lie')

['Magnus Lie Hetland']

看起来能正确地运行。下面再来尝试几次。

>>> store(MyNames, 'Robin Hood')

>>> store(MyNames, 'Robin Locksley')

>>> lookup(MyNames, 'first', 'Robin')

['Robin Hood', 'Robin Locksley']

>>> store(MyNames, 'Mr. Gumby')

>>> lookup(MyNames, 'middle', '')

['Robin Hood', 'Robin Locksley', 'Mr. Gumby']

如你所见,如果多个人的名字、中间名或姓相同,可同时获取这些人员。

注意 这种程序非常适合使用面向对象编程,这将在下一章介绍。

2. 如果参数是不可变的

在有些语言(如C++、 Pascal和Ada)中,经常需要给参数赋值并让这种修改影响函数外部的变量。在Python中,没法直接这样做,只能修改参数对象本身。但如果参数是不可变的(如数)呢?

不好意思,没办法。在这种情况下,应从函数返回所有需要的值(如果需要返回多个值,就以元组的方式返回它们)。例如,可以像下面这样编写将变量的值加1的函数:

>>> 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]

但更清晰的解决方案是返回修改后的值。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181002G0OP8600?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券