python類變數與構造函數的使用

類變數:可在類的所有實例之間共享的變數
實例類對象:類的實例是調用類對象來創建的。如: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()!