python類變數與構造函數的使用
- 2020 年 3 月 2 日
- 筆記
類變數:可在類的所有實例之間共享的變數
實例類對象:類的實例是調用類對象來創建的。如:par = Parent(),par就是類Parent的一個實例類對象。
實例變數(成員變數):同一個類對象可以創建多個實例類對象,類定義中有self標誌的變數就是實例變數
一個例子,下面的程式碼有問題
class MyObject(object): x = 1 def __init__(self): objectNum = 99 def changeNum(self, anotherNum): self.objectNum = anotherNum def showNum(self): print("self.num = ", self.objectNum)
什麼問題呢,看似構造函數__init__中的變數object在實例化對象的時候會自動創建並初始化為99,其實不然,這裡用一個小的測試程式碼就可以發現問題。
obj = MyObject() obj.showNum() Traceback (most recent call last): File "class.py", line 24, in <module> obj.showNum() File "class.py", line 20, in showNum print("self.num = ", self.objectNum) AttributeError: 'MyObject' object has no attribute 'objectNum'
報錯了,提示實例化對象MyObject並沒有objectNum這個普通成員變數,為什麼呢?
問題就在於,在Python中,類的成員變數必須使用self.propertName進行聲明,這樣才能完成創建,因為self的含義就是代表實例對象;
在這個類中,objectNum和self.objectNum就是兩個完全不同的東西:
定義在__init__函數中的變數objectNum在這裡是一個局部變數,不是類變數
接下來我們可以再寫一段程式碼,調用changNum()方法,來生成這個成員變數self.objectNum:
obj = MyObject() obj.changeNum(10) obj.showNum() >>> self.num = 10
能看到成功返回結果,
由於在changeNum()方法中,有self.objectNum = anotherNum的賦值,而__init__中,沒有創建類普通成員變數self.objectNum, 而是創建了一個臨時變數objectNum,所以在這裡,雖然changeNum()沒有被自動調用(因為不是__init__()函數),但是其實充當了創建類成員變數和初始化的作用, 但是python並不會在創建新的實例化對象的時候自動調用它。
所以通過實驗得到3個結論:
1.python中的”構造函數”非常的自由,如果不考慮自動調用,任何類方法都可以去創建類成員變數:
class ExampleClass: def createObjectProperty(self, value): self.newObjectProperty = value
如上面的程式碼,這裡聲明一個類方法,傳入參數self 和 value,調用這個方法,就可以生成一個普通成員變數newObjectProperty,並對其賦初值value
2.如果想要找到真正意義上的成員變數,那麼只需要在__init__(self)中聲明self.objectProperty即可
3.python中的self不能隱式調用,如果你不想生成一個臨時變數而是創建一個類成員變數,那麼就應該使用self.variableName
class MyObject(object): x = 1 def __init__(self): self.objectNum = 99 def changeNum(self, anotherNum): self.objectNum = anotherNum def showNum(self): print("self.num = ", self.objectNum)
obj = MyObject()
# obj.changeNum(10)
obj.showNum()
>>>self.num = 99
知道了成員變數的問題之後,再來討論一下類變數
class MyObject(object): x = 1 def __init__(self): self.objectNum = 99 def changeNum(self, anotherNum): self.objectNum = anotherNum def showNum(self): print("self.num = ", self.objectNum) obj = MyObject() print(MyObject.x) >>> 1
在聲明類T的時候,我們在所有的方法之外(但是仍在類的作用域中聲明了一個變數classNum),從命名的角度來看,我們希望這是一個類變數,但我們不希望這次又是一個成員變數,測試發現它確實可以由類名直接訪問再試一下能否修改:
MyObject.x = 100 print(MyObject.x) >>> 100
發現可以修改
下面我們驗證一下其是否能被所有實例化對象訪問和修改,並且是否具有全局性。
t1 = MyObject() print(t1.x) >>> 1 t2 = MyObject() print(t2.x) >>> 1 MyObject.x = 1000 print(t1.x) >>> 1000 print(t2.x) >>> 1000 t1.x = 2000 print(t2.x) >>>1000 print(t1.x) >>>2000 print(MyObject.x) >>>1000
從以上結果看出類名.類變數名修改其值會導致實例化對象的值全部被改變,但是用實例化對象名.類變數名修改其值,就僅僅改變自己,不會真的改變類變數的數值。
我們來檢查一下記憶體,看一段程式碼:
t2 = MyObject() t1 = MyObject() print(MyObject.x is t1.x) >>>True print(MyObject.x is t2.x) >>>True print(t2.x is t1.x) >>>True --------------------------------------- t2 = MyObject() t1 = MyObject() t2.x = 10 print(MyObject.x is t1.x) >>>True print(MyObject.x is t2.x) >>>False print(t2.x is t1.x) >>>False -------------------------------------- t2 = MyObject() t1 = MyObject() MyObject.x = 100 t2.x = 10 print(MyObject.x is t1.x) >>>True print(MyObject.x is t2.x) >>>False print(t2.x is t1.x) >>>False
看得出來在最開始的時候MyObject.x和實例化對象t1.x與t2.x記憶體是同一處的,但當直接修改了實例化對象t2.x的數值後t2.x記憶體的數值便與其他兩個不同,所以直接修改實例化對象的數值會指向新的記憶體空間,並且不受類變數改變而改變。
總結:
一個類=類變數(可以沒有)+構造函數(必須有,沒有的話默認調用)+成員函數(自己定義,可以沒有)
構造函數中定義了類的成員變數,類的成員變數一定是在構造函數中以self.開頭的變數!
成員函數中可以調用成員變數和類變數!成員函數的形參在類的實例調用該函數時傳遞,成員函數的局部變數在該成員函數內部定義。調用成員函數和調用普通函數一樣,只是成員函數由該函數對應的類調用,即需要寫成xxxx.func()而不是直接使用func()!