一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

從零學(xué)Python:第十六課-面向?qū)ο缶幊倘腴T(mén)

 千鋒Python學(xué)堂 2020-07-15

面向?qū)ο缶幊淌且环N非常流行的編程范式(programming paradigm),所謂編程范式就是程序設(shè)計(jì)的方法學(xué),也就是程序員對(duì)程序的認(rèn)知和理解。

前面的課程中我們說(shuō)過(guò)“程序是指令的集合”,運(yùn)行程序時(shí),程序中的語(yǔ)句會(huì)變成一條或多條指令,然后由CPU(中央處理器)去執(zhí)行。為了簡(jiǎn)化程序的設(shè)計(jì),我們又講到了函數(shù),把相對(duì)獨(dú)立且經(jīng)常重復(fù)使用的代碼放置到函數(shù)中,在需要使用這些代碼的時(shí)候調(diào)用函數(shù)即可。如果一個(gè)函數(shù)的功能過(guò)于復(fù)雜和臃腫,我們又可以進(jìn)一步將函數(shù)進(jìn)一步拆分為多個(gè)子函數(shù)來(lái)降低系統(tǒng)的復(fù)雜性。

不知大家是否發(fā)現(xiàn),我們所謂的編程其實(shí)是寫(xiě)程序的人按照計(jì)算機(jī)的工作方式通過(guò)代碼控制機(jī)器完成任務(wù)。但是,計(jì)算機(jī)的工作方式與人類正常的思維模式是不同的,如果編程就必須拋棄人類正常的思維方式去迎合計(jì)算機(jī),編程的樂(lè)趣就少了很多,而“每個(gè)人都應(yīng)該學(xué)習(xí)編程”這樣的豪言壯語(yǔ)也就只能喊喊口號(hào)而已。不是說(shuō)我們不能按照計(jì)算機(jī)的工作方式去編寫(xiě)代碼,但是當(dāng)我們需要開(kāi)發(fā)一個(gè)復(fù)雜的系統(tǒng)時(shí),這種方式會(huì)讓代碼過(guò)于復(fù)雜,從而導(dǎo)致開(kāi)發(fā)和維護(hù)工作都變得舉步維艱,這也就是上世紀(jì)60年代末,出現(xiàn)了“軟件危機(jī)”、“軟件工程”這些概念的原因。

隨著軟件復(fù)雜性的增加,解決“軟件危機(jī)”就成了軟件開(kāi)發(fā)者必須直面的問(wèn)題。誕生于上世紀(jì)70年代的Smalltalk語(yǔ)言讓軟件開(kāi)發(fā)者看到了希望,因?yàn)樗肓艘环N新的編程范式叫面向?qū)ο缶幊?。在面向?qū)ο缶幊痰氖澜缋?,程序中?span style="-webkit-tap-highlight-color: transparent;box-sizing: border-box;font-weight: 700;margin: 0px;padding: 0px;border: 0px;">數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)是一個(gè)邏輯上的整體,我們稱之為對(duì)象,對(duì)象可以接收消息,解決問(wèn)題的方法就是創(chuàng)建對(duì)象并向?qū)ο蟀l(fā)出各種各樣的消息;通過(guò)消息傳遞,程序中的多個(gè)對(duì)象可以協(xié)同工作,這樣就能構(gòu)造出復(fù)雜的系統(tǒng)并解決現(xiàn)實(shí)中的問(wèn)題。當(dāng)然,面向?qū)ο缶幊痰碾r形還可以向前追溯到更早期的Simula語(yǔ)言,但這不是我們現(xiàn)在要討論的重點(diǎn)。

說(shuō)明: 今天我們使用的很多高級(jí)程序設(shè)計(jì)語(yǔ)言都支持面向?qū)ο缶幊蹋敲嫦驅(qū)ο缶幊桃膊皇墙鉀Q軟件開(kāi)發(fā)中所有問(wèn)題的“銀彈”,或者說(shuō)在軟件開(kāi)發(fā)這個(gè)行業(yè)目前還找不到這種所謂的“銀彈”。

類和對(duì)象

如果要用一句話來(lái)概括面向?qū)ο缶幊?,我認(rèn)為下面的說(shuō)法是相當(dāng)精準(zhǔn)的。

面向?qū)ο缶幊?/span>:把一組數(shù)據(jù)和處理數(shù)據(jù)的方法組成對(duì)象,把行為相同的對(duì)象歸納為,通過(guò)封裝隱藏對(duì)象的內(nèi)部細(xì)節(jié),通過(guò)繼承實(shí)現(xiàn)類的特化和泛化,通過(guò)多態(tài)實(shí)現(xiàn)基于對(duì)象類型的動(dòng)態(tài)分派。

這句話對(duì)初學(xué)者來(lái)說(shuō)可能難以理解,但是我們先為大家圈出幾個(gè)關(guān)鍵詞:對(duì)象(object)、類(class)、封裝(encapsulation)、繼承(inheritance)、多態(tài)(polymorphism)。

我們先說(shuō)說(shuō)類和對(duì)象這兩個(gè)詞。在面向?qū)ο缶幊讨校?span style="-webkit-tap-highlight-color: transparent;box-sizing: border-box;font-weight: 700;margin: 0px;padding: 0px;border: 0px;">類是一個(gè)抽象的概念,對(duì)象是一個(gè)具體的概念。我們把同一類對(duì)象的共同特征抽取出來(lái)就是一個(gè)類,比如我們經(jīng)常說(shuō)的人類,這是一個(gè)抽象概念,而我們每個(gè)人就是人類的這個(gè)抽象概念下的具體的實(shí)實(shí)在在的存在,也就是一個(gè)對(duì)象。簡(jiǎn)而言之,類是對(duì)象的藍(lán)圖和模板,對(duì)象是類的實(shí)例。

在面向?qū)ο缶幊痰氖澜缰校?span style="-webkit-tap-highlight-color: transparent;box-sizing: border-box;font-weight: 700;margin: 0px;padding: 0px;border: 0px;">一切皆為對(duì)象,對(duì)象都有屬性和行為,每個(gè)對(duì)象都是獨(dú)一無(wú)二的,而且對(duì)象一定屬于某個(gè)類。對(duì)象的屬性是對(duì)象的靜態(tài)特征,對(duì)象的行為是對(duì)象的動(dòng)態(tài)特征。按照上面的說(shuō)法,如果我們把擁有共同特征的對(duì)象的屬性和行為都抽取出來(lái),就可以定義出一個(gè)類。

從零學(xué)Python:第十六課-面向?qū)ο缶幊倘腴T(mén)

定義類

在Python中,可以使用class關(guān)鍵字加上類名來(lái)定義類,通過(guò)縮進(jìn)我們可以確定類的代碼塊,就如同定義函數(shù)那樣。在類的代碼塊中,我們需要寫(xiě)一些函數(shù),我們說(shuō)過(guò)類是一個(gè)抽象概念,那么這些函數(shù)就是我們對(duì)一類對(duì)象共同的動(dòng)態(tài)特征的提取。寫(xiě)在類里面的函數(shù)我們通常稱之為方法,方法就是對(duì)象的行為,也就是對(duì)象可以接收的消息。方法的第一個(gè)參數(shù)通常都是self,它代表了接收這個(gè)消息的對(duì)象本身。

class Student:

def study(self, course_name):
print(f'學(xué)生正在學(xué)習(xí){course_name}.')

def play(self):
print(f'學(xué)生正在玩游戲.')

創(chuàng)建和使用對(duì)象

在我們定義好一個(gè)類之后,可以使用構(gòu)造器語(yǔ)法來(lái)創(chuàng)建對(duì)象,代碼如下所示。

stu1 = Student()
stu2 = Student()
print(stu1) # <__main__.Student object at 0x10ad5ac50>
print(stu2) # <__main__.Student object at 0x10ad5acd0>
print(hex(id(stu1)), hex(id(stu2))) # 0x10ad5ac50 0x10ad5acd0

在類的名字后跟上圓括號(hào)就是所謂的構(gòu)造器語(yǔ)法,上面的代碼創(chuàng)建了兩個(gè)學(xué)生對(duì)象,一個(gè)賦值給變量stu1,一個(gè)復(fù)制給變量stu2。當(dāng)我們用print函數(shù)打印stu1和stu2兩個(gè)變量時(shí),我們會(huì)看到輸出了對(duì)象在內(nèi)存中的地址(十六進(jìn)制形式),跟我們用id函數(shù)查看對(duì)象標(biāo)識(shí)獲得的值是相同的。現(xiàn)在我們可以告訴大家,我們定義的變量其實(shí)保存的是一個(gè)對(duì)象在內(nèi)存中的邏輯地址(位置),通過(guò)這個(gè)邏輯地址,我們就可以在內(nèi)存中找到這個(gè)對(duì)象。所以stu3 = stu2這樣的賦值語(yǔ)句并沒(méi)有創(chuàng)建新的對(duì)象,只是用一個(gè)新的變量保存了已有對(duì)象的地址。

接下來(lái),我們嘗試給對(duì)象發(fā)消息,即調(diào)用對(duì)象的方法。剛才的Student類中我們定義了study和play兩個(gè)方法,兩個(gè)方法的第一個(gè)參數(shù)self代表了接收消息的學(xué)生對(duì)象,study方法的第二個(gè)參數(shù)是學(xué)習(xí)的課程名稱。Python中,給對(duì)象發(fā)消息有兩種方式,請(qǐng)看下面的代碼。

# 通過(guò)“類.方法”調(diào)用方法,第一個(gè)參數(shù)是接收消息的對(duì)象,第二個(gè)參數(shù)是學(xué)習(xí)的課程名稱
Student.study(stu1, 'Python程序設(shè)計(jì)') # 學(xué)生正在學(xué)習(xí)Python程序設(shè)計(jì).
# 通過(guò)“對(duì)象.方法”調(diào)用方法,點(diǎn)前面的對(duì)象就是接收消息的對(duì)象,只需要傳入第二個(gè)參數(shù)
stu1.study('Python程序設(shè)計(jì)') # 學(xué)生正在學(xué)習(xí)Python程序設(shè)計(jì).

Student.play(stu2) # 學(xué)生正在玩游戲.
stu2.play() # 學(xué)生正在玩游戲.

初始化方法

大家可能已經(jīng)注意到了,剛才我們創(chuàng)建的學(xué)生對(duì)象只有行為沒(méi)有屬性,如果要給學(xué)生對(duì)象定義屬性,我們可以修改Student類,為其添加一個(gè)名為_(kāi)_init__的方法。在我們調(diào)用Student類的構(gòu)造器創(chuàng)建對(duì)象時(shí),首先會(huì)在內(nèi)存中獲得保存學(xué)生對(duì)象所需的內(nèi)存空間,然后通過(guò)自動(dòng)執(zhí)行__init__方法,完成對(duì)內(nèi)存的初始化操作,也就是把數(shù)據(jù)放到內(nèi)存空間中。所以我們可以通過(guò)給Student類添加__init__方法的方式為學(xué)生對(duì)象指定屬性,同時(shí)完成對(duì)屬性賦初始值的操作,正因如此,__init__方法通常也被稱為初始化方法。

我們對(duì)上面的Student類稍作修改,給學(xué)生對(duì)象添加name(姓名)和age(年齡)兩個(gè)屬性。

class Student:
"""學(xué)生"""

def __init__(self, name, age):
"""初始化方法"""
self.name = name
self.age = age

def study(self, course_name):
"""學(xué)習(xí)"""
print(f'{self.name}正在學(xué)習(xí){course_name}.')

def play(self):
"""玩耍"""
print(f'{self.name}正在玩游戲.')

修改剛才創(chuàng)建對(duì)象和給對(duì)象發(fā)消息的代碼,重新執(zhí)行一次,看看程序的執(zhí)行結(jié)果有什么變化。

# 由于初始化方法除了self之外還有兩個(gè)參數(shù)
# 所以調(diào)用Student類的構(gòu)造器創(chuàng)建對(duì)象時(shí)要傳入這兩個(gè)參數(shù)
stu1 = Student('駱昊', 40)
stu2 = Student('王大錘', 15)
stu1.study('Python程序設(shè)計(jì)') # 駱昊正在學(xué)習(xí)Python程序設(shè)計(jì).
stu2.play() # 王大錘正在玩游戲.

打印對(duì)象

上面我們通過(guò)__init__方法在創(chuàng)建對(duì)象時(shí)為對(duì)象綁定了屬性并賦予了初始值。在Python中,以兩個(gè)下劃線__(讀作“dunder”)開(kāi)頭和結(jié)尾的方法通常都是有特殊用途和意義的方法,我們一般稱之為魔術(shù)方法魔法方法。如果我們?cè)诖蛴?duì)象的時(shí)候不希望看到對(duì)象的地址而是看到我們自定義的信息,可以通過(guò)在類中放置__repr__魔術(shù)方法來(lái)做到,該方法返回的字符串就是用print函數(shù)打印對(duì)象的時(shí)候會(huì)顯示的內(nèi)容,代碼如下所示。

class Student:
"""學(xué)生"""

def __init__(self, name, age):
"""初始化方法"""
self.name = name
self.age = age

def study(self, course_name):
"""學(xué)習(xí)"""
print(f'{self.name}正在學(xué)習(xí){course_name}.')

def play(self):
"""玩耍"""
print(f'{self.name}正在玩游戲.')

def __repr__(self):
return f'{self.name}: {self.age}'


stu1 = Student('駱昊', 40)
print(stu1) # 駱昊: 40
students = [stu1, Student('王小錘', 16), Student('王大錘', 25)]
print(students) # [駱昊: 40, 王小錘: 16, 王大錘: 25]

面向?qū)ο蟮闹е?/h3>

面向?qū)ο缶幊逃腥笾е?,就是我們之前給大家劃重點(diǎn)的時(shí)候圈出的三個(gè)詞:封裝、繼承和多態(tài)。后面兩個(gè)概念在下一節(jié)課中會(huì)詳細(xì)說(shuō)明,這里我們先說(shuō)一下什么是封裝。我自己對(duì)封裝的理解是:隱藏一切可以隱藏的實(shí)現(xiàn)細(xì)節(jié),只向外界暴露簡(jiǎn)單的調(diào)用接口。我們?cè)陬愔卸x的對(duì)象方法其實(shí)就是一種封裝,這種封裝可以讓我們?cè)趧?chuàng)建對(duì)象之后,只需要給對(duì)象發(fā)送一個(gè)消息就可以執(zhí)行方法中的代碼,也就是說(shuō)我們?cè)谥恢婪椒ǖ拿趾蛥?shù)(方法的外部視圖),不知道方法內(nèi)部實(shí)現(xiàn)細(xì)節(jié)(方法的內(nèi)部視圖)的情況下就完成了對(duì)方法的使用。

舉一個(gè)例子,假如要控制一個(gè)機(jī)器人幫我倒杯水,如果不使用面向?qū)ο缶幊?,不做任何的封裝,那么就需要向這個(gè)機(jī)器人發(fā)出一系列的指令,如站起來(lái)、向左轉(zhuǎn)、向前走5步、拿起面前的水杯、向后轉(zhuǎn)、向前走10步、彎腰、放下水杯、按下出水按鈕、等待10秒、松開(kāi)出水按鈕、拿起水杯、向右轉(zhuǎn)、向前走5步、放下水杯等,才能完成這個(gè)簡(jiǎn)單的操作,想想都覺(jué)得麻煩。按照面向?qū)ο缶幊痰乃枷?,我們可以將倒水的操作封裝到機(jī)器人的一個(gè)方法中,當(dāng)需要機(jī)器人幫我們倒水的時(shí)候,只需要向機(jī)器人對(duì)象發(fā)出倒水的消息就可以了,這樣做不是更好嗎?

在很多場(chǎng)景下,面向?qū)ο缶幊唐鋵?shí)就是一個(gè)三步走的問(wèn)題。第一步定義類,第二步創(chuàng)建對(duì)象,第三步給對(duì)象發(fā)消息。當(dāng)然,有的時(shí)候我們是不需要第一步的,因?yàn)槲覀兿胗玫念惪赡芤呀?jīng)存在了。之前我們說(shuō)過(guò),Python內(nèi)置的list、set、dict其實(shí)都不是函數(shù)而是類,如果要?jiǎng)?chuàng)建列表、集合、字典對(duì)象,我們就不用自定義類了。當(dāng)然,有的類并不是Python標(biāo)準(zhǔn)庫(kù)中直接提供的,它可能來(lái)自于第三方的代碼,如何安裝和使用三方代碼在后續(xù)課程中會(huì)進(jìn)行討論。在某些特殊的場(chǎng)景中,我們會(huì)用到名為“內(nèi)置對(duì)象”的對(duì)象,所謂“內(nèi)置對(duì)象”就是說(shuō)上面三步走的第一步和第二步都不需要了,因?yàn)轭愐呀?jīng)存在而且對(duì)象已然創(chuàng)建過(guò)了,直接向?qū)ο蟀l(fā)消息就可以了,這也就是我們常說(shuō)的“開(kāi)箱即用”。

經(jīng)典案例

例子1:定義一個(gè)類描述數(shù)字時(shí)鐘。

import time


# 定義數(shù)字時(shí)鐘類
class Clock(object):
"""數(shù)字時(shí)鐘"""

def __init__(self, hour=0, minute=0, second=0):
"""初始化方法
:param hour: 時(shí)
:param minute: 分
:param second: 秒
"""
self.hour = hour
self.min = minute
self.sec = second

def run(self):
"""走字"""
self.sec += 1
if self.sec == 60:
self.sec = 0
self.min += 1
if self.min == 60:
self.min = 0
self.hour += 1
if self.hour == 24:
self.hour = 0

def show(self):
"""顯示時(shí)間"""
return f'{self.hour:0>2d}:{self.min:0>2d}:{self.sec:0>2d}'


# 創(chuàng)建時(shí)鐘對(duì)象
clock = Clock(23, 59, 58)
while True:
# 給時(shí)鐘對(duì)象發(fā)消息讀取時(shí)間
print(clock.show())
# 休眠1秒鐘
time.sleep(1)
# 給時(shí)鐘對(duì)象發(fā)消息使其走字
clock.run()

例子2:定義一個(gè)類描述平面上的點(diǎn),要求提供計(jì)算到另一個(gè)點(diǎn)距離的方法。

class Point(object):
"""屏面上的點(diǎn)"""

def __init__(self, x=0, y=0):
"""初始化方法
:param x: 橫坐標(biāo)
:param y: 縱坐標(biāo)
"""
self.x, self.y = x, y

def distance_to(self, other):
"""計(jì)算與另一個(gè)點(diǎn)的距離
:param other: 另一個(gè)點(diǎn)
"""
dx = self.x - other.x
dy = self.y - other.y
return (dx * dx + dy * dy) ** 0.5

def __str__(self):
return f'({self.x}, {self.y})'


p1 = Point(3, 5)
p2 = Point(6, 9)
print(p1, p2)
print(p1.distance_to(p2))

簡(jiǎn)單的總結(jié)

面向?qū)ο缶幊淌且环N非常流行的編程范式,除此之外還有指令式編程、函數(shù)式編程等編程范式。由于現(xiàn)實(shí)世界是由對(duì)象構(gòu)成的,而對(duì)象是可以接收消息的實(shí)體,所以面向?qū)ο缶幊谈先祟愓5乃季S習(xí)慣。類是抽象的,對(duì)象是具體的,有了類就能創(chuàng)建對(duì)象,有了對(duì)象就可以接收消息,這就是面向?qū)ο缶幊痰幕A(chǔ)。定義類的過(guò)程是一個(gè)抽象的過(guò)程,找到對(duì)象公共的屬性屬于數(shù)據(jù)抽象,找到對(duì)象公共的方法屬于行為抽象。抽象的過(guò)程是一個(gè)仁者見(jiàn)仁智者見(jiàn)智的過(guò)程,對(duì)同一類對(duì)象進(jìn)行抽象可能會(huì)得到不同的結(jié)果,如下圖所示。

從零學(xué)Python:第十六課-面向?qū)ο缶幊倘腴T(mén)

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    亚洲天堂精品一区二区| 亚洲男人的天堂就去爱| 大香伊蕉欧美一区二区三区| 高清欧美大片免费在线观看| 开心久久综合激情五月天| 91欧美视频在线观看免费| 国产剧情欧美日韩中文在线| 久久国产成人精品国产成人亚洲| 色丁香之五月婷婷开心| 亚洲三级视频在线观看免费| 国产黑人一区二区三区| 日本不卡在线一区二区三区| 午夜福利视频偷拍91| 99热九九在线中文字幕| 真实偷拍一区二区免费视频| 亚洲精品国产第一区二区多人| 亚洲欧美日本国产有色| 中文字幕中文字幕在线十八区| 美国欧洲日本韩国二本道| 大香蕉久久精品一区二区字幕| 亚洲国产欧美久久精品| 成人免费视频免费观看| 亚洲中文在线中文字幕91| 加勒比人妻精品一区二区| 国内欲色一区二区三区| 久久91精品国产亚洲| 在线亚洲成人中文字幕高清| 国产午夜福利在线观看精品| 黄片免费播放一区二区| 国产又粗又爽又猛又黄的| 99久久免费看国产精品| 午夜福利大片亚洲一区| 亚洲av秘片一区二区三区| 日本婷婷色大香蕉视频在线观看 | 国产成人精品午夜福利| 国产精品大秀视频日韩精品| 黄片在线观看一区二区三区| 日本一区不卡在线观看| 九九热视频网在线观看| 国内精品伊人久久久av高清| 亚洲中文字幕在线综合视频|