[AI Studio] 飛槳PaddlePaddle Python零基礎入門深度學習筆記<五>
技術標籤:paddlepaddle飛槳筆記python多型類
[AI Studio] 飛槳PaddlePaddle Python零基礎入門深度學習筆記<五>
新需求
橄欖球教練Roger,拿出了自己的資料結構,我們的隊員除了速度訓練,還需要進行力量的練習。既然你的類表現的不錯,我能不能用呢?
loren,2011-11-3,270,3.59,4.11,3:11,3:23,4-10,3-23,4:10,4.21,4-21
首先我們複習一下昨天的部分
第一我們先開啟檔案,讀取Roger教練記錄的資料,這需要一個函式
def get_coach_data(filename):
with open(filename) as f:
line = f.readline()
return line.strip().split(',')
為了方便處理多人資料,我們學習了一個新的概念,類
class Athlete:
def __init__(self,a_name,a_dob=None,a_times=[]):
self.name = a_name
self.dob = a_dob
self.times = a_times
def top3(self):
return sorted (set([self.sanitize(t) for t in self.times]))[0:3]
def sanitize(self,time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
在以上程式碼中
類的主體結構是
class 類名:
def __init__(self,引數1,引數2,etc.):
self.屬性名 = 引數1
self.屬性名 = 引數2
def 方法名(self,引數1,引數2,etc.):
#方法內容
return #return並不是必須的部分,但方法要有內容
- 例項化後 類內方法可以呼叫類內方法,例如top3()方法中呼叫了
self.sanitize(t)
return sorted(set([self.sanitize(t) for t in self.times]))[0:3] - 設定屬性名時,在類內部呼叫方法時,都要在前加
self.
return sorted(set([self.sanitize(t) for t in self.times]))[0:3] - 千萬不要遺漏類名後、方法後的冒號!
一,繼承
來看看如何完成Roger教練的新需求吧,一樣為了快速處理大量資料,我們需要一個類來作為資料模板,但是和昨天不同的是,明顯Athlete類已經不能滿足我們的需求了,但難道要再打一段很長很長的Rugby類嗎?
class Rugby:
def __init__(self,a_name,a_dob=None,a_times=[],a_squat):
self.name = a_name
self.dob = a_dob
self.times = a_times
self.squat = a_squat
def top3(self):
return sorted(set([self.sanitize(t) for t in self.times]))[0:3]
def sanitize(self,time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
明顯程式碼重複性太高了,完全不符合簡潔性,所以我們需要使用繼承
Athlete類就是父類
Rugby類就是子類
子類繼承了父類所有的屬性和方法,同時子類還可以擁有更多的屬性和方法
#定義橄欖球運送員類
class Rugby(Athlete):
def __init__(self,a_name,a_bod,a_squat,a_times):
#呼叫父類__init__
Athlete.__init__(self,a_name,a_bod,a_times)
#深蹲次數
self.squat = a_squat
# 繼承後下面兩個函式就在Rugby類中,只是看不到而已
# def top3(self):
# return sorted(set([self.sanitize(t) for t in self.times]))[0:3]
# def sanitize(self,time_string):
# if '-' in time_string:
# splitter = '-'
# elif ':' in time_string:
# splitter = ':'
# else:
# return (time_string)
# (mins,secs) = time_string.split(splitter)
# return (mins+'.'+secs)
如果這樣,只需要四行程式碼就可以繼承Athlete的所有內容,外加一個新的屬性squat
loren = get_coach_data('mywork/loren.txt')
rugby = Rugby(loren.pop(0),loren.pop(0),loren.pop(0),loren)
print('姓名:%s,生日:%s,深蹲:%s個,最塊的3次成績:%s' %(rugby.name,rugby.dob,rugby.squat,rugby.top3()))
將Rugby類例項化後測試
輸出成功了,證明繼承成功
關於類
定義:class 子類名(父類名):
情況1,如果子類有新增的屬性,那麼需要在子類__init方法中,呼叫父類的__init__
情況2,如果子類沒有新增的屬性,子類不需要寫__init__方法
使用: 物件名 = 子類名(引數)
繼承的好處:程式碼重用,升級功能(重寫),新增功能(新的方法)
我們已經學會了如何給子類新增屬性,那麼關於新的方法呢?
方法重寫
平常需要新的方法時,直接在新的子類中define一個就可以,但如果我們的新方法名和父類的一個方法相同時,新方法會覆蓋掉舊方法,也就是方法重寫。
子類方法與父類方法完全相同,子類若重寫了父類的方法,則子類物件呼叫方法時就是呼叫的自己類中重新的方法,但放心,方法重寫不會影響父類方法。
Roger教練不需要隊員的三個最快成績,相反,他需要三個最慢成績,甚至不需要資料去重,那我們應該怎麼寫呢?
class Rugby(Athlete):
def __init__(self,a_name,a_bod,a_squat,a_times):
Athlete.__init__(self,a_name,a_bod,a_times)
self.squat = a_squat
def top3(self):
return sorted([self.sanitize(t) for t in self.times])[-3:]
這樣一來,Rugby內的top3方法就變成了:
將資料規範化,寫進一個新列表,然後排序,取出末三位
二,多型
又來10個各種各樣的運動員,他們的top3完全不同,程式碼看起來是這樣的。
class OtherAthlete(Athlete):
def __init__(self,a_name,a_bod,a_squat,a_times):
Athlete.__init__(self,a_name,a_bod,a_times)
self.squat = a_squat
def top3(self):
return sorted([self.sanitize(t) for t in self.times])[0:3]
我需要為每種遠動員定義一個子類,呼叫的時候是一堆重複的程式碼。
mark1 = get_coach_data('mywork/mark.txt')
mark2 = get_coach_data('mywork/mark1.txt')
mark3 = get_coach_data('mywork/mark2.txt')
mark1 = OtherAthlete(mark1.pop(0),mark1.pop(0),mark1.pop(0),mark1)
mark2 = OtherAthlete(mark2.pop(0),mark2.pop(0),mark2.pop(0),mark2)
mark3 = OtherAthlete(mark3.pop(0),mark3.pop(0),mark3.pop(0),mark3)
print('姓名:%s,生日:%s,深蹲:%s個,最快的3次成績:%s' %(mark1.name,mark1.dob,mark1.squat,mark1.top3()))
print('姓名:%s,生日:%s,深蹲:%s個,最快的3次成績:%s' %(mark2.name,mark2.dob,mark2.squat,mark2.top3()))
print('姓名:%s,生日:%s,深蹲:%s個,最快的3次成績:%s' %(mark3.name,mark3.dob,mark3.squat,mark3.top3()))
這樣的重複程式碼很麻煩,Python作為面嚮物件語言的三大特徵——封裝,繼承,多型
多型的體現就可以解決這個問題
我們將需要子類做的事情放進一個函式中
def print_rugby(athlete):
print(athlete.name)
print(athlete.dob)
print(athlete.squat)
print(athlete.top3())
這個函式的引數是例項化後的父類,每當我們需要呼叫子類的組成部分的時候,只需要呼叫print_rugby函式,並且將子類作為引數放進去
全部的程式碼是這樣的
loren = get_coach_data('mywork/loren.txt')
mark = get_coach_data('mywork/mark.txt')
loren = Rugby(loren.pop(0),loren.pop(0),loren.pop(0),loren)
mark = OtherAthlete(mark.pop(0),mark.pop(0),mark.pop(0),mark)
#新定義一個函式,將父類作為一個引數,方便重複使用子類
#稱坐多型
def print_rugby(athlete):
print(athlete.name)
print(athlete.dob)
print(athlete.squat)
print(athlete.top3())
#具體怎麼使用決定於子類
print_rugby(loren)
print_rugby(mark)
輸出結果
多型性:一個事物多種形態
上面例子中print_rugby的引數athlete,athlete.name,athlete.top3()的行為由athlete的子類決定。
多型的好處是:減少重複程式碼,分離經常改變的程式碼與不經常改變的程式碼,使得程式碼可維護性提高。
工廠
方便建立多個物件
#如何優化建立物件的程式碼呢?
#將大段程式碼的變化,轉為引數的變化
def obj_factory(name,filename):
with open(filename) as f:
line = f.readline()
templ = line.strip().split(',')
if name == 'r':
return Rugby(templ.pop(0),templ.pop(0),templ.pop(0),templ)
elif name == 'oa':
return OtherAthlete(templ.pop(0),templ.pop(0),templ.pop(0),templ)
oa = obj_factory('oa','mywork/mark.txt')
print(oa.name)
三,多繼承
一個子類可以繼承多個父類的屬性,程式碼的複用性越高,看起來就越簡潔,而不是一團漿糊
class Father():
def talk(self):
print("---爸爸的表達能力---")
class Mather():
def smart(self):
print("---媽媽聰明的頭腦---")
class Child(Father,Mather):
pass
child1 = Child()
child1.talk()
child1.smart()
切記,多個父類在建立之時就應該注意,不應該擁有相同的方法名,不然多繼承時會出現問題
四,封裝
在每個需要Athlete的時候,我都需要把Athlete類的定義複製貼上要用的程式碼上方。
有沒有什麼辦法可以讓程式碼主體更加簡潔呢?
import sys
sys.path.append('mywork')
# import athlete
# print(dir(athlete))
from athlete import *
import sys
匯入sys模組 sys.path.append(‘work’)
將模組athlete.py新增到模組搜尋路徑
from athlete import *
匯入athlete模組,使用athlete模組下的所有程式碼
如果有一個模組mymodule.py中也包含get_coach_data函式,該怎麼區分這兩個函式呢?
import sys
sys.path.append('mywork')
from p1.mymodule import *
from p2.mymodule import *
import p1
import p2
p1.mymodule.demo()
p2.mymodule.demo()
要寫清是哪個資料夾下的哪個檔案裡的demo()函式