新聞中心
使用 Python 類使你的代碼變得更加模塊化。
創(chuàng)新互聯(lián)服務(wù)項目包括賓川網(wǎng)站建設(shè)、賓川網(wǎng)站制作、賓川網(wǎng)頁制作以及賓川網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,賓川網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到賓川省份的部分城市,未來相信會繼續(xù)擴大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
在我上一篇文章中,我解釋了如何通過使用函數(shù)、創(chuàng)建模塊或者兩者一起來使 Python 代碼更加模塊化。函數(shù)對于避免重復(fù)多次使用的代碼非常有用,而模塊可以確保你在不同的項目中復(fù)用代碼。但是模塊化還有另一種方法:類。
如果你已經(jīng)聽過面對對象編程object-oriented programming(OOP)這個術(shù)語,那么你可能會對類的用途有一些概念。程序員傾向于將類視為一個虛擬對象,有時與物理世界中的某些東西直接相關(guān),有時則作為某種編程概念的表現(xiàn)形式。無論哪種表示,當(dāng)你想要在程序中為你或程序的其他部分創(chuàng)建“對象”時,你都可以創(chuàng)建一個類來交互。
沒有類的模板
假設(shè)你正在編寫一個以幻想世界為背景的游戲,并且你需要這個應(yīng)用程序能夠涌現(xiàn)出各種壞蛋來給玩家的生活帶來一些刺激。了解了很多關(guān)于函數(shù)的知識后,你可能會認(rèn)為這聽起來像是函數(shù)的一個教科書案例:需要經(jīng)常重復(fù)的代碼,但是在調(diào)用時可以考慮變量而只編寫一次。
下面一個純粹基于函數(shù)的敵人生成器實現(xiàn)的例子:
#!/usr/bin/env python3import randomdef enemy(ancestry,gear):enemy=ancestryweapon=gearhp=random.randrange(0,20)ac=random.randrange(0,20)return [enemy,weapon,hp,ac]def fight(tgt):print("You take a swing at the " + tgt[0] + ".")hit=random.randrange(0,20)if hit > tgt[3]:print("You hit the " + tgt[0] + " for " + str(hit) + " damage!")tgt[2] = tgt[2] - hitelse:print("You missed.")foe=enemy("troll","great axe")print("You meet a " + foe[0] + " wielding a " + foe[1])print("Type the a key and then RETURN to attack.")while True:action=input()if action.lower() == "a":fight(foe)if foe[2] < 1:print("You killed your foe!")else:print("The " + foe[0] + " has " + str(foe[2]) + " HP remaining")
enemy 函數(shù)創(chuàng)造了一個具有多個屬性的敵人,例如譜系、武器、生命值和防御等級。它返回每個屬性的列表,表示敵人全部特征。
從某種意義上說,這段代碼創(chuàng)建了一個對象,即使它還沒有使用類。程序員將這個 enemy 稱為對象,因為該函數(shù)的結(jié)果(本例中是一個包含字符串和整數(shù)的列表)表示游戲中一個單獨但復(fù)雜的東西。也就是說,列表中字符串和整數(shù)不是任意的:它們一起描述了一個虛擬對象。
在編寫描述符集合時,你可以使用變量,以便隨時使用它們來生成敵人。這有點像模板。
在示例代碼中,當(dāng)需要對象的屬性時,會檢索相應(yīng)的列表項。例如,要獲取敵人的譜系,代碼會查詢 foe[0],對于生命值,會查詢 foe[2],以此類推。
這種方法沒有什么不妥,代碼按預(yù)期運行。你可以添加更多不同類型的敵人,創(chuàng)建一個敵人類型列表,并在敵人創(chuàng)建期間從列表中隨機選擇,等等,它工作得很好。實際上,Lua 非常有效地利用這個原理來近似了一個面對對象模型。
然而,有時候?qū)ο蟛粌H僅是屬性列表。
使用對象
在 Python 中,一切都是對象。你在 Python 中創(chuàng)建的任何東西都是某個預(yù)定義模板的實例。甚至基本的字符串和整數(shù)都是 Python type 類的衍生物。你可以在這個交互式 Python shell 中見證:
>>> foo=3>>> type(foo)>>> foo="bar">>> type(foo)
當(dāng)一個對象由一個類定義時,它不僅僅是一個屬性的集合,Python 類具有各自的函數(shù)。從邏輯上講,這很方便,因為只涉及某個對象類的操作包含在該對象的類中。
在示例代碼中,fight 的代碼是主應(yīng)用程序的功能。這對于一個簡單的游戲來說是可行的,但對于一個復(fù)雜的游戲來說,世界中不僅僅有玩家和敵人,還可能有城鎮(zhèn)居民、牲畜、建筑物、森林等等,它們都不需要使用戰(zhàn)斗功能。將戰(zhàn)斗代碼放在敵人的類中意味著你的代碼更有條理,在一個復(fù)雜的應(yīng)用程序中,這是一個重要的優(yōu)勢。
此外,每個類都有特權(quán)訪問自己的本地變量。例如,敵人的生命值,除了某些功能之外,是不會改變的數(shù)據(jù)。游戲中的隨機蝴蝶不應(yīng)該意外地將敵人的生命值降低到 0。理想情況下,即使沒有類,也不會發(fā)生這種情況。但是在具有大量活動部件的復(fù)雜應(yīng)用程序中,確保不需要相互交互的部件永遠(yuǎn)不會發(fā)生這種情況,這是一個非常有用的技巧。
Python 類也受垃圾收集的影響。當(dāng)不再使用類的實例時,它將被移出內(nèi)存。你可能永遠(yuǎn)不知道這種情況會什么時候發(fā)生,但是你往往知道什么時候它不會發(fā)生,因為你的應(yīng)用程序占用了更多的內(nèi)存,而且運行速度比較慢。將數(shù)據(jù)集隔離到類中可以幫助 Python 跟蹤哪些數(shù)據(jù)正在使用,哪些不在需要了。
優(yōu)雅的 Python
下面是一個同樣簡單的戰(zhàn)斗游戲,使用了 Enemy 類:
#!/usr/bin/env python3import randomclass Enemy():def __init__(self,ancestry,gear):self.enemy=ancestryself.weapon=gearself.hp=random.randrange(10,20)self.ac=random.randrange(12,20)self.alive=Truedef fight(self,tgt):print("You take a swing at the " + self.enemy + ".")hit=random.randrange(0,20)if self.alive and hit > self.ac:print("You hit the " + self.enemy + " for " + str(hit) + " damage!")self.hp = self.hp - hitprint("The " + self.enemy + " has " + str(self.hp) + " HP remaining")else:print("You missed.")if self.hp < 1:self.alive=False# 游戲開始foe=Enemy("troll","great axe")print("You meet a " + foe.enemy + " wielding a " + foe.weapon)# 主函數(shù)循環(huán)while True:print("Type the a key and then RETURN to attack.")action=input()if action.lower() == "a":foe.fight(foe)if foe.alive == False:print("You have won...this time.")exit()
這個版本的游戲?qū)橙俗鳛橐粋€包含相同屬性(譜系、武器、生命值和防御)的對象來處理,并添加一個新的屬性來衡量敵人時候已被擊敗,以及一個戰(zhàn)斗功能。
類的第一個函數(shù)是一個特殊的函數(shù),在 Python 中稱為 init 或初始化的函數(shù)。這類似于其他語言中的構(gòu)造器,它創(chuàng)建了類的一個實例,你可以通過它的屬性和調(diào)用類時使用的任何變量來識別它(示例代碼中的 foe)。
Self 和類實例
類的函數(shù)接受一種你在類之外看不到的新形式的輸入:self。如果不包含 self,那么當(dāng)你調(diào)用類函數(shù)時,Python 無法知道要使用的類的哪個實例。這就像在一間充滿獸人的房間里說:“我要和獸人戰(zhàn)斗”,向一個獸人發(fā)起。沒有人知道你指的是誰,所有獸人就都上來了。
CC-BY-SA by Buch on opengameart.org
類中創(chuàng)建的每個屬性都以 self 符號作為前綴,該符號將變量標(biāo)識為類的屬性。一旦派生出類的實例,就用表示該實例的變量替換掉 self 前綴。使用這個技巧,你可以在一間滿是獸人的房間里說:“我要和譜系是 orc 的獸人戰(zhàn)斗”,這樣來挑戰(zhàn)一個獸人。當(dāng) orc 聽到 “gorblar.orc” 時,它就知道你指的是誰(他自己),所以你得到是一場公平的戰(zhàn)斗而不是斗毆。在 Python 中:
gorblar=Enemy("orc","sword")print("The " + gorblar.enemy + " has " + str(gorblar.hp) + " remaining.")
通過檢索類屬性(gorblar.enemy 或 gorblar.hp 或你需要的任何對象的任何值)而不是查詢 foe[0](在函數(shù)示例中)或 gorblar[0] 來尋找敵人。
本地變量
如果類中的變量沒有以 self 關(guān)鍵字作為前綴,那么它就是一個局部變量,就像在函數(shù)中一樣。例如,無論你做什么,你都無法訪問 Enemy.fight 類之外的 hit 變量:
>>> print(foe.hit)Traceback (most recent call last):File "./enclass.py", line 38, inprint(foe.hit)AttributeError: 'Enemy' object has no attribute 'hit'>>> print(foe.fight.hit)Traceback (most recent call last):File "./enclass.py", line 38, inprint(foe.fight.hit)AttributeError: 'function' object has no attribute 'hit'
hit 變量包含在 Enemy 類中,并且只能“存活”到在戰(zhàn)斗中發(fā)揮作用。
更模塊化
本例使用與主應(yīng)用程序相同的文本文檔中的類。在一個復(fù)雜的游戲中,我們更容易將每個類看作是自己獨立的應(yīng)用程序。當(dāng)多個開發(fā)人員處理同一個應(yīng)用程序時,你會看到這一點:一個開發(fā)人員負(fù)責(zé)一個類,另一個開發(fā)人員負(fù)責(zé)主程序,只要他們彼此溝通這個類必須具有什么屬性,就可以并行地開發(fā)這兩個代碼塊。
要使這個示例游戲模塊化,可以把它拆分為兩個文件:一個用于主應(yīng)用程序,另一個用于類。如果它是一個更復(fù)雜的應(yīng)用程序,你可能每個類都有一個文件,或每個邏輯類組有一個文件(例如,用于建筑物的文件,用于自然環(huán)境的文件,用于敵人或 NPC 的文件等)。
將只包含 Enemy 類的一個文件保存為 enemy.py,將另一個包含其他內(nèi)容的文件保存為 main.py。
以下是 enemy.py:
import randomclass Enemy():def __init__(self,ancestry,gear):self.enemy=ancestryself.weapon=gearself.hp=random.randrange(10,20)self.stg=random.randrange(0,20)self.ac=random.randrange(0,20)self.alive=Truedef fight(self,tgt):print("You take a swing at the " + self.enemy + ".")hit=random.randrange(0,20)if self.alive and hit > self.ac:print("You hit the " + self.enemy + " for " + str(hit) + " damage!")self.hp = self.hp - hitprint("The " + self.enemy + " has " + str(self.hp) + " HP remaining")else:print("You missed.")if self.hp < 1:self.alive=False
以下是 main.py:
#!/usr/bin/env python3import enemy as en# game startfoe=en.Enemy("troll","great axe")print("You meet a " + foe.enemy + " wielding a " + foe.weapon)# main loopwhile True:print("Type the a key and then RETURN to attack.")action=input()if action.lower() == "a":foe.fight(foe)if foe.alive == False:print("You have won...this time.")exit()
導(dǎo)入模塊 enemy.py 使用了一條特別的語句,引用類文件名稱而不用帶有 .py 擴展名,后跟你選擇的命名空間指示符(例如,import enemy as en)。這個指示符是在你調(diào)用類時在代碼中使用的。你需要在導(dǎo)入時添加指示符,例如 en.Enemy,而不是只使用 Enemy()。
所有這些文件名都是任意的,盡管在原則上不要使用罕見的名稱。將應(yīng)用程序的中心命名為 main.py 是一個常見約定,和一個充滿類的文件通常以小寫形式命名,其中的類都以大寫字母開頭。是否遵循這些約定不會影響應(yīng)用程序的運行方式,但它確實使經(jīng)驗豐富的 Python 程序員更容易快速理解應(yīng)用程序的工作方式。
在如何構(gòu)建代碼方面有一些靈活性。例如,使用該示例代碼,兩個文件必須位于同一目錄中。如果你只想將類打包為模塊,那么必須創(chuàng)建一個名為 mybad 的目錄,并將你的類移入其中。在 main.py 中,你的 import 語句稍有變化:
from mybad import enemy as en
兩種方法都會產(chǎn)生相同的結(jié)果,但如果你創(chuàng)建的類足夠通用,你認(rèn)為其他開發(fā)人員可以在他們的項目中使用它們,那么后者更好。
無論你選擇哪種方式,都可以啟動游戲的模塊化版本:
$ python3 ./main.pyYou meet a troll wielding a great axeType the a key and then RETURN to attack.aYou take a swing at the troll.You missed.Type the a key and then RETURN to attack.aYou take a swing at the troll.You hit the troll for 8 damage!The troll has 4 HP remainingType the a key and then RETURN to attack.aYou take a swing at the troll.You hit the troll for 11 damage!The troll has -7 HP remainingYou have won...this time.
游戲啟動了,它現(xiàn)在更加模塊化了?,F(xiàn)在你知道了面對對象的應(yīng)用程序意味著什么,但最重要的是,當(dāng)你向獸人發(fā)起決斗的時候,你知道是哪一個。
本文名稱:使用Python學(xué)習(xí)面對對象的編程
標(biāo)題路徑:http://m.fisionsoft.com.cn/article/dhcggpe.html


咨詢
建站咨詢

