Python 類、對象、數據分類、函數參數傳遞的理解


  最近在基於python寫的接口自動化腳本,從Excel表中讀取所有數據,每一行數據保存為字典,再將很多行的字典數據保存到一個列表里,運行時發現,列表中的字典均相同,且一直是excel最后一行的數據,情況類比如下:
dd = {"a":1,"b":10}
i = 2
list1 = []
while i< 5:
    dd["a"] = i
    i+=1
    list1.append(dd)
print("list1:{}".format(list1))

  運行結果如下圖,打印結果並不是預期的 list1:[{'b': 10, 'a': 2}, {'b': 10, 'a': 3}, {'b': 10, 'a': 4}] ,為什么呢??

  問題的關鍵在於,數據分為可變及不可變類型,python中字典是可變類型,列表實際保存的是字典所指向的那片內存,而這片內存的內容,保存的是最后一次修改的值。
  為加深理解,重新溫故下python關於類、對象、數據分類、函數傳遞參數的相關知識。

1、基本概念

1.1 類與對象的關系

對象:包含屬性(特征)和方法(行為)。例如,狗作為一個對象,有年齡、眼睛等特征,有走路、覓食等行為。
類:即把有相同屬性和方法的對象進行提取(抽象化),是對象的模板。例如,對狗這個對象進行抽象化,把具有覓食行為,具有年齡等相同特征的對象,抽象一個Animal類。

1.2 self的作用

class Animal():
    self.age = 0    #類屬性
    def Eat(self):      #類方法
        print ("覓食")
        
dog = Animal()   #類的實例化,即對象
cat = Animal()   #類的實例化,即對象
dog.Eat()   #相當於Animal.Eat(dog)
  在python里,當對象調用類中的方法時,需要先把對象作為參數傳入方法中,相當於告訴類,“老子來調用這個方法啦,留個名”。而對象把自己傳入方法,就是通過self。
  如上圖,dog對象調用Eat(self)方法,執行dog.Eat()時,先用self接收dog對象,等同於執行Animal.Eat(dog)。
  正是因為self參數接收的是對象本身,而self的英文翻譯就是“自己,自個”,所以大家都約定俗成的用了這個單詞,它並不是python的關鍵字,如果換成把self緩存that,here等,其實也一樣。

1.3 對象的創建與引用

  在python中,一切都是對象。對象均具備三個屬性:地址,類型,值。
  當"左邊 = 右邊"時,實際是創建、引用對象的過程。
  如a = 3, 3實際上是一個對象,且對象的值為3,對象創建后存儲在內存中,被a所引用。
 
 
  如果對象中的值可以更改,該值屬於可變類型;
  如果對象中的值不能更改,該值屬於不可變類型;
  如果對象中又包含對其他對象的引用,該對象就是容器,如d={},list1[0]=d,list1就是容器。

2、數據的分類

  數據可變不可變,指的是存儲在內存的內容,即對象的值,是否可以被修改,分為倆大類:
  • 不可變類型:例如整型,浮點型,字符串類型;
  • 可變類型:例如字典,列表。

2.1 不可變類型

  不可變類型,即對象本身的值不可以改變。
  python在引用不可變類型的對象時,會尋找該對象是否創建過,若該對象已創建,則變量會直接引用該對象,不會再申請新的內存空間。
a = 3
b = 3
print(id(a))
print(id(b))
a = 4
print(id(a)) 

>> 502853488
>> 502853488
>> 502853520 
  3這個對象創建后,a、b都引用了它,所以打印出來的地址是相同的。
  當a = 4后,因為3屬於不可變類型,因此又創建了一個4的對象,將a指向這個新創建的對象。

2.2 可變類型

  可變類型,即在對象本身的值允許改變,而內存地址不需要改變,如 列表.append。
  python在引用不可變類型的對象時,會先申請新的內存空間,來存儲這個對象,有別於不可變類型。
 
a = [1,2,3]
b = [1,2,3]
print(id(a))
print(id(b))
a.append(4)
print(id(a)) 

>> 48751048
>> 48751560
>> 48751048
  a、b創建了倆個相同內容的列表,但是其指向的內存地址不相同。當對a指向的可變對象增加元素后,a所引用的對象內容已改變,但地址依舊不變。

3、函數傳遞參數的方式

3.1 值傳遞

  主函數向調用函數傳遞的參數是不可變類型時,實際上只是將實參的拷貝(即臨時副本)傳遞給了被調用函數,並不是實參本身,這樣被調函數不能直接修改主調函數中變量的值,而只能修改其私有的臨時副本的值。
def ChangeString(s):
    s = '我是不可變類型,傳遞給函數'

s = '我是不可變類型'
print (s)
ChangeString(s)
print (s)

>> 我是不可變類型
>> 我是不可變類型
  如代碼所示,s是字符串,屬於不可變類型,傳遞給ChangeString(s)時,是將s實際的值傳入,s本身不會被改變。

3.2 引用傳遞

  主函數向調用函數傳遞的參數是可變類型時,實際上是將實參的引用傳入了調用函數,對引用的操作等於對其指定的對象進行操作。
def ChangeList(list1):
    list1[1] = 5
li = [1,1,1]
print (li)
ChangeList(li)
print (li)

>> [1, 1, 1]
>> [1, 5, 1]
  如代碼所示,li是列表,屬於可變類型,傳遞給ChangeList(list1)時,list1也指向了li所引用的同一片內存。

小結

  1、類是對象的抽象化,對象是類的實例化,在python中一切都是對象。
  2、self代表的是對象本身,將對象作為一個參數傳入方法中執行。
  3、內存中的內容按是否可以修改,分為可變類型和不可變類型,所對應的可變對象和不可變對象,創建和引用方式也不同。
  4、不可變類型參數被函數調用時,是值傳遞,可變類型參數被函數調用時,是引用傳遞。
 
 
 
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM