如果将一个变量作为参数传入函数,并且在函数内部改变这个变量的值,那么结果会怎么样呢?我们不妨做一个实验。
x = 20
s = "世界您好"
def test(x,s):
x = 40
s = "hello world"
test(x,s)
print(x,s)
执行这段代码,会输出如下图所示的内容。
在上面的代码中,首先定义了两个变量:x和s,然后将其传入test函数,并在该函数中修改这两个变量的值。最后在函数外部输出这两个变量,得到的结果是他们的值并没有改变。所以说,对于数值类型、字符串类型等一些简单类型,在函数内部可以修改变量的值,但不会影响到原始变量的值。也就是说,函数内部操作的参数变量实际上是x和s的一个副本。将变量传入函数,并修改变量值的过程与下面的代码类似。
x = 20
s = "世界您好"
# 下面的代码相当于函数内部的操作
x1 = x # x1是x的副本,相当于将x传入函数
s1 = s # s1是s的副本,相当于将s传入函数
x1 = 40
s1 = "hello world"
# 这里相当于退出函数,在函数外部输出x变量和s变量
print(x,s)
执行这段代码的输出结果与上图完全一致。
现在让我们再来看看下面的代码。在这段代码中,变量x和变量y的数据类型分别是字典和列表。
x = {"a":30, "b":20}
y = ["a","b","c"]
def test(x,y):
x["a"] = 100
y[1] = "abcd"
test(x,y)
print(x,y)
程序运行结果如下图所示。
我们可以看到,如果将字典和列表变量传入函数,在函数内部修改字典和列表变量的值,是可以影响x变量和y变量的。这就涉及到一个值传递和引用传递的问题了。如果传递的变量类型是数值、字符串、布尔等类型,那么就是值传递,如果传递的变量类型是序列、对象(后面的章节介绍)等复合类型,就是引用传递。
值传递就是在传递时,将自身复制一份,而在函数内部接触到的参数实际上是传递给函数的变量的副本,修改副本的值自然不会影响到原始变量了。而像序列、对象这样的复合类型的变量,在传入函数时,实际上也将其复制了一份,但复制的不是变量中的数据,而是变量的引用。因为这些复合类型在内存是用一块连续或不连续的内存空间保存中,要想找到这些复合类型的数据,必须得到这些内存空间的首地址,而这个首地址就是复合类型数据的引用。因此,如果将复合类型的变量传入函数,复制的是内存空间的首地址,而不是首地址指向的内存空间本身。对于本例来说,在函数内部访问的x和y与在函数外部定义的x和y指向同一个内存空间,所以修改内存空间中的数据,自然会影响到函数外部的x变量和y变量中的值。
现在我们已经知道了,如果要想在函数内部修改参数变量的值,从而在函数退出时,仍然保留修改痕迹,那么就要向函数传入复合类型的变量。这一点非常有用,我们利用函数的这个特性对某些经常使用的代码进行抽象,这样会使代码更简洁,也更容易维护。例7.3将代码抽象演绎到了极致。
本例定义了一个名为data的字典类型变量,字典data有3个key:d、names和products。其中d对应的值类型是一个字典,names和products对应的值类型都是列表。要求从控制台输入这3个key对应的值。多个值之间用逗号分隔。如“Bill,Mike,John”,在输入完数据后,通过程序将由逗号分隔的字符串转换成字典或列表。如果要转换为字典,列表偶数位置的元素为key,奇数位置的元素为value。如“a,10,b,20”转换为字典后的结果是“{a:10,b:20}”。最后输出字典data,要将每一个key和对应的值在同一行输出,不同的key和对应的值在不同行输出。可能这个描述看着有点复杂,不过不要紧,我们还是先看代码吧!
# 未使用函数抽象的代码实现
data = {}
# 下面的代码初始化字典data和key的值
data["d"] = {}
data["names"] = []
data["products"] = []
print("请输入字典数据,key和value之间用逗号分隔")
# 从控制台输入key为d的值
dictStr = input(":")
# 将以逗号分隔的字符串转换为列表
list = dictStr.split(",")
keys = []
values = []
# 将列表拆分成keys和values的两个列表
for i in range(len(list)):
# key
if i % 2 == 0:
keys.append(list[i])
else:
values.append(list[i])
# 利用zip和dict函数将keys和values两个列表合并成一个字典,
# 并利用update方法将该字典追加到key为d的值的后面。
data["d"].update(dict(zip(keys,values)))
print("请输入姓名,多个姓名之间用逗号分隔")
# 从控制台输入key为names的值
nameStr = input(":")
# 将以逗号分隔的字符串转换为列表
names = nameStr.split(",")
# 将列表names追加到key为names的值的后面
data["names"].extend(names)
print("请输入产品,多个产品之间用逗号分隔")
# 从控制台输入key为products的值
productStr = input(":")
# 将以逗号分隔的字符串转换为列表
products = productStr.split(",")
# 将列表products追加到key为products的值的后面
data["products"].extend(products)
# 输出字典data中的数据,每一个key和对应的值是一行
for key in data.keys():
print(key,":",data[key])
程序运行结果如下图所示。
如果从功能上看,上面的代码实现的很完美。不过问题是,如果要对多个字典进行同样操作呢?是不是要将这些代码复制多份?这太麻烦了,而且会造成代码极大的冗余。那么接下来,我们就用函数对这段代码进行抽象,将经常使用的代码段提炼处理封装在函数中。
在抽象代码之前,我们要先看看有哪些代码可以被抽象出来。本例可以抽象出来的代码有如下几种。
• 初始化字典data
• 从控制台输入以逗号分隔的字符串,并将其转换为列表或字典
• 输出字典data
其中初始化字典data和输出字典data这两段代码都很简单,也很容易抽象,而第2点需要费电脑子,由于字典data中有的value是字典类型,有的value是列表类型,所以就要求这个函数既可以将字符串转换为列表,又可以将字符串转换为字典。本例采用了一个flag参数进行控制,flag是布尔类型,如果该变量的值为True,表示将字符串转换为列表,如果为False,表示将字符串转换为字典。
为了一步到位,干脆将这些抽象出来的函数放到一个单独的Python脚本文件中,然后通过import作为模块导入这些函数。下面先来实现这些函数。
# 初始化函数
def init(data):
data["d"] = {}
data["names"] = []
data["products"] = []
# 从控制台采集数据,并转化为列表或字典的函数,flag为True将字符串转换为列表,为False,转换为字典
# msg表示提示文本,为了方便,这里假设输入的数据以逗号分隔,也可以将分隔符通过函数参数传入
def inputListOrDict(flag,msg):
print(msg)
# 从控制台输入字符串
inputStr = input(":")
# 将字符串用逗号拆分成列表
list = inputStr.split(",")
# 返回列表
if flag:
return list
# 下面的代码将list转换为字典,并返回这个字典
keys = []
values = []
result = {}
for i in range(len(list)):
# key
if i % 2 == 0:
keys.append(list[i])
else:
values.append(list[i])
# 返回字典
return dict(zip(keys,values))
# 输出字典中的数据
def outDict(data):
for key in data.keys():
print(key,":",data[key])
在上面的代码中定义了3个函数:init、inputListOrDict和outDict,分别用来初始化字典、从控制台输入字符串,并将其转换为列表或字典、以及在控制台输出字典。下面我们利用这3个函数处理两个字典:data1和data2。
# 导入dataman.py中的所有函数
from dataman import *
# 定义字典data1
data1 = {}
# 定义字典data2
data2 = {}
# 初始化data1
init(data1)
# 初始化data2
init(data2)
# 从控制台输入字符串,并将其转换为字典,最后追加到key为d的值的后面
data1["d"].update(inputListOrDict(False, "请输入字典数据,key和value之间用逗号分隔"))
# 从控制台输入字符串,并将其转换为列表,最后追加到key为names的值的后面
data1["names"].extend(inputListOrDict(True, "请输入姓名,多个姓名之间用逗号分隔"))
# 从控制台输入字符串,并将其转换为列表,最后追加到key为products的值的后面
data1["products"].extend(inputListOrDict(True, "请输入产品,多个产品之间用逗号分隔"))
# 下面的代码与对data1的操作类似
data2["d"].update(inputListOrDict(False, "请输入字典数据,key和value之间用逗号分隔"))
data2["names"].extend(inputListOrDict(True, "请输入姓名,多个姓名之间用逗号分隔"))
data2["products"].extend(inputListOrDict(True, "请输入产品,多个产品之间用逗号分隔"))
# 输出data1
outDict(data1)
# 输出data2
outDict(data2)
程序运行结果如下图所示。
怎么样,利用函数将经常使用的代码抽象成了3个函数,是不是在使用起来很方便呢?尤其在处理多个字典的情况下更是如此。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。