Python中類與對象1
類與對象
類
- 類(class)呢簸,比如整數(shù)矮台、字符串、浮點(diǎn)數(shù)等根时,不同的數(shù)據(jù)類型就屬于不同的類瘦赫。它們的全名是整數(shù)類、字符串類蛤迎、浮點(diǎn)數(shù)類确虱。我們可以用type()函數(shù)來驗(yàn)證。
print(type('1')) # '1'屬于字符串類'str'
print(type(1)) # 1屬于整數(shù)類'int'
print(type([1])) # [1]屬于列表類'list'
- 在Python的術(shù)語里替裆,我們把類的個(gè)例就叫做實(shí)例 (instance)校辩,可理解為“實(shí)際的例子”窘问。
- 類是某個(gè)特定的群體,實(shí)例是群體中某個(gè)具體的個(gè)體宜咒。類似于:群體和個(gè)體惠赫。群體里的每個(gè)個(gè)體都有著相同/相似的特征和行為。
對象
編程中的對象(object)故黑,等于類和實(shí)例的集合:即類可以看作是對象儿咱,實(shí)例也可以看作是對象,比如列表list是個(gè)類對象场晶,[1,2]是個(gè)實(shí)例對象混埠,它們都是對象。
類的創(chuàng)建和調(diào)用
屬性和方法
-
在編程中钳宪,我們要了解屬性(what)和方法(how)
- 屬性描述事物是怎樣的
- 方法描述事務(wù)能做什么
比如列表的屬性有:外層有中括號,元素之間用英文逗號隔開扳炬,方法有:都可以做增刪改操作(如 append使套、del等)
Python里的每個(gè)類都有自己獨(dú)特的屬性(attribute)和方法(method),是這個(gè)類的所有實(shí)例都共享的鞠柄。換言之侦高,每個(gè)實(shí)例都可以調(diào)用類中所有的屬性和方法。
-
編程中屬性和方法和現(xiàn)實(shí)有些不同
- 現(xiàn)實(shí)世界中各個(gè)類的屬性和方法是我們根據(jù)客觀存在做出的抽象總結(jié)厌杜。
- 編程世界中各個(gè)類的屬性和方法奉呛,是需要我們自行創(chuàng)建的(實(shí)際上整數(shù)、列表這些內(nèi)置數(shù)據(jù)類型也是需要的夯尽,只不過Python預(yù)先創(chuàng)建好了瞧壮,我們可以直接使用)。
類的創(chuàng)建
- 例:創(chuàng)建電腦類的示例代碼
class Computer:
screen = True
def start(self):
print('電腦正在開機(jī)中……')
# 電腦類匙握,都有屏幕咆槽,所以屬性screen的值為True;開機(jī)時(shí)會(huì)顯示“電腦正在開機(jī)中……”
實(shí)例方法的創(chuàng)建語句圈纺,和函數(shù)的定義語句很類似秦忿,唯一不同的是:實(shí)例方法中有個(gè)必須放在首位的參數(shù)self
例:創(chuàng)建中國人類
# 類名首字母大寫
class Chinese:
# 用賦值語句,創(chuàng)建類的屬性
eye = 'black'
# 創(chuàng)建實(shí)例方法時(shí)蛾娶,不要漏了 self
def eat(self):
print('吃飯灯谣,選擇用筷子。')
類的調(diào)用
class Computer:
screen = True
def start(self):
print('電腦正在開機(jī)中……')
my_computer = Computer()
print(my_computer.screen)
my_computer.start()
- 調(diào)用的關(guān)鍵在第7行代碼:my_computer = Computer()蛔琅。這個(gè)過程叫作:類的實(shí)例化胎许,即在某個(gè)類下創(chuàng)建一個(gè)實(shí)例對象。
class Computer:
screen = True
def start(self):
print('電腦正在開機(jī)中……')
my_computer = Computer()
print(type(my_computer))
print(my_computer)
'''
<class '__main__.Computer'>
<__main__.Computer object at 0x7f72f95c7040>
'''
- 第一行:<class 'main.Computer'>驗(yàn)證了my_computer屬于Computer這個(gè)類
- 第二行打印出Computer類的一個(gè)實(shí)例對象(object),后面的一串字符表示這個(gè)對象的內(nèi)存地址辜窑。
- 當(dāng)實(shí)例my_computer一被創(chuàng)建出來钩述,就可以調(diào)用類中的屬性和方法。一句話概括就是:類有的實(shí)例都會(huì)有穆碎。調(diào)用的語法是實(shí)例名.屬性和實(shí)例名.方法
class Computer:
screen = True
def start(self):
print('電腦正在開機(jī)中……')
my_computer = Computer()
print(my_computer.screen)
my_computer.start()
- 倒數(shù)第二行:my_computer.screen先是獲取到類屬性screen對應(yīng)的值True切距,再用print()打印出來。
- 最后一行:my_computer.start()調(diào)用方法start()惨远,這個(gè)方法的功能是直接打印出'電腦正在開機(jī)中……'谜悟。
- 參數(shù)self的特殊之處:在定義時(shí)不能丟,在調(diào)用時(shí)要忽略北秽。實(shí)例調(diào)用方法時(shí)不用傳參
class Chinese: # 創(chuàng)建一個(gè)類
eye = 'black'
def eat(self):
print('吃飯葡幸,選擇用筷子。')
wsl = Chinese() # 類的實(shí)例化
print(wsl.eye) # 實(shí)例調(diào)用類屬性
wsl.eat() # 調(diào)用類中的方法(傳參不用管self)
- 類中創(chuàng)建的屬性和方法可以被其所有的實(shí)例調(diào)用贺氓,而且蔚叨,實(shí)例的數(shù)目在理論上是無限的。我們可以同時(shí)“新建”多個(gè)實(shí)例
# 閱讀代碼后點(diǎn)擊運(yùn)行
class Chinese:
eye = 'black'
def eat(self):
print('吃飯辙培,選擇用筷子蔑水。')
# 類的實(shí)例化:創(chuàng)建多個(gè)實(shí)例
wsl = Chinese()
lz = Chinese()
ls = Chinese()
print(lz.eye)
wsl.eat()
ls.eat()
因此,類也被稱為“實(shí)例工廠”扬蕊,因其為所有實(shí)例提供了一套藍(lán)圖(即預(yù)先設(shè)定好有什么屬性和方法)搀别。
創(chuàng)建類的兩個(gè)關(guān)鍵點(diǎn)
特殊參數(shù):self
- 特殊參數(shù)self的作用:self會(huì)接收實(shí)例化過程中傳入的數(shù)據(jù),當(dāng)實(shí)例對象創(chuàng)建后尾抑,實(shí)例便會(huì)代替 self歇父,在代碼中運(yùn)行。
- 換言之再愈,self 是所有實(shí)例的替身
- 實(shí)際上和一般函數(shù)一樣榜苫,類的方法也可以設(shè)置多個(gè)參數(shù)
self參數(shù)分析
class Chinese:
name = 'wsl' # 類屬性name
def say(self, someone): # 帶有兩個(gè)參數(shù)的方法
print(someone + '是中國人')
person = Chinese()
print(person.name)
person.say('wsl')
# self調(diào)用時(shí)要忽略,'wsl'傳給參數(shù)someone
這樣寫雖然沒錯(cuò)翎冲,但實(shí)際上是多此一舉垂睬,因?yàn)橹灰趕ay方法內(nèi)部調(diào)用類屬性'wsl',就可以實(shí)現(xiàn)同樣的功能抗悍,不必重復(fù)傳參驹饺。
怎么在方法內(nèi)部調(diào)用類屬性
class Chinese:
name = '吳楓' # 類屬性name
def say(self):
print(name + '是中國人')
# 打印出'吳楓是中國人'
person = Chinese()
person.say()
這樣會(huì)報(bào)錯(cuò),系統(tǒng)會(huì)告訴你name在say方法中沒有被定義
- 如果要在類的外部調(diào)用類屬性檐春,我們得先創(chuàng)建一個(gè)實(shí)例逻淌,再用實(shí)例名.屬性的格式調(diào)用
- 如果想在類的內(nèi)部調(diào)用類屬性么伯,而實(shí)例又還沒創(chuàng)建之前疟暖,我們就需要有個(gè)變量先代替實(shí)例接收數(shù)據(jù),這個(gè)變量就是參數(shù)self。
class Chinese:
name = 'wsl' # 類屬性name
def say(self):
print(self.name + '是中國人')
person = Chinese() # 創(chuàng)建Chinese的實(shí)例person
person.say() # 調(diào)用實(shí)例方法
當(dāng)最后一行代碼運(yùn)行時(shí)俐巴,實(shí)例person會(huì)像參數(shù)一樣傳給self骨望,替換掉self,第六行的self.name等價(jià)于person.name
person.name就相當(dāng)于調(diào)用了類屬性name(即'wsl')欣舵,然后跑完整個(gè)方法擎鸠。
等價(jià)的代碼
class Chinese:
name = 'wsl' # 類屬性name
def say(person):
print(person.name + '是中國人')
person = Chinese() # 創(chuàng)建Chinese的實(shí)例person
person.say() # 調(diào)用實(shí)例方法
- 可見,self的作用相當(dāng)于先給實(shí)例占了個(gè)位置缘圈,等到實(shí)例創(chuàng)建好就“功成身退劣光,退位讓賢”。
- 如果想在類的方法內(nèi)部調(diào)用其他方法時(shí)糟把,我們也需要用到self來代表實(shí)例绢涡。
class Chinese:
def greeting(self):
print('很高興遇見你')
def say(self):
self.greeting()
print('我來自中國')
person = Chinese()
# 創(chuàng)建實(shí)例person
person.say()
# 調(diào)用say()方法
- 當(dāng)最后一行實(shí)例person調(diào)用say()方法時(shí),便會(huì)執(zhí)行say()內(nèi)部的語句(第七行開始)遣疯。
- self.greeting()就變成person.greeting()雄可,也就是調(diào)用實(shí)例方法greeting(),打印出'很高興遇見你'缠犀,再打印出'我來自中國'数苫。
綜上,所以我們說self代表的是類的實(shí)例本身辨液,方便數(shù)據(jù)的流轉(zhuǎn)虐急。
- 只要在類中用def創(chuàng)建方法時(shí),就必須把第一個(gè)參數(shù)位置留給 self滔迈,并在調(diào)用方法時(shí)忽略它(不用給self傳參)戏仓。
- 當(dāng)在類的方法內(nèi)部想調(diào)用類屬性或其他方法時(shí),就要采用self.屬性名或self.方法名的格式
特殊方法:初始化方法
- 定義初始化方法的格式是def init(self)亡鼠,是由init加左右兩邊的【雙】下劃線組成( initialize “初始化”的縮寫)赏殃。
- 初始化方法的作用在于:當(dāng)每個(gè)實(shí)例對象創(chuàng)建時(shí),該方法內(nèi)的代碼無須調(diào)用就會(huì)自動(dòng)運(yùn)行间涵。
class Chinese:
def __init__(self):
print('很高興遇見你仁热,我是初始化方法')
person = Chinese()
- 只是創(chuàng)建了實(shí)例,還沒有調(diào)用勾哩,初始化方法就自動(dòng)執(zhí)行
- 利用這個(gè)特性抗蠢,在編寫習(xí)慣上,我們會(huì)在初始化方法內(nèi)部完成類屬性的創(chuàng)建思劳,為類屬性設(shè)置初始值迅矛,這樣類中的其他方法就能直接、隨時(shí)調(diào)用潜叛。
class Chinese:
def __init__ (self):
self.mouth = 1 # self.不能丟
self.eye = 2
print(self.mouth)
def body(self):
print('我有%s張嘴巴' % self.mouth)
print('我有%s只眼睛' % self.eye)
person = Chinese()
#person.body()
- 除了設(shè)置固定常量秽褒,初始化方法同樣可以接收其他參數(shù)壶硅,讓傳入的這些數(shù)據(jù)能作為屬性在類的方法之間流轉(zhuǎn)。
class Chinese:
def __init__(self, name, birth, region):
self.name = name # self.name = 'huangyaoshi'
self.birth = birth # self.birth = '廣東'
self.region = region # self.region = '深圳'
def born(self):
print(self.name + '出生在' + self.birth)
def live(self):
print(self.name + '居住在' + self.region)
person = Chinese('huangyaoshi','廣東','深圳') # 傳入初始化方法的參數(shù)
person.born()
person.live()
- 先看14行:當(dāng)初始化方法有多個(gè)參數(shù)的時(shí)候销斟,在實(shí)例化的時(shí)候就要傳入相應(yīng)的值庐椒,這里'huangyaoshi'傳給參數(shù)name, '廣東'傳給birth,'深圳'傳給region蚂踊。
- 當(dāng)實(shí)例person創(chuàng)建完成后约谈,初始化方法會(huì)自動(dòng)執(zhí)行,此時(shí)第三行的self.name = name就等價(jià)于self.name = 'huangyaoshi'犁钟,以此類推棱诱。(self.name中的name可以換成其他名稱,只是我們習(xí)慣上這么寫)
- 類的其他方法就能通過self.屬性名的形式調(diào)用傳入的數(shù)據(jù)了
- 隨著我們想實(shí)現(xiàn)的功能愈發(fā)復(fù)雜涝动,我們會(huì)在類內(nèi)部編寫很多的方法军俊,如果我們需要傳入的數(shù)據(jù)能在類中長久保存并能被隨時(shí)調(diào)用,初始化方法就是一個(gè)不錯(cuò)的解決方案捧存。
# 例
class Chinese:
# 初始化方法的創(chuàng)建粪躬,init兩邊雙下劃線。
def __init__(self, hometown):
self.hometown = hometown
print('你在哪里出生昔穴?')
def born(self):
print('我生在%s镰官。' % self.hometown)
wufeng = Chinese('廣東') # 傳給參數(shù)hometown
wufeng.born()
面向?qū)ο缶幊?/h2>
與面向?qū)ο缶幊滔鄬?yīng)的是面向過程編程,也是以前的學(xué)習(xí)里我們所采用的編程思維吗货。
- 面向過程編程:首先分析出解決問題所需要的步驟(即“第一步做什么泳唠,第二步做什么,第三步做什么”)宙搬,然后用函數(shù)實(shí)現(xiàn)各個(gè)步驟笨腥,再依次調(diào)用。
- 面向?qū)ο缶幊逃露猓瑫?huì)將程序看作是一組對象的集合(對象包括類對象和實(shí)例對象)
- 用這種思維設(shè)計(jì)代碼時(shí)脖母,考慮的不是程序具體的執(zhí)行過程(即先做什么后做什么),而是考慮先創(chuàng)建某個(gè)類闲孤,在類中設(shè)定好屬性和方法谆级,即是什么,和能做什么讼积。
- 接著肥照,再以類為模版創(chuàng)建一個(gè)實(shí)例對象,用這個(gè)實(shí)例去調(diào)用類中定義好的屬性和方法即可勤众。
- 如果把面向過程編程的代碼改成面向?qū)ο缶幊逃咭铮覀兛梢韵葎?chuàng)建一個(gè)Project類,在類中定義好屬性和方法们颜,再創(chuàng)建一個(gè)實(shí)例吕朵。
# 例
import math
class Project:
def __init__(self):
self.key = 1
def input(self):
choice = input('請選擇計(jì)算類型:(1-工時(shí)計(jì)算猎醇,2-人力計(jì)算)')
if choice == '1':
self.size = float(input('請輸入項(xiàng)目大小:(1代表標(biāo)準(zhǔn)大小边锁,請輸入小數(shù))'))
self.number = int(input('請輸入人力數(shù)量:(請輸入整數(shù))'))
self.time = None
if choice == '2':
self.size = float(input('請輸入項(xiàng)目大泄檬场:(1代表標(biāo)準(zhǔn)大小波岛,請輸入小數(shù))'))
self.number = None
self.time = float(input('請輸入工時(shí)數(shù)量:(請輸入小數(shù))'))
def estimated(self):
# 人力計(jì)算
if (self.number == None) and (self.time != None):
self.number = math.ceil(self.size * 80 / self.time)
print('項(xiàng)目大小為%.1f個(gè)標(biāo)準(zhǔn)項(xiàng)目茅坛,如果需要在%.1f個(gè)工時(shí)完成,則需要人力數(shù)量為:%d人' %(self.size,self.time,self.number))
# 工時(shí)計(jì)算
elif (self.number != None) and (self.time == None):
self.time = self.size * 80 / self.number
print('項(xiàng)目大小為%.1f個(gè)標(biāo)準(zhǔn)項(xiàng)目则拷,使用%d個(gè)人力完成贡蓖,則需要工時(shí)數(shù)量為:%.1f個(gè)' %(self.size,self.number,self.time))
def again(self):
a = input('是否繼續(xù)計(jì)算?繼續(xù)請輸入y煌茬,輸入其他鍵將結(jié)束程序斥铺。')
if a != 'y':
# 如果用戶不輸入'y',則把key賦值為0
self.key = 0
# 主函數(shù)
def main(self):
print('歡迎使用工作量計(jì)算小程序坛善!')
while self.key == 1:
self.input()
self.estimated()
self.again()
print('感謝使用工作量計(jì)算小程序晾蜘!')
# 創(chuàng)建實(shí)例
project1 = Project()
project1.main()
- 用類編寫一個(gè)直觀的好處就是參數(shù)的傳遞會(huì)比普通函數(shù)要省事很多,也不必考慮全局變量和局部變量眠屎,因?yàn)轭愔械姆椒梢灾苯诱{(diào)用屬性剔交。
- 當(dāng)項(xiàng)目難度越大,需要的參數(shù)越多改衩,用類編寫在程序的可拓展性岖常、可讀性、維護(hù)成本都會(huì)更勝一籌葫督。
- 面向?qū)ο缶幊蹋阂詫ο鬄橹行慕甙埃瑢⒂?jì)算機(jī)程序看作一組對象的集合。
總結(jié)一下:
- 和之前說過的函數(shù)類似橄镜,面向?qū)ο缶幊虒?shí)際上也是一種對代碼的封裝偎快。只不過,類能封裝更多的東西洽胶,既能包含操作數(shù)據(jù)的方法滨砍,又能包含數(shù)據(jù)本身。所以妖异,代碼的可復(fù)用性也更高惋戏。
- 對于需要長期更新的代碼而言,面向?qū)ο缶幊虒懗傻拇a結(jié)構(gòu)會(huì)更清晰他膳。所以响逢,代碼的可讀性、可拓展性和可維護(hù)性這幾個(gè)方面都會(huì)優(yōu)于面向過程編程棕孙。
- 面向?qū)ο缶幊烫蛲ぃ瑢⒋a具體的數(shù)據(jù)和處理方法都封裝在類中些膨,讓我們不用完全了解過程也可以調(diào)用類中的各種方法。
- 這個(gè)優(yōu)勢讓我們可以在 Python 中輕松地調(diào)用各種標(biāo)準(zhǔn)庫钦铺、第三方庫和自定義模塊(可以簡單理解成別人寫好的類)订雾,
- “面向?qū)ο缶幊蹋瑫?huì)為你打開一個(gè)新的世界”矛洞。將他人封裝好的代碼為自己所用洼哎,效率和能做的事情自然是天壤之別。
練習(xí)
- 我們看過Chinese類有這樣兩個(gè)代碼案例:一個(gè)會(huì)打印某個(gè)人現(xiàn)在的居住地沼本,一個(gè)會(huì)打印出生地噩峦。下面,我們會(huì)通過課一個(gè)小知識一次性“說完”這兩個(gè)信息抽兆。
# 例
class Chinese:
def greeting(self):
print('很高興遇見你')
def say(self):
# 在say()方法中調(diào)用greeting()方法
self.greeting()
print('我來自中國')
person = Chinese()
# 創(chuàng)建實(shí)例person
person.say()
# 調(diào)用say()方法
- 改進(jìn)代碼
# 新建一個(gè)方法识补,讓實(shí)例只要調(diào)用一個(gè)方法,就能打印出兩個(gè)信息辫红。
# 代碼完成后凭涂,請運(yùn)行一下,驗(yàn)證是否成功贴妻。
class Chinese:
def __init__(self,hometown,region):
self.hometown = hometown
self.region = region
print('程序持續(xù)更新中……')
def born(self):
print('我生在%s切油。'%(self.hometown))
def live(self):
print('我在%s。'%(self.region))
def diayong(self):
self.born()
self.live()
he=Chinese('廣州','江蘇')
he.diayong()
- 練習(xí)二:打招呼的機(jī)器人代碼
class Robot:
def __init__(self):
self.name = input('我現(xiàn)在剛誕生揍瑟,還沒有名字白翻,幫我起一個(gè)吧。')
self.master = input('對了绢片,我要怎么稱呼你呢滤馍?')
print('你好%s,我叫%s底循。很開心巢株,遇見你~'%(self.master,self.name))
def say_wish(self):
wish = input('告訴一個(gè)你的愿望吧:')
print(self.master+'的愿望是:')
# 這里也可以用字符串的格式化,不過熙涤,用循環(huán)語句的話阁苞,之后改復(fù)述次數(shù)會(huì)方便些。
for i in range(3):
print(wish)
robot1 = Robot()
robot1.say_wish()