您好,登錄后才能下訂單哦!
如果將一個變量作為參數傳入函數,并且在函數內部改變這個變量的值,那么結果會怎么樣呢?我們不妨做一個實驗。
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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。