目錄:
創作不易,各位看官,點個贊收藏、關注必回關、QAQ、您的點贊是我更新的最大動力!
一、封裝
(一)什麼是封裝
封裝,是面向對象的第一個特性,體現了對數據的有效組織和數據安全性的保護機制, 在傳統的編碼中,面向對象編程思想中的類型體現的是對數據的描述,而對象體現的則是個 體數據的展示,既然是個體數據反映的就是生活中的私有數據,就需要考慮其訪問安全性, 反應到生活中就是每個個體私有數據的保護,如圖所示:
(二)封裝的基本語法
1、所有屬性私有化
python 語言在實現封裝時,一些項目規範中約定使用一個下劃線開頭的屬性爲當前對象私 有屬性,不允許外界訪問,但是一個下劃線開頭的屬性是可以被直接訪問的。 |
語法上兩個下劃線開頭的屬性或者方法,在外界是不允許直接訪問的。 |
語法上兩個下劃線開頭並且結尾的屬性有特殊的應用場合。 |
class Manager:
"""管理員類型"""
def __init__(self, username, password, nickname, email, phone):
"""初始化屬性數據:所有屬性私有化"""
self.__username = username # 標準的私有化語法
self.__password = password # 標準的私有化語法
self.__nickname = nickname # 標準的私有化語法
2、訪問私有屬性
屬性私有化之後,給每個屬性提供訪問屬性的 get 方法和設置賦值的 set 方法,通過固 定語法的格式提供給對象外部的調用者
- 一般訪問 str
#屬性私有化之前,用戶信息可以直接訪問並修改
class User:
"""用戶類型"""
def __init__(self,name,age):
"""用戶姓名和年齡"""
self.name = name
self.age = age
def __str__(self):
"""打印用戶信息"""
return f"我叫{self.name},今年{self.age}歲"
user = User("小明",23)
print(user)
- 使用setter和getter訪問
def set__username(self, username):
"""給私有屬性設置數據的方法"""
self.__username = username
def get__username(self):
"""獲取私有屬性數據的方法"""
return self.__username
- 使用限定條件訪問
作用:就是在開發過程中保護核心代碼,在類的外部不能使用(對象不能調用私有方法)
3、私有方法
class A:
def __test1(self):
print("--in test1--")
def test2(self):
self.__test1()
print("--in test2--")
a = A()
# a.__test1() #對象不能調用私有方法
a.test2()
4、私有屬性修改
class User:
"""用戶類型"""
def __init__(self,name,age):
"""用戶姓名和年齡"""
# self.name = name
# self.age = age
self.__name = name
self.__age = age
def __str__(self):
"""打印用戶信息"""
# return f"我叫{self.name},今年{self.age}歲"
return f"我叫{self.__name},今年{self.__age}歲"
user = User("小明",23)
# print(user)
#屬性私有化之前,對象的屬性如下
# print(user.__dict__) #{'name': '小明', 'age': 23}
# user.name = "穩穩" #可以修改
#屬性私有化之後,對象的屬性如下
print(user.__dict__) #{'_User__name': '小明', '_User__age': 23}
# user.__name = "穩穩" #只是增加屬性
# print(user.__dict__)
user._User__name = "穩穩"
print(user)
二、繼承
(一)什麼是繼承讓類與類之間產生父子關係,子類可以擁有父類的屬性和方法(私有屬性和私有方法無法繼承)
父類 | 用於被繼承的類,稱爲父類,也叫基類,或者超類 |
子類 | 繼承其它類的類,稱爲子類,也叫派生類 |
查看繼承父類的 | _ base_() |
繼承的作用: 可以提高代碼的複用率
(二)繼承的基本語法
"""
繼承的基本語法
父類:被繼承的類型,需要重複利用的代碼包含在父類中
子類:當前類型,繼承其他類型,重複使用其他類型中的代碼
python中的所有類型都是直接或者間接繼承自object
都是從object派生出來的!
子類繼承父類,父類派生子類!
"""
class Father:
"""父親類型"""
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
def sleep(self):
print("睡覺時間到了,準備午休..")
def study(self):
print("充電時間到了,準備學習..")
class Son(Father):
"""兒子類型:繼承父親類型"""
pass
tom = Son("湯姆", "男", 18)
print(tom.name, tom.age, tom.gender)
tom.study()
tom.sleep()
複用性體現 [使用父類的屬性]:
"""
繼承中父類的代碼的複用
"""
class Animal:
"""動物類型"""
def __init__(self, nickname, amtype):
self.nickname = nickname
self.amtype = amtype
def move(self):
print("某個動物在運動中...")
class Beast(Animal):
"""地上跑的"""
pass
class Bird(Animal):
"""天上飛的"""
pass
class Fish(Animal):
"""水裏遊的"""
pass
cat = Beast("湯姆貓", "走獸")
beita = Bird("開飛機的貝塔", "飛禽")
xiaolu = Fish("水裏的美人魚", "游魚")
cat.move()
beita.move()
xiaolu.move()
複用性高級[使用父類的屬性,自己定義自己的屬性]:
"""
繼承中父類的代碼的複用升級
從一個問題說起:
直接使用父類不好嗎?爲什麼要寫那麼多子類的空代碼?
靈機一動:難道爲了一行代碼一塊錢?
① 通過子類繼承父類
子類能更好的更精確的描述生活中的某個對象
貓 = 動物()
貓 = 走獸() 更加準確
② 通過子類繼承父類
子類在複用父類代碼的情況下,能擴展自己的屬性
父類的每個子類,都有互相不同的地方~
所以纔將多個子類相同的代碼聲明在父類中複用
子類中互不相同的代碼保留在自己子類中使用
"""
class Animal:
"""動物類型"""
def __init__(self, nickname, amtype):
self.nickname = nickname
self.amtype = amtype
def move(self):
print("某個動物在運動中...")
class Beast(Animal):
"""地上跑的"""
def __init__(self, nickname, amtype, blood):
# 子類如果一旦編寫__init__()方法
# 就必須顯式的調用父類的__init__()方法初始化父類數據
# Animal.__init__(nickname, amtype)
# super(Beast, self).__init__(nickname, amtype)
# ① 複用父類的代碼
super().__init__(nickname, amtype)
# ② 子類自己的代碼
self.blood = blood
class Bird(Animal):
"""天上飛的"""
def __init__(self, nickname, amtype, attact):
# ① 複用父類的代碼
super().__init__(nickname, amtype)
# ② 子類獨有的代碼
self.attact = attact
class Fish(Animal):
"""水裏遊的"""
pass
cat = Beast("湯姆貓", "走獸", 2000)
beita = Bird("開飛機的貝塔", "飛禽", 100)
xiaolu = Fish("水裏的美人魚", "游魚")
print(cat.nickname, cat.amtype, cat.blood)
print(beita.nickname, beita.amtype, beita.attact)
cat.move()
beita.move()
xiaolu.move()
(三)方法覆蓋 [重寫]
簡單的來說,就是將父類裏面的方法,在子類裏面重寫
"""
方法重載:
子類中聲明和定義了父類中相同名稱和參數的方法
"""
class Animal:
"""動物類型"""
def __init__(self, nickname):
self.nickname = nickname
def move(self):
print(f"{self.nickname}在移動中..")
class Beast(Animal):
"""走獸"""
def move(self):
print(f"{self.nickname}在快速的奔跑中..")
class Bird(Animal):
"""飛禽"""
def move(self):
print(f"{self.nickname}在愉快的飛行中..")
class Fish:
"""游魚"""
def __init__(self, nickname):
self.nickname = nickname
def swimming(self):
"""移動的方法"""
print("飛快的遊走中...")
cat = Beast("湯姆貓")
shuke = Bird("舒克的飛機")
# 調用的是同一個方法,但是執行的是不同的結果
cat.move() # 湯姆貓在快速的奔跑中..
shuke.move() # 舒克的飛機在愉快的飛行中..
xiaolu = Fish("小鹿")
xiaolu.swimming()
'''
通過方法重寫
① 理解繼承的代碼的重用型
② 繼承關係中,對於子類中的代碼有了一定的約束性
③ 方法覆蓋,體現了一種運行時狀態改變-體現了多態
如果子類沒有重寫父類的方法,就直接執行父類的方法
如果子類重寫了父類的方法,就執行子類重寫後的方法
'''
(四)子類訪問父類 [使用父類中的super()方法,三種方法]
坦克大戰:
"""
方法重載案例:坦克大戰中的坦克
"""
class Tank:
"""坦克類型"""
def fire(self):
print("坦克開火了,發射子彈(10行代碼)...")
class HeroTank(Tank):
"""英雄坦克:玩家控制"""
def fire(self):
# 子類中額外添加的功能
print("英雄坦克,模擬玩家按下空格鍵,發射子彈[事件(按下空格動作)操作]")
# 複用父類中發射子彈的代碼
# Tank.fire(self) #1.父類名.方法名(self)
# super(Tank,self).fire() #2.super(當前類名,self).方法名()
super().fire() #3.super().fire()
class EnemyTank(Tank):
"""敵方坦克:電腦控制"""
def fire(self):
print("添加代碼,控制坦克間隔隨機時間發射子彈")
super().fire()
hero = HeroTank()
hero.fire()
enemy = EnemyTank()
enemy.fire()
綜合案例-寵物醫院:
"""
寵物醫院案例
面向對象開發
"""
import time
class Pet:
"""寵物父類"""
def __init__(self, nickname, health):
self.nickname = nickname # 暱稱
self.health = health # 健康狀態[0~30病危|30-60生病|60-80亞健康|80-100健康]
def recovery(self):
"""康復的行爲"""
while self.health <= 65:
self.health += 5
time.sleep(0.5)
print(f"{self.health}>>正在恢復中.....")
class Hospital:
"""寵物醫院"""
def __init__(self, name):
self.name = name
def care(self, pet):
"""治療的行爲"""
# 判斷某個對象是否屬於某種類型:判斷pet對象是否Pet類型
if isinstance(pet, Pet):
# 醫院開始治療,用藥
print(f"開始治療{pet.nickname}")
# 寵物開始恢復
pet.recovery()
else:
print("寵物醫院只接受寵物進行治療....")
class Cat(Pet):
"""寵物貓"""
pass
class Dog(Pet):
"""寵物狗"""
pass
class Person:
def __init__(self, nickname, health):
self.nickname = nickname
self.health = health
def recovery(self):
while self.health <= 60:
self.health += 20
print("正在快速的恢復中...")
hospital = Hospital("瑪利亞寵物醫院")
# cat = Cat("湯姆貓", 30)
# hospital.care(cat)
# print("最終寵物的健康值:", cat.health)
dog = Dog("二哈", 20)
hospital.care(dog)
print("最終二哈的健康值:", dog.health)
# damu = Person("大牧", 50)
# hospital.care(damu)
(五)多繼承 [隔代繼承]
1、基本語法
python 語法中提供了多繼承的語法,用於體現生活中一個對象多種角色的情況,具體 語法上是將多個繼承的類型依次寫在子類聲明後面的括號中
隔代繼承:
class Animal:
def __init__(self):
print("我是動物")
class dog(Animal):
pass
class cat(dog):
pass
#創建對象
cat_tom = cat() #輸出 “我是動物”
多繼承:
"""
多繼承關係
多繼承需要注意的問題:如果多個父類中出現了相同名稱的屬性、方法怎麼辦?
"""
class Son:
"""兒子類型"""
def respect(self):
"""尊敬,孝順"""
print("百善孝爲先")
def play(self):
print("作爲兒子角色,越快的玩耍中...")
class Husband:
"""丈夫類型"""
def love(self):
"""恩愛的行爲"""
print("相濡以沫")
def play(self):
print("作爲丈夫的角色,王者榮耀中...")
class Father:
"""老爹類型"""
def care(self):
"""愛護的行爲"""
print("家裏的千金,萬般呵護..")
class Teacher:
"""老師類型"""
def teach(self):
"""授課的行爲"""
print("前人栽樹....")
class Person(Son, Husband, Father, Teacher):
"""人的類型"""
pass
# def play(self):
# print("Person 愉快的玩耍中...")
wenwen = Person()
wenwen.play() # Son, Husband
# # 兒子
# if isinstance(wenwen, Son):
# wenwen.respect()
#
# # 丈夫
# if isinstance(wenwen, Husband):
# wenwen.love()
#
# # 父親
# if isinstance(wenwen, Father):
# wenwen.care()
#
# # 老師
# if isinstance(damu, Teacher):
# wenwen.teach()
2、注意事項
多繼承關係中,一個類型繼承多個類型,如果在繼承的多個類型中出現了相同名稱的屬 性或者方法(當然如果出現一般都是軟件結構設計上存在問題),就會出現子類如果使用到了 這樣的屬性和方法,優先調用的是繼承的那個類型的屬性或者方法了 上述問題如果一旦出現,有兩種解決方案:
- 重新設計軟件架構,對類型中的屬性進行規範整理
- 梳理調用順序,按照語法語義上的順序執行
運行程序只看輸出“動物的愛”
第一種方案如果一旦實施就是常規編程,第二種方案中確定某個重複聲明的屬性的查詢 順序,可以通過規範語法進行確定,Python 提供了 mro() 方法用於確定繼承關係中屬性和方 法的查詢操作順序
class Animal:
"""動物類型"""
def love(self):
print("動物的愛")
class Person:
"""人的類型"""
def love(self):
print("人的愛")
class Tom(Animal,Person):
pass
user_Tom = Tom()
user_Tom.love()
print(Tom.mro())
3、屬性查詢順序
Python3 中已經改善了 Python2 中多種類型操作方式(Python2/3 的區別在十五講會詳細 說明),統一了類型的聲明和加載方式,對於多繼承中的重名屬性和重名方法的查詢執行順 序,使用了廣度優先的查詢原則
(六)___init()___
這邊其實方法重寫已經提到過了
所以這邊就簡單複述下
- 繼__init() __構建對象後屬性初始化的方法
- 雙下劃線開頭結尾稱爲魔法方法
- 魔法方法只會繼 承固定語法格式,如果在子類中改變了原有的語法格式的情況下,一定要主動調用父類相同 的方法初始化,否則會出現父類數據沒有初始化的問題
如果子類中沒有編寫__init__()方法,創建對象時就會自動執行繼承的默認的無參數的 init()方法,並且自動調用父類的__init__()完成初始化過程,如果所示:
如果子類中一旦編寫了自定義了__init__()方法,默認繼承的空參數的__init__()就會失效, 必須顯式的主動的調用父類的__init__()方法完成父類初始化工作,否則父類的數據就不會被 正確的繼承過來了
三、多態
(一)什麼是多態?多態是程序運行過程中,根據運行時的數據的差異,產生不同的處理狀態,運行不同的 邏輯代碼的過程 多態作爲面向對象的特徵之一,沒有固定的語法,在經典的程序設計模式中有多種體現 方式,如策略模式中就可以根據傳遞的具體執行方案執行對應的處理結果
多態基本案例:
# 定義人類:可以調速,可以玩,在玩的過程中跳舞
# 實現多態:老年人跳廣場舞
# 多態三個條件
"""
1.必須存在繼承關係
2.重寫目標方法
3.使用子類對象調用父類方法
"""
class Person:
"""人的類型"""
def danc(self):
print("跳舞")
def play(self): # self = old 老年人
self.danc()# 老年人調用跳舞,本人含義跳舞
class OldMan(Person):
"""老年人類型"""
def danc(self):
print("跳廣場舞")
#
# # per1 = Person()
# # per1.play()
old = OldMan()
old.play()
策略模式
圖示中用戶需要在某網站上註冊賬號,網站動態判斷用戶的註冊方式並響應驗證 碼給正確的終端設備,在開發過程中函數式編程可以直接進行判斷處理,但是不利於程序功 能的擴展(大家可以編寫函數式的實現方式進行嘗試)。通過策略模式進行完成上述功能流程, 同時還爲將來功能的擴展預留了空間
"""
網站可以註冊,註冊有默認的驗證碼發送方式
分析:
網站類
方法:註冊方法,調用默認設備,發送驗證碼
設備類
方法:生成驗證碼併發送
策略模式
"""
class WebSite:
"""網站類型"""
def register(self,device):
print("開始註冊")
#調用設備發送驗證碼的方法
device.send("6666")
input("驗證碼已發送,請輸入")
print("註冊成功")
class Device:
"""設備類型"""
def send(self,code):
print("默認發送驗證碼:",code)
class Phone(Device):
"""手機註冊"""
def send(self,code):
print("通過手機發送驗證碼: ",code)
class Email(Device):
"""郵箱註冊"""
def send(self, code):
print("通過手機發送驗證碼: ", code)
# 1 網頁註冊
# # 用戶註冊
# ws = WebSite()
# device = Device()
# # 發起註冊
# ws.register(device)
# 2 手機註冊
# 用戶註冊
ws = WebSite() # 創建對象
phone = Phone() # 實例化設備對象
# 發起註冊
ws.register(phone)
四、綜合案例
"""
學生選課管理系統
管理員,添加課程,查看課程 [,刪除課程,修改課程]
宗澄:課程保存課程說不通
業務受理:增加課程,,管理員增加課程 add_course()
邏輯處理:增刪改查數據-- 保存數據-- 課程對象~保存自己的數據 save()
"""
class Database:
"""數據庫類型:存儲項目中的所有數據"""
# 類屬性:存儲課程-- key課程名稱:value課程對象
courses_dict = dict()
class Users:
"""學生選課管理系統 用戶類型"""
def __init__(self, username, password, nickname, email, phone):
"""初始化屬性數據"""
self.username = username
self.password = password
self.nickname = nickname
self.email = email
self.phone = phone
class Manager(Users):
"""管理員類型"""
def add_course(self):
"""增加課程的方法"""
name = input("請輸入課程名稱:")
if name in Database.courses_dict:
print("課程已經存在,請使用其他名稱添加")
return self.add_course()
teacher = input("請輸入授課老師:")
maxsize = input("請輸入人數上限:")
times = input("請輸入課程課時:")
score = input("請輸入課程學分:")
desc = input("請輸入課程描述:")
# 創建課程對象
course = Course(name, teacher, maxsize, times, score, desc)
# 保存課程數據
course.save()
input("課程添加完成,按任意鍵繼續。")
def check_course(self):
"""查詢課程的方法"""
# TODO 待完善
for name, course in Database.courses_dict.items():
print(f"課程名稱:{name}, 課程:{course}")
input("課程查看完成,按任意鍵繼續")
def update_course(self):
"""修改課程信息的方法"""
name = input("請輸入課程名稱:")
if name not in Database.courses_dict:
print("課程不存在,請重新輸入")
return self.update_course()
# 獲取到要修改的課程數據
course = Database.courses_dict.get(name)
# 修改數據
teacher = input("請輸入授課老師:")
maxsize = input("請輸入人數上限:")
times = input("請輸入課程課時:")
score = input("請輸入課程學分:")
desc = input("請輸入課程描述:")
# 保存課程數據
course.update()
input("課程修改完成,按任意鍵繼續。")
def delete_course(self):
"""刪除課程的方法"""
name = input("請輸入要刪除的課程名稱:")
if name not in Database.courses_dict:
print("要刪除的課程不存在,按任意鍵重新輸入")
return self.delete_course()
# 直接刪除課程:直接從字典中根據課程key刪除了鍵值對
Database.courses_dict.pop(name)
input("課程刪除完成,按任意鍵繼續。")
class Course:
"""課程類型"""
def __init__(self, name, teacher, maxsize, times, score, desc):
self.name = name # 課程名稱
self.teacher = teacher # 授課老師
self.maxsize = maxsize # 選擇人數限制
self.times = times # 授課課時
self.score = score # 課程學分
self.desc = desc # 課程描述
def save(self):
"""保存課程數據"""
Database.courses_dict[self.name] = self
def update(self):
"""修改課程數據"""
# 重新保存數據時,因爲key沒有變化,所以新的數據會覆蓋舊的數據
self.save()
def delete(self):
"""刪除課程"""
# 通過key刪除字典中的一個鍵值對
Database.courses_dict.pop(self.name)
def __str__(self):
return f"課程[名稱:{self.name}, 老師:{self.teacher},描述:{self.desc}]"
# 創建管理員對象
manager = Manager("wenwen", "123", "穩穩", "[email protected]", "15666666666")
# 添加課程
manager.add_course()
# 查看課程
manager.check_course()