一. 細分類的組成成員
之前咱們講過類大緻分兩塊區域,如下圖所示:

每個區域詳細劃分又可以分為:
class A:
company_name = '老男孩教育' # 靜态變量(靜态字段)
__iphone = '1353333xxxx' # 私有靜态變量(私有靜态字段)
def __init__(self,name,age): #特殊方法
self.name = name #對象屬性(普通字段)
self.__age = age # 私有對象屬性(私有普通字段)
def func1(self): # 普通方法
pass
def __func(self): #私有方法
print(666)
@classmethod # 類方法
def class_func(cls):
""" 定義類方法,至少有一個cls參數 """
print('類方法')
@staticmethod #靜态方法
def static_func():
""" 定義靜态方法 ,無預設參數"""
print('靜态方法')
@property # 屬性
def prop(self):
pass
二. 類的私有成員
對于每一個類的成員而言都有兩種形式:
- 公有成員,在任何地方都能通路
- 私有成員,隻有在類的内部才能方法
私有成員和公有成員的通路限制不同:
靜态字段(靜态屬性)
- 公有靜态字段:類可以通路;類内部可以通路;派生類中可以通路
- 私有靜态字段:僅類内部可以通路;
class C:
name = "公有靜态字段"
def func(self):
print C.name
class D(C):
def show(self):
print C.name
C.name # 類通路
obj = C()
obj.func() # 類内部可以通路
obj_son = D()
obj_son.show() # 派生類中可以通路
公有靜态字段
公有靜态屬性(字段)
class C:
__name = "私有靜态字段"
def func(self):
print C.__name
class D(C):
def show(self):
print C.__name
C.__name # 不可在外部通路
obj = C()
obj.__name # 不可在外部通路
obj.func() # 類内部可以通路
obj_son = D()
obj_son.show() #不可在派生類中可以通路
私有靜态字段
私有靜态屬性(字段)
普通字段(對象屬性)
- 公有普通字段:對象可以通路;類内部可以通路;派生類中可以通路
- 私有普通字段:僅類内部可以通路;
class C:
def __init__(self):
self.foo = "公有字段"
def func(self):
print self.foo # 類内部通路
class D(C):
def show(self):
print self.foo # 派生類中通路
obj = C()
obj.foo # 通過對象通路
obj.func() # 類内部通路
obj_son = D();
obj_son.show() # 派生類中通路
公有普通字段
公有對象屬性
class C:
def __init__(self):
self.__foo = "私有字段"
def func(self):
print self.foo # 類内部通路
class D(C):
def show(self):
print self.foo # 派生類中通路
obj = C()
obj.__foo # 通過對象通路 ==> 錯誤
obj.func() # 類内部通路 ==> 正确
obj_son = D();
obj_son.show() # 派生類中通路 ==> 錯誤
私有普通字段
私有對象屬性
方法:
- 公有方法:對象可以通路;類内部可以通路;派生類中可以通路
- 私有方法:僅類内部可以通路;
class C:
def __init__(self):
pass
def add(self):
print('in C')
class D(C):
def show(self):
print('in D')
def func(self):
self.show()
obj = D()
obj.show() # 通過對象通路
obj.func() # 類内部通路
obj.add() # 派生類中通路
公有方法
公有方法
class C:
def __init__(self):
pass
def __add(self):
print('in C')
class D(C):
def __show(self):
print('in D')
def func(self):
self.__show()
obj = D()
obj.__show() # 通過不能對象通路
obj.func() # 類内部可以通路
obj.__add() # 派生類中不能通路
私有方法
總結:
對于這些私有成員來說,他們隻能在類的内部使用,不能再類的外部以及派生類中使用.
*ps:非要通路私有成員的話,可以通過 對象._類__屬性名,但是絕對不允許!!!*
*為什麼可以通過._類__私有成員名通路呢?因為類在建立時,如果遇到了私有成員(包括私有靜态字段,私有普通字段,私有方法)它會将其儲存在記憶體時自動在前面加上_類名.*
三. 類的其他成員
這裡的其他成員主要就是類方法:
方法包括:普通方法、靜态方法和類方法,三種方法在記憶體中都歸屬于類,差別在于調用方式不同。
執行個體方法
定義:第一個參數必須是執行個體對象,該參數名一般約定為“self”,通過它來傳遞執行個體的屬性和方法(也可以傳類的屬性和方法);
調用:隻能由執行個體對象調用。
類方法
定義:使用裝飾器@classmethod。第一個參數必須是目前類對象,該參數名一般約定為“cls”,通過它來傳遞類的屬性和方法(不能傳執行個體的屬性和方法);
調用:執行個體對象和類對象都可以調用。
靜态方法
定義:使用裝飾器@staticmethod。參數随意,沒有“self”和“cls”參數,但是方法體中不能使用類或執行個體的任何屬性和方法;
調用:執行個體對象和類對象都可以調用。
雙下方法(後面會講到)
定義:雙下方法是特殊方法,他是解釋器提供的 由爽下劃線加方法名加爽下劃線 __方法名__的具有特殊意義的方法,雙下方法主要是python源碼程式員使用的,
我們在開發中盡量不要使用雙下方法,但是深入研究雙下方法,更有益于我們閱讀源碼。
調用:不同的雙下方法有不同的觸發方式,就好比盜墓時觸發的機關一樣,不知不覺就觸發了雙下方法,例如:init
執行個體方法
簡而言之,執行個體方法就是類的執行個體能夠使用的方法。這裡不做過多解釋。
3.1 類方法
使用裝飾器@classmethod。
原則上,類方法是将類本身作為對象進行操作的方法。假設有個方法,且這個方法在邏輯上采用類本身作為對象來調用更合理,那麼這個方法就可以定義為類方法。另外,如果需要繼承,也可以定義為類方法。
如下場景:
假設我有一個學生類和一個班級類,想要實作的功能為:
執行班級人數增加的操作、獲得班級的總人數;
學生類繼承自班級類,每執行個體化一個學生,班級人數都能增加;
最後,我想定義一些學生,獲得班級中的總人數。
思考:這個問題用類方法做比較合适,為什麼?因為我執行個體化的是學生,但是如果我從學生這一個執行個體中獲得班級總人數,在邏輯上顯然是不合理的。同時,如果想要獲得班級總人數,如果生成一個班級的執行個體也是沒有必要的。
class Student:
__num = 0
def __init__(self,name,age):
self.name = name
self.age= age
Student.addNum() # 寫在__new__方法中比較合适,但是現在還沒有學,暫且放到這裡
@classmethod
def addNum(cls):
cls.__num += 1
@classmethod
def getNum(cls):
return cls.__num
a = Student('太白金星', 18)
b = Student('武sir', 36)
c = Student('alex', 73)
print(Student.getNum())
3.2 靜态方法
使用裝飾器@staticmethod。
靜态方法是類中的函數,不需要執行個體。靜态方法主要是用來存放邏輯性的代碼,邏輯上屬于類,但是和類本身沒有關系,也就是說在靜态方法中,不會涉及到類中的屬性和方法的操作。可以了解為,靜态方法是個獨立的、單純的函數,它僅僅托管于某個類的名稱空間中,便于使用和維護。
譬如,我想定義一個關于時間操作的類,其中有一個擷取目前時間的函數。
import time
class TimeTest(object):
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
@staticmethod
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
print(TimeTest.showTime())
t = TimeTest(2, 10, 10)
nowTime = t.showTime()
print(nowTime)
如上,使用了靜态方法(函數),然而方法體中并沒使用(也不能使用)類或執行個體的屬性(或方法)。若要獲得目前時間的字元串時,并不一定需要執行個體化對象,此時對于靜态方法而言,所在類更像是一種名稱空間。
其實,我們也可以在類外面寫一個同樣的函數來做這些事,但是這樣做就打亂了邏輯關系,也會導緻以後代碼維護困難。
3.3 屬性
什麼是特性property
property是一種特殊的屬性,通路它時會執行一段功能(函數)然後傳回值
例一:BMI指數(bmi是計算而來的,但很明顯它聽起來像是一個屬性而非方法,如果我們将其做成一個屬性,更便于了解)
成人的BMI數值:
過輕:低于18.5
正常:18.5-23.9
過重:24-27
肥胖:28-32
非常肥胖, 高于32
體質指數(BMI)=體重(kg)÷身高^2(m)
EX: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('egon',75,1.85)
print(p1.bmi)
例一代碼
View Code
為什麼要用property
将一個類的函數定義成特性以後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,這種特性的使用方式遵循了統一通路的原則
**由于新式類中具有三種通路方式,我們可以根據他們幾個屬性的通路特點,分别将三個方法定義為對同一個屬性:擷取、修改、删除
class Foo:
@property
def AAA(self):
print('get的時候運作我啊')
@AAA.setter
def AAA(self,value):
print('set的時候運作我啊')
@AAA.deleter
def AAA(self):
print('delete的時候運作我啊')
#隻有在屬性AAA定義property後才能定義AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
或者:
class Foo:
def get_AAA(self):
print('get的時候運作我啊')
def set_AAA(self,value):
print('set的時候運作我啊')
def delete_AAA(self):
print('delete的時候運作我啊')
AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三個參數與get,set,delete一一對應
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
View Code
class Goods(object):
def __init__(self):
# 原價
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 實際價格 = 原價 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deltter
def price(self, value):
del self.original_price
obj = Goods()
obj.price # 擷取商品價格
obj.price = 200 # 修改商品原價
del obj.price # 删除商品原價
商品執行個體
商品示例
四. isinstace 與 issubclass
class A:
pass
class B(A):
pass
obj = B()
print(isinstance(obj,B))
print(isinstance(obj,A))
isinstance
isinstance(a,b):判斷a是否是b類(或者b類的派生類)執行個體化的對象
class A:
pass
class B(A):
pass
class C(B):
pass
print(issubclass(B,A))
print(issubclass(C,A))
issubclass
issubclass(a,b): 判斷a類是否是b類(或者b的派生類)的派生類
思考:那麼 list str tuple dict等這些類與 Iterble類 的關系是什麼?
from collections import Iterable
print(isinstance([1,2,3], list)) # True
print(isinstance([1,2,3], Iterable)) # True
print(issubclass(list,Iterable)) # True
# 由上面的例子可得,這些可疊代的資料類型,list str tuple dict等 都是 Iterable的子類。
View Code
課外了解:元類type。
按照Python的一切皆對象理論,類其實也是一個對象,那麼類這個對象是從哪裡執行個體化出來的呢?
print(type('abc'))
print(type(True))
print(type(100))
print(type([1, 2, 3]))
print(type({'name': '太白金星'}))
print(type((1,2,3)))
print(type(object))
class A:
pass
print(isinstance(object,type))
print(isinstance(A, type))
View Code
type元類是擷取該對象從屬于的類,而type類比較特殊,Python原則是:一切皆對象,其實類也可以了解為'對象',而type元類又稱作建構類,python中大多數内置的類(包括object)以及自己定義的類,都是由type元類創造的。
* 而type類與object類之間的關系比較獨特:object是type類的執行個體,而type類是object類的子類,這種關系比較神奇無法使用python的代碼表述,因為定義其中一個之前另一個必須存在。是以這個隻作為了解。