面向對象結構
面向對象類中的資料大緻分為兩塊區塊:
class Demo:
name = '張三' # 第一部分:靜态字段(靜态變量)
age = 18 # 第一部分:靜态字段(靜态變量)
address = '北京' # 第一部分:靜态字段(靜态變量)
def __init__(self): # 第二部分:動态字段(方法)部分
pass
def func(self): # 第二部分:動态字段(方法)部分
pass
結論:分為靜态字段(變量)和動态字段(方法)兩部分
每個部分還可以分為多個小部分:
class Demo:
idNum = '111111111' # 靜态變量
__address = '北京' # 私有靜态變量
def __init__(self, name, age, height): # 構造方法,也屬于普通方法
self.name = name
self.__age = age
self.__height = height
def func1(self): # 普通方法
pass
def __func2(self): # 私有方法
pass
@classmethod
def class_func(cls): # 類方法,至少含有一個cls參數
print("類方法")
@staticmethod # 靜态方法,無預設參數
def static_func():
print("靜态方法")
@property # 屬性
def property_func(self):
return "屬性"
類有這麼多的成員,那麼我們先從那些地方研究呢? 可以從私有與公有部分,方法的詳細分類兩個方向去研究。
私有與公有
對于每一個類的成員而言都有兩種形式:
- 公有成員,在任何地方都能通路。
- 私有成員,隻有在類的内部才能方法。
私有和公有成員通路權限不同:
靜态字段(靜态變量)
- 公有靜态字段:類可以通路;内部類可以通路;派生類(子類)可以通路。
- 私有靜态字段:僅内部類可以通路。
class DemoA:
name1 = '公有字段'
__name2 = '私有字段'
def func1(self): # 内部類通路
print((DemoA.name1)) # 可以通路
print(DemoA.__name2) # 可以通路
class DemoB(DemoA):
def show1(self): # 派生類通路
print(DemoA.name1) # 可以通路
# print(DemoA.__name2) # 不可以通路
c1 = DemoA()
c1.func1()
c2 = DemoB()
c2.show1()
結果:
公有字段
私有字段
公有字段
Process finished with exit code 0
普通字段(對象屬性)
- 公有普通字段:對象可以通路;類内部可以通路;派生類中可以通路。
- 私有普通字段:僅類内部可以通路。
class DemoA:
def __init__(self):
self.foo1 = '公有字段'
self.__foo2 = '私有字段'
def func1(self): # 内部類通路
print(self.foo1) # 可以通路
print(self.__foo2) # 可以通路
class DemoB(DemoA):
def show1(self): # 派生類通路
print(self.foo1) # 可以通路
# print(self.__foo2) # 不可以通路,報錯
c1 = DemoA()
c1.func1()
c2 = DemoB()
c2.show1()
結果:
公有字段
私有字段
公有字段
Process finished with exit code 0
方法:
- 公有方法:對象可以通路;類内部可以通路;派生類中可以通路。
- 私有方法:僅類内部可以通路。
class DemoA:
def __init__(self):
pass
def add1(self):
print("超類公有方法")
def __add2(self):
print("超類私有方法")
def add3(self):
self.__add2() # 本類調用自己的私有方法
class DemoB(DemoA):
def show1(self):
print("派生類公有方法")
def __show2(self):
print("派生類私有方法")
# def show3A_add2(self):
# self.__add2 # 派生類不能引用超類私有方法,報錯
def show4B_show2(self):
self.__show2() # 派生類可以引用自身的私有方法,屬于自己調用
c1 = DemoA()
c1.add1() # 調用自身公有方法
# c1.add2() # 外部無法調用私有方法,報錯
c1.add3() # 外部間接調用類中私有方法
c2 = DemoB()
c2.add1() # 派生類調用超類公有方法
# c2.add2() # 派生類無法調用超類私有方法,報錯
c2.add3() # 派生類調用自身公有方法
c2.show1() # 派生類調用自身公有方法
# c2.show2() # 派生類在外部無法調用自身私有方法,報錯
# c2.show3A_add2() # 派生類不能引用超類私有方法,報錯
c2.show4B_show2() # 派生類間接調用父類私有方法
結果:
超類公有方法
超類私有方法
超類公有方法
超類私有方法
派生類公有方法
派生類私有方法
Process finished with exit code 0
總結:
對于這些私有成員來說,他們隻能在類的内部使用,不能再類的外部以及派生類中使用。
成員
字段
字段包括:普通字段和靜态字段,他們在定義和使用中有所差別,而最本質的差別是記憶體中儲存的位置不同。
普通字段屬于對象,靜态字段屬于類。
class Demo:
country = '中國' # 靜态字段
def __init__(self, name):
self.name = name # 普通字段
obj = Demo('北京')
# 直接通路普通字段
print(obj.name)
print(obj.country)
# 直接通路靜态字段
print(Demo.country)
結果:
北京
中國
中國
Process finished with exit code 0
結論:
普通字段需要通過對象來通路,也可以使用使用對象來通路。
靜态字段通過類通路,類不能直接通路普通字段。
注意:靜态字段在記憶體中隻儲存一份,普通字段在每個對象中都要儲存一份。
方法
方法包括:普通方法、靜态方法和類方法,三種方法在記憶體中都歸屬于類,差別在于調用方式不同。
- 普通方法:由對象調用;至少一個self參數;執行普通方法時,自動将調用該方法的對象指派給self。
- 類方法:由類調用; 至少一個cls參數;執行類方法時,自動将調用該方法的類複制給cls。
- 靜态方法:由類調用,無預設參數。
class Foo:
def __init__(self, name):
self.name = name
def ord_func(self):
""" 定義普通方法,至少有一個self參數 """
print('普通方法')
@classmethod
def class_func(cls):
""" 定義類方法,至少有一個cls參數,cls表示這個類 """
print('類方法')
@staticmethod
def static_func():
""" 定義靜态方法 ,無預設參數"""
print('靜态方法')
# 調用普通方法
f = Foo(1) # 随便寫一個實參,無實際意義
f.ord_func()
# 調用類方法
Foo.class_func()
# 調用靜态方法
Foo.static_func()
結果:
普通方法
類方法
靜态方法
Process finished with exit code 0
這些方法的差異:
相同點:對于所有的方法而言,均屬于類(非對象)中,是以,在記憶體中也隻儲存一份。
不同點:方法調用者不同、調用方法時自動傳入的參數不同。
屬性(property)
具有通路它時會執行一段功能(函數)然後傳回值的特性。
例:BMI指數(bmi是計算而來的,但很明顯它聽起來像是一個屬性而非方法,如果我們将其做成一個屬性,更便于了解) 成人的BMI數值:
- 過輕:低于18.5
- 正常:18.5-23.9
- 過重:24-27
- 肥胖:28-32
- 非常肥胖, 高于32
體質指數(BMI)=體重(kg)÷身高^2(m),例:70kg÷(1.75×1.75)=22.86。
class People:
def __init__(self, name, weight, height):
self.name = name
self.weight = weight
self.height = height
@property
def bmi(self):
return self.weight / (self.height ** 2)
p1 = People('張三', 70, 1.75)
print(p1.bmi)
結果:
22.857142857142858
Process finished with exit code 0
ps:這是本人的BIM值,唉~又胖了啊。
使用屬性的好處:
将一個類的函數定義成特性以後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,這種特性的使用方式遵循了統一通路的原則。
由于新式類(Python3中全是新式類)中具有三種通路方式,我們可以根據他們幾個屬性的通路特點,分别将三個方法定義為對同一個屬性:擷取、修改、删除。
class people: # 定義一個人的類
def __init__(self, name, sex):
self.name = name
self.sex = sex # p1.sex = "male",遇到property,優先用property
@property # 檢視sex的值
def sex(self):
return self.__sex # 傳回正真存值的地方
@sex.setter # 修改sex的值
def sex(self, value):
if not isinstance(value, str): # 在設定值之前進行類型檢查
raise TypeError("性别必須是字元串類型") # 不是str類型時,主動抛出異常
self.__sex = value # 類型正确的時候,直接修改__sex的值,這是值正真存放的地方
# 這裡sex前加"__",對sex變形,隐藏。
@sex.deleter # 删除sex
def sex(self):
del self.__sex
p1 = people("張三", "男") # 執行個體化對象p1
print(p1.sex) # 檢視p1的sex,此時要注意self.sex的優先級
p1.sex = "女" # 修改sex的值
print(p1.sex) # 檢視修改後p1的sex
print(p1.__dict__) # 檢視p1的名稱空間,此時裡面有sex
del p1.sex # 删除p1的sex
print(p1.__dict__) # 檢視p1的名稱空間,此時發現裡面已經沒有sex了
結果:
男
女
{'name': '張三', '_people__sex': '女'}
{'name': '張三'}
Process finished with exit code 0