一文看懂Python面向?qū)ο缶幊?/h1>

原文:一文看懂Python面向?qū)ο缶幊?Python學(xué)習(xí)與新手入門必看)-絕對(duì)原創(chuàng) - 知乎 (zhihu.com)

類(Class)與對(duì)象(Object)

類(Class)是用來描述具有相同屬性(Attribute)和方法(Method)對(duì)象的集合汛聚。對(duì)象(Object)是類(Class)的具體實(shí)例脐供。比如學(xué)生都有名字和分?jǐn)?shù),他們有著共同的屬性谚攒。這時(shí)我們就可以設(shè)計(jì)一個(gè)學(xué)生類, 用于記錄學(xué)生的名字和分?jǐn)?shù),并自定義方法打印出他們的名字和方法。

屬性(Attribute): 類里面用于描述所有對(duì)象共同特征的變量或數(shù)據(jù)。比如學(xué)生的名字和分?jǐn)?shù)眠副。
方法(Method): 類里面的函數(shù),用來區(qū)別類外面的函數(shù), 用來實(shí)現(xiàn)某些功能竣稽。比如打印出學(xué)生的名字和分?jǐn)?shù)囱怕。
要?jiǎng)?chuàng)建一個(gè)類我們需要使用關(guān)鍵詞class. 這個(gè)學(xué)生類Student看上去應(yīng)該是這樣的:

創(chuàng)建一個(gè)學(xué)生類

class Student:

# 定義學(xué)生屬性,初始化方法
def __init__(self, name, score):
    self.name = name
    self.score = score

# 定義打印學(xué)生信息的方法
def show(self):
    print("Name: {}. Score: {}".format(self.name, self.score))

在這個(gè)案例中毫别,我們只定義了一個(gè)抽象的類娃弓,電腦并沒有創(chuàng)建什么存儲(chǔ)空間。只有當(dāng)我們完成類的實(shí)例化(Instance)時(shí)岛宦,電腦才會(huì)創(chuàng)建一個(gè)具體的對(duì)象(Object)台丛,并為之分配存儲(chǔ)空間。所以對(duì)象(Object)是類(Class)的一個(gè)實(shí)例砾肺。

要?jiǎng)?chuàng)建一個(gè)具體的學(xué)生對(duì)象(Object)挽霉,我們還需要輸入:

student1 = Student("John", 100)
student2 = Student("Lucy", 99)
在這個(gè)案例中防嗡,Student是類,student1和student2是我們創(chuàng)建的具體的學(xué)生對(duì)象侠坎。當(dāng)我們輸入上述代碼時(shí)蚁趁,Python會(huì)自動(dòng)調(diào)用默認(rèn)的init初始構(gòu)造函數(shù)來生成具體的對(duì)象。關(guān)鍵字self是個(gè)非常重要的參數(shù)硅蹦,代表創(chuàng)建的對(duì)象本身荣德。

當(dāng)你創(chuàng)建具體的對(duì)象后,你可以直接通過student1.name和student1.score來分別獲取學(xué)生的名字和分?jǐn)?shù)童芹,也可以通過student1.show()來直接打印學(xué)生的名字和分?jǐn)?shù)。

類變量(class variables)與實(shí)例變量(instance variables)

假設(shè)我們需要在Student類里增加一個(gè)計(jì)數(shù)器number鲤拿,每當(dāng)一個(gè)新的學(xué)生對(duì)象(Object)被創(chuàng)建時(shí)假褪,這個(gè)計(jì)數(shù)器就自動(dòng)加1。由于這個(gè)計(jì)數(shù)器不屬于某個(gè)具體學(xué)生近顷,而屬于Student類的生音,所以被稱為類變量(class variables)。而姓名和分?jǐn)?shù)屬于每個(gè)學(xué)生對(duì)象的窒升,所以屬于實(shí)例變量(instance variables)缀遍,也被稱為對(duì)象變量(object variables)。

這個(gè)新Student類看上去應(yīng)該是這樣的:

創(chuàng)建一個(gè)學(xué)生類

class Student:

# number屬于類變量饱须,定義在方法外域醇,不屬于具體實(shí)例
number = 0

# 定義學(xué)生屬性,初始化方法
# name和score屬于實(shí)例變量蓉媳,定義在方法里
def __init__(self, name, score):
    self.name = name
    self.score = score
    # 此處有錯(cuò)誤
    number = number + 1

# 定義打印學(xué)生信息的方法
def show(self):
    print("Name: {}. Score: {}".format(self.name, self.score))

類變量和實(shí)例變量的區(qū)別很大譬挚,訪問方式也也不一樣。

類變量:類變量在整個(gè)實(shí)例化的對(duì)象中是公用的酪呻。類變量定義在類中且在函數(shù)體之外减宣。訪問或調(diào)用類變量的正確方式是類名.變量名或者self.class.變量名。self.class自動(dòng)返回每個(gè)對(duì)象的類名玩荠。
實(shí)例變量:定義在方法中的變量漆腌,屬于某個(gè)具體的對(duì)象。訪問或調(diào)用實(shí)例變量的正確方式是對(duì)象名.變量名或者self.變量名.

注意到上述Student類有個(gè)錯(cuò)誤沒? 我們?cè)噲D直接使用number = number + 1調(diào)用屬于類的變量number阶冈。正確的方式是使用Student.number或self.class.number訪問屬于類的變量闷尿。下面的代碼才是正確的:

創(chuàng)建一個(gè)學(xué)生類

class Student:

# number屬于類變量,不屬于某個(gè)具體的學(xué)生實(shí)例
number = 0

# 定義學(xué)生屬性眼溶,初始化方法
# name和score屬于實(shí)例變量
def __init__(self, name, score):
    self.name = name
    self.score = score
    Student.number = Student.number + 1

# 定義打印學(xué)生信息的方法
def show(self):
    print("Name: {}. Score: {}".format(self.name, self.score))

實(shí)例化悠砚,創(chuàng)建對(duì)象

student1 = Student("John", 100)
student2 = Student("Lucy", 99)

print(Student.number) # 打印2
print(student1.class.number) # 打印2

類方法(Class method)

正如同有些變量只屬于類,有些方法也只屬于類堂飞,不屬于具體的對(duì)象灌旧。你有沒有注意到屬于對(duì)象的方法里面都有一個(gè)self參數(shù), 比如init(self), show(self)? self是指對(duì)象本身绑咱。屬于類的方法不使用self參數(shù), 而使用參數(shù)cls枢泰,代表類本身描融。另外習(xí)慣上對(duì)類方法我們會(huì)加上@classmethod的修飾符做說明。

同樣拿Student為例子衡蚂,我們不用print函數(shù)打印出已創(chuàng)建學(xué)生對(duì)象的數(shù)量窿克,而是自定義一個(gè)類方法來打印,我們可以這么做:

class Student:

# number屬于類變量毛甲,不屬于某個(gè)具體的學(xué)生實(shí)例
number = 0

# 定義學(xué)生屬性年叮,初始化方法
# name和score屬于實(shí)例變量
def __init__(self, name, score):
    self.name = name
    self.score = score
    Student.number = Student.number + 1

# 定義打印學(xué)生信息的方法
def show(self):
    print("Name: {}. Score: {}".format(self.name, self.score))

# 定義類方法,打印學(xué)生的數(shù)量
@classmethod
def total(cls):
    print("Total: {0}".format(cls.number))

實(shí)例化玻募,創(chuàng)建對(duì)象

student1 = Student("John", 100)
student2 = Student("Lucy", 99)

Student.total() # 打印 Total: 2

類的私有屬性(private attribute)和私有方法(private method)

類里面的私有屬性和私有方法以雙下劃線__開頭只损。私有屬性或方法不能在類的外部被使用或直接訪問。我們同樣看看學(xué)生類這個(gè)例子七咧,把分?jǐn)?shù)score變?yōu)樗接袑傩栽颈梗纯磿?huì)發(fā)生什么。

創(chuàng)建一個(gè)學(xué)生類

class Student:

# 定義學(xué)生屬性艾栋,初始化方法
# name和score屬于實(shí)例變量, 其中__score屬于私有變量
def __init__(self, name, score):
    self.name = name
    self.__score = score

# 定義打印學(xué)生信息的方法
def show(self):
    print("Name: {}. Score: {}".format(self.name, self.__score))

實(shí)例化爆存,創(chuàng)建對(duì)象

student1 = Student("John", 100)

student1.show() # 打印 Name: John, Score: 100
student1.__score # 打印出錯(cuò),該屬性不能從外部訪問蝗砾。

如果你將score變成__score, 你將不能直接通過student1.__score獲取該學(xué)生的分?jǐn)?shù)先较。show()可以正常顯示分?jǐn)?shù),是因?yàn)樗穷惱锩娴暮瘮?shù)遥诉,可以訪問私有變量拇泣。

私有方法是同樣的道理。當(dāng)我們把show()變成矮锈,__show()你將不能再通過student1.__show()打印出學(xué)生的名字和分?jǐn)?shù)霉翔。值得注意的是私有方法必需含有self這個(gè)參數(shù),且把它作為第一個(gè)參數(shù)苞笨。

在面向?qū)ο蟮木幊讨?通常情況下很少讓外部類直接訪問類內(nèi)部的屬性和方法债朵,而是向外部類提供一些按鈕,對(duì)其內(nèi)部的成員進(jìn)行訪問,以保證程序的安全性,這就是封裝瀑凝。

@property的用法與神奇之處

在上述案例中用戶不能用student1.__score方式訪問學(xué)生分?jǐn)?shù)序芦,然而用戶也就知道了__score是個(gè)私有變量。我們有沒有一種方法讓用戶通過student1.score來訪問學(xué)生分?jǐn)?shù)而繼續(xù)保持__score私有變量的屬性呢粤咪?這時(shí)我們就可以借助python的@property裝飾器了谚中。我們可以先定義一個(gè)方法score(), 然后利用@property把這個(gè)函數(shù)偽裝成屬性。見下面例子:

創(chuàng)建一個(gè)學(xué)生類

class Student:

# 定義學(xué)生屬性,初始化方法
# name和score屬于實(shí)例變量, 其中score屬于私有變量
def __init__(self, name, score):
    self.name = name
    self.__score = score

# 利用property裝飾器把函數(shù)偽裝成屬性
@property
def score(self):
    print("Name: {}. Score: {}".format(self.name, self.__score))

實(shí)例化宪塔,創(chuàng)建對(duì)象

student1 = Student("John", 100)

student1.score # 打印 Name: John. Score: 100

注意: 一旦給函數(shù)加上一個(gè)裝飾器@property,調(diào)用函數(shù)的時(shí)候不用加括號(hào)就可以直接調(diào)用函數(shù)了

類的繼承(Inheritance)

面向?qū)ο蟮木幊處淼淖畲蠛锰幹痪褪谴a的重用磁奖,實(shí)現(xiàn)這種重用的方法之一是通過繼承(Inheritance)。你可以先定義一個(gè)基類(Base class)或父類(Parent class)某筐,再按通過class 子類名(父類名)來創(chuàng)建子類(Child class)比搭。這樣子類就可以從父類那里獲得其已有的屬性與方法,這種現(xiàn)象叫做類的繼承南誊。

我們?cè)倏戳硪粋€(gè)例子身诺,老師和學(xué)生同屬學(xué)校成員,都有姓名和年齡的屬性抄囚,然而老師有工資這個(gè)專有屬性霉赡,學(xué)生有分?jǐn)?shù)這個(gè)專有屬性。這時(shí)我們就可以定義1一個(gè)學(xué)校成員父類怠苔,2個(gè)子類同廉。

創(chuàng)建父類學(xué)校成員SchoolMember

class SchoolMember:

def __init__(self, name, age):
    self.name = name
    self.age = age

def tell(self):
    # 打印個(gè)人信息
    print('Name:"{}" Age:"{}"'.format(self.name, self.age), end=" ")

創(chuàng)建子類老師 Teacher

class Teacher(SchoolMember):

def __init__(self, name, age, salary):
    SchoolMember.__init__(self, name, age) # 利用父類進(jìn)行初始化
    self.salary = salary

# 方法重寫
def tell(self):
    SchoolMember.tell(self)
    print('Salary: {}'.format(self.salary))

創(chuàng)建子類學(xué)生Student

class Student(SchoolMember):

def __init__(self, name, age, score):
    SchoolMember.__init__(self, name, age)
    self.score = score

def tell(self):
    SchoolMember.tell(self)
    print('score: {}'.format(self.score))

teacher1 = Teacher("John", 44, "$60000")
student1 = Student("Mary", 12, 99)

teacher1.tell() # 打印 Name:"John" Age:"44" Salary: $60000
student1.tell() # Name:"Mary" Age:"12" score: 99

上述代碼中,你注意到以下幾點(diǎn)了嗎柑司?

在創(chuàng)建子類的過程中,你需要手動(dòng)調(diào)用父類的構(gòu)造函數(shù)init來完成子類的構(gòu)造锅劝。
在子類中調(diào)用父類的方法時(shí)攒驰,需要加上父類的類名前綴,且需要帶上self參數(shù)變量故爵。比如SchoolMember.tell(self), 這個(gè)可以通過使用super關(guān)鍵詞簡(jiǎn)化代碼玻粪。
如果子類調(diào)用了某個(gè)方法(如tell())或?qū)傩裕琍ython會(huì)先在子類中找诬垂,如果找到了會(huì)直接調(diào)用劲室。如果找不到才會(huì)去父類找。這為方法重寫帶來了便利结窘。

實(shí)際Python編程過程中很洋,一個(gè)子類可以繼承多個(gè)父類,原理是一樣的隧枫。第一步總是要手動(dòng)調(diào)用init構(gòu)造函數(shù)喉磁。

super()關(guān)鍵字調(diào)用父類方法

在子類當(dāng)中可以通過使用super關(guān)鍵字來直接調(diào)用父類的中相應(yīng)的方法,簡(jiǎn)化代碼官脓。在下面例子中协怒,學(xué)生子類調(diào)用了父類的tell()方法。super().tell()等同于SchoolMember.tell(self)卑笨。當(dāng)你使用Python super()關(guān)鍵字調(diào)用父類方法時(shí)時(shí)孕暇,注意去掉括號(hào)里self這個(gè)參數(shù)。

創(chuàng)建子類學(xué)生Student

class Student(SchoolMember):

def __init__(self, name, age, score):
    SchoolMember.__init__(self, name, age)
    self.score = score

def tell(self):
    super().tell() # 等同于 SchoolMember.tell(self)
    print('score: {}'.format(self.score))
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市妖滔,隨后出現(xiàn)的幾起案子隧哮,更是在濱河造成了極大的恐慌,老刑警劉巖铛楣,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件近迁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡簸州,警方通過查閱死者的電腦和手機(jī)鉴竭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岸浑,“玉大人搏存,你說我怎么就攤上這事∈钢蓿” “怎么了璧眠?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長读虏。 經(jīng)常有香客問我责静,道長,這世上最難降的妖魔是什么盖桥? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任灾螃,我火速辦了婚禮,結(jié)果婚禮上揩徊,老公的妹妹穿的比我還像新娘腰鬼。我一直安慰自己,他們只是感情好塑荒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布熄赡。 她就那樣靜靜地躺著,像睡著了一般齿税。 火紅的嫁衣襯著肌膚如雪彼硫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天偎窘,我揣著相機(jī)與錄音乌助,去河邊找鬼。 笑死陌知,一個(gè)胖子當(dāng)著我的面吹牛他托,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播仆葡,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼赏参,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼志笼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起把篓,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤纫溃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后韧掩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體紊浩,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年疗锐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坊谁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡滑臊,死狀恐怖口芍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情雇卷,我是刑警寧澤鬓椭,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站关划,受9級(jí)特大地震影響小染,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贮折,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一氧映、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脱货,春花似錦、人聲如沸律姨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽择份。三九已至扣孟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荣赶,已是汗流浹背凤价。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拔创,地道東北人利诺。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像剩燥,于是被迫代替她去往敵國和親慢逾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容