面向對象編程
類和實例
類是創建實例的模板,而實例則是一個一個具體的對象,各個實例擁有的數據都互相獨立,互不影響;
方法就是與實例綁定的函數,和普通函數不同,方法可以直接訪問實例的數據;
通過在實例上調用方法,我們就直接操作了對象內部的數據,但無需知道方法內部的實現細節。
和靜態語言不同,Python允許對實例變量綁定任何數據,也就是說,對于兩個實例變量,雖然它們都是同一個類的不同實例,但擁有的變量名稱都可能不同
訪問權限
如果要讓內部屬性不被外部訪問,可以把屬性的名稱前加上兩個下劃線__
,在Python中,實例的變量名如果以__
開頭,就變成了一個私有變量(private),只有內部可以訪問,外部不能訪問
以之前的Student類,name score 屬性為例:
如果外部代碼要獲取name和score怎么辦?可以給Student類增加get_name
和get_score
這樣的方法:
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
如果又要允許外部代碼修改score怎么辦?可以再給Student類增加set_score
方法:
class Student(object):
...
def set_score(self, score):
self.__score = score
在Python中,變量名類似__xxx__
的,也就是以雙下劃線開頭,并且以雙下劃線結尾的,是特殊變量,特殊變量是可以直接訪問的
__xxx
是私有變量,_xxx
是公有變量,但約定俗成,最好當成私有變量,不要隨意訪問
獲取對象信息
type()
isinstance()
用在class判斷繼承關系上方便
dir()
獲得一個對象的所有屬性和方法,可以使用dir()
函數,它返回一個包含字符串的list
getattr()
>>> getattr(obj, 'y') # 獲取屬性'y'
19
setattr()
>>> setattr(obj, 'y', 19) # 設置一個屬性'y'
>>> hasattr(obj, 'y') # 有屬性'y'嗎?
True
hasattr()
>>> hasattr(obj, 'x') # 有屬性'x'嗎?
True
通過內置的一系列函數,我們可以對任意一個Python對象進行剖析,拿到其內部的數據。要注意的是,只有在不知道對象信息的時候,我們才會去獲取對象信息。
實例屬性和類屬性
由于Python是動態語言,根據類創建的實例可以任意綁定屬性。
給實例綁定屬性的方法是通過實例變量,或者通過self變量:
class Student(object): # 給實例綁定屬性的方法是通過實例變量,或者通過self變量
def __init__(self, name,score):
self.name = name
self.score = score
s = Student('Bob', 90) # 這里init定義2個參數,所以這里必須輸入2個參數,否則報錯
s.name
# 輸出結果: 'Bob'
s.score
# 輸出結果:90
In [204]: class Student(object):
...: sex = 'girl' # 類屬性
...: def __init__(self, name, score): # 實例屬性
...: self.name = name
...: self.score = score
...:
In [205]: s = Student('lily',99)
In [206]: s.name
Out[206]: 'lily'
In [207]: s.score
Out[207]: 99
In [208]: instance = Student() # 必須傳入2參數
TypeError: __init__() missing 2 required positional arguments: 'name' and 'score'
In [209]: instance = Student('lily',99)
In [210]: instance.sex # sex是類屬性,但該類的實例同樣可以訪問它
Out[210]: 'girl'
但是,如果Student類本身需要綁定一個屬性呢?可以直接在class中定義屬性,這種屬性是類屬性,歸Student類所有:
class Student(object): # 類屬性
name = 'Student'
類屬性雖然歸類所有,但類的所有實例都可以訪問到
>>> class Student(object):
... name = 'Student'
...
>>> s = Student() # 創建實例s
>>> print(s.name) # 打印name屬性,因為實例并沒有name屬性,所以會繼續查找class的name屬性
Student
>>> print(Student.name) # 打印類的name屬性
Student
>>> s.name = 'Michael' # 給實例綁定name屬性
>>> print(s.name) # 由于實例屬性優先級比類屬性高,因此,它會屏蔽掉類的name屬性
Michael
>>> print(Student.name) # 但是類屬性并未消失,用Student.name仍然可以訪問
Student
>>> del s.name # 如果刪除實例的name屬性
>>> print(s.name) # 再次調用s.name,由于實例的name屬性沒有找到,類的name屬性就顯示出來了
Student
??注意:從上面的例子可以看出,在編寫程序的時候,千萬不要把實例屬性和類屬性使用相同的名字,因為相同名稱的實例屬性將屏蔽掉類屬性,但是當你刪除實例屬性后,再使用相同的名稱,訪問到的將是類屬性
對類 子類 繼承 類屬性 類方法 的實驗
class Model(object):
def __init__(self, high, sex):
self.high = high
self.sex = sex
weight = 100 # 類屬性
def love(self,x): # 實例屬性
print(x, ': i love you~')
class Man(Model):
def __init__(self, age):
self.age = age
state = 'china' # 類屬性
def miss(self,y): # 實例屬性
print(y, ': i miss you much~')
def what_the_fuck(z):
print(z.love('kobe'), ':tell me duck type, what the fuck~')
# 測試 Model
test = Model(180, 'male') # test 為Model類的實例
test1 = Model(high = 180, sex = 'male') # 在ORM章節,定義有這樣的參數輸入,本質等價
print( test,test1 ) # 結果一樣: <__main__.Model object at 0x1070609b0> #
print( test.high, test.sex ) # 結果: 180 male #
print( Model.weight, test.weight ) # 結果: 100 100 # weight是類屬性,所以類與實例均可調用
print( test.love('lily') ) # 結果: lily : i love you~ # 其他方式均報錯
# 測試 Man,繼承自 Model
jack = Man(18) # 只能輸入一個age參數,父類的 high sex 不影響它
# ??除非用 super()方法去繼承,這樣父類里的__init__(self, *args,**kwargs)才能被繼承作為子類生成實例時必須輸入的參數
print( jack, jack.age, jack.miss('lily'), jack.love('lily') ) # 運行正常,第一個提示為對象;可識別父類的 love 方法
print( jack.high ) # 報錯,不識別父類的 high
print( jack.state, Man.state, jack.weight, Man.weight ) # 結果 china china 100 100 # 也是就是說子類的類和實例 都 可以繼承父類的類屬性
# 測試鴨子類型
z = Man(18)
print( what_the_fuck(z) ) # 結果: kobe : i love you~ kobe : i love you~ # 這就是鴨子類型,把類實例作為函數的參數,函數中正好有該類實例的自身的函數,可自動識別