12.25 面向對象高階-描述符與設計模式

描述符

當一個類中,包含了三個魔術方法(__get__,__set__,__delete__)之一,或者全部時,那麼這個類就稱為描述符類

作用

描述符的作用就是對一個類中的某個成員進行一個詳細的管理操作(獲取,賦值,刪除) 描述符就是代理了一個類中的成員的操作,描述符屬於類,只能定義為類的屬性

三個魔術方法

<code>'''
__get__(self, instance, owner)
\t觸發機制:在訪問對象成員屬性時自動觸發(當該成員已經交給描述符管理時)
\t作用:設置當前屬性獲取的值
\t參數:1. self 描述符對象 2.被管理成員的類的對象。3.被管理成員的類
\t返回值:返回值作為成員屬性獲取的值
\t注意事項:無
__set__(self, instance, value)
\t觸發機制:在設置對象成員屬性時自動觸發(當該成員已經交給描述符管理時)
\t作用:對成員的賦值進行管理
\t參數:1. self 描述符對象 2.被管理成員的類的對象。3.要設置的值
\t返回值:無
\t注意事項:無
__delete__(self, instance)
\t觸發機制:在刪除對象成員屬性時自動觸發(當該成員已經交給描述符管理時)

\t作用:對成員屬性的刪除進行管理
\t參數:1. self 描述符對象 2.被管理成員的類的對象。
\t返回值:無
\t注意事項:無
'''/<code>

數據描述符:(完整)

同時具備三個魔術方法的類就是 數據描述符

非數據描述符:(不完整)

沒有同時具備三個魔術方法的類就是 非描述符類

基本使用格式

把當前的描述符類賦值給一個需要代理的類中的成員屬性

代碼示例:

<code># 定義描述符類
class PersonName():
__name = 'abc'

def __get__(self, instance, owner):
# print(self,instance,owner)
return self.__name

def __set__(self, instance, value):
# print(self,instance,value)
self.__name = value

def __delete__(self, instance):
# print(self,instance)
# del self.__name
print('不允許刪除')


# 定義的普通類
class Person():
# 把類中的一個成員屬性交給一個描述符類來實現
# 一個類中的成員的值是另一個描述符類的對象()
# 那麼當對這個類中得成員進行操作時,可以理解為就是對另一個對象的操作
name = PersonName()

# 實例化對象
zs = Person()
print(zs.name)
zs.name = '張三'
print(zs.name)
del zs.name
print(zs.name)/<code>

描述符應用解析

<code>#定義一個學生類,需要記錄 學員的id,名字,分數
class Student():
def __init__(self,id,name,score):
self.id = id
self.name = name
self.score = score

def returnMe(self):
info = f'''
學員編號:{self.id}
學員姓名:{self.name}
學員分數:{self.score}
'''
print(info)
'''
# 要求:學員的分數只能在0-100範圍中
解決方法:
1。在__init__方法中檢測當前分數範圍

# 檢測分數範圍
if score >= 0 and score <= 100:
self.score = score
這個解決方案只能在對象初始化時有效。
2。 定義一個setattr魔術方法檢測
檢測如果給score分數進行賦值時,進行分數的檢測判斷
def __setattr__(self, key, value):
# 檢測是否是給score進行賦值操作
if key == 'score':
# 檢測分數範圍
if value >= 0 and value <= 100:
object.__setattr__(self, key, value)
else:
print('當前分數不符合要求')
else:
object.__setattr__(self,key,value)

假如 學員的分數不止一個時怎麼辦,比如 語文分數,數學分數,英語分數
另外就是當前這個類中的代碼是否就比較多了呢?

3。可以思考使用描述符來代理我們的分數這個屬性
1.定義Score描述符類
2.把學生類中的score這個成員交給描述符類進行代理
3.只要在代理的描述符類中對分數進行賦值和獲取就ok了
'''

#定義描述符類 代理分數的管理
class Score():

def __get__(self, instance, owner):
return self.__score
def __set__(self, instance, value):
if value >= 0 and value <= 100:
self.__score = value
else:
print('分數不符合要求')

# 使用描述符類代理score分數屬性
class Student():
score = Score()
def __init__(self,id,name,score):
self.id = id
self.name = name
self.score = score

def returnMe(self):
info = f'''
學員編號:{self.id}
學員姓名:{self.name}
學員分數:{self.score}
'''
print(info)



# 實例化對象
zs = Student(1011,'張三瘋',99)
zs.returnMe()
zs.score = -20
zs.score = 88
zs.returnMe()
/<code>

描述符的三種定義格式

<code># 格式一 通過定義 描述符類來實現  推薦
'''
class ScoreManage():
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):

pass

class Student():
score = ScoreManage()
'''

# 格式二, 使用 property 函數 來實現
'''
class Student():

# 在當前需要被管理的類中 直接定義類似下面三個方法
def getscore(self):
print('getscore')

def setscore(self,value):
print('setscore',value)

def delscore(self):
print('delscore')

# 在 property 函數中指定對應的三個方法,對應的方法 1。__get__,2。__set__,3。__delete__
score = property(getscore,setscore,delscore)

zs = Student()
# print(zs.score)
# zs.score = 200
# del zs.score
'''

# 格式三 使用 @property 裝飾器語法來實現
'''
class Student():
__score = None

@property
def score(self):
print('get')
return self.__score

@score.setter
def score(self,value):
print('set')
self.__score = value

@score.deleter
def score(self):
print('delete')
del self.__score

zs = Student()
# print(zs.score)
zs.score = 199
print(zs.score)
del zs.score
'''/<code>

設計模式

設計模式是前人為完成某個功能或需求,根據經驗和總結,對實現的代碼步驟和代碼設計進行了總結和歸納,成為了實現某個需求的經典模式。

設計模式並不是固定的代碼格式,而是一種面向對象編程的設計

單例(單態)設計模式

在當前腳本中,同一個類只能創建出一個對象去使用。這種情況就成為單例(單態)。

<code>'''
實現單例的案例,思考:
單例和婚姻法的關係,特別像,一個人只能有一個結婚對象
在社會中是如何完成一夫一妻制的?
如果要結婚,必須要到 民政局 登記
民政局 需要檢測兩個人的戶口本,看上面是否屬於 結婚的狀態

如果是已婚,肯定就攆出去了。
如果沒有結婚,可以給蓋個章了,開始登記。

那麼按照這樣的思路如何去實現 python中的單例設計模式呢?

1。需要有一個方法,可以去控制當前對象的創建過程?
構造方法 __new__
2。需要有一個標示來存儲和表示是否有對象
創建一個私有屬性 進行存儲,默認值為None
3。在創建對象的方法中去檢測和判斷是否有對象?
如果沒有對象,則創建對象,並且把對象存儲起來,返回對象
如果存儲的是對象,則直接返回對象,就不需要創建新的對象了
'''

class Demo():
# 2.定義私有屬性存儲對象,默認值為None
__obj = None

# 1.定義構造方法
def __new__(cls, *args, **kwargs):
# 3。在創建對象的過程中,判斷是否有對象
if not cls.__obj:
# 判斷如果沒有對象,則創建對象,並且存儲起來
cls.__obj = object.__new__(cls)
# 直接把存儲的對象返回

return cls.__obj


# 實例化對象
a = Demo()
b = Demo()
print(a)
print(b)
'''
<__main__.demo object="" at="">
<__main__.demo object="" at="">
'''/<code>

Mixin 混合設計模式

Mixin類

Mixin 必須是表示一種功能,而不是一個對象。

Mixin 的功能必須單一,如果有多個功能,那就多定義Mixin類

python 中的Mixin是通過多繼承實現的

Mixin 這個類通常不單獨使用,而是混合到其它類中,去增加功能的

Mixin 類不依賴子類的實現,即便子類沒有繼承這個Mixin,子類也能正常運行,可能就是缺少了一些功能。。

使用Mixin混入類的好處?

1.Mixin 這個混入類的設計模式,在不對類的內容修改的前提下,擴展了類的功能

2.Mixin 混入類為了提高代碼的重用性,使得代碼結構更加簡單清晰

3.可以根據開發需要任意調整功能(創建新的Mixin混入類)避免設計多層次的複雜的繼承關係。

示例:

<code>'''
繼承需要有一個必要的前提,繼承應該是一個 'is-a' 的關係
例如:
蘋果可以去繼承水果,因為蘋果就是一個水果
蘋果不能繼承午飯,因為午飯可以有蘋果也可以沒有

比如 汽車可以繼承 交通工具,因為汽車本身就是一個交通工具

交通工具有哪些?
汽車,飛機,直升機,這些都屬於 交通工具
那麼如何去設計這些類的關係呢?
比如創建一個交通工具類,然後屬於交通工具的都來繼承,再去實現。。。
但是,飛機和直升機都有飛行的功能,而汽車並沒有,那麼在交通工具中如果去定義 飛行這個功能,那就不太合適了。。
能不能在飛機和直升機類中分別實現 飛行 這個功能呢?可以,但是代碼又無法重用。


怎麼辦?
單獨去定義交通工具類,和 飛行器 這個兩個父類,這樣飛機和直升機就可以去繼承這兩個類.
'''

# 交通工具 vehicle
class vehicle():
# 運輸貨物
def huo(self):
print('運輸貨物')
# 搭載乘客
def ren(self):
print('搭載乘客')

# 飛行器
class FlyingMixin():
def fly(self):
print('可以起飛了。。。')

# 定義汽車類
class cart(vehicle):
pass

# 定義飛機
class airplane(vehicle,FlyingMixin):
pass

# 定義直升機
class helicopter(vehicle,FlyingMixin):
pass

# 此時去定義一個飛行器的類 Flying,讓需要飛行的交通工具,直接繼承這個類。可以解決這個問題。
# 但是,1。出現類多繼承,違背了'is-a' 2。飛行器這個類很容易被誤解
# 解決方案也是使用多繼承,但是給飛行器這個類,定義成為一個 Mixin 混合類,

# 此時就是等於把飛行器這個類,作為了一個擴展的功能,來擴展其它類

'''
在上面的代碼中,雖然直升機和飛機都使用了多繼承,也就是繼承了FlyingMixin
但是由於 FlyingMixin 類加了 Minin這個名,就告訴了後面閱讀代碼的人,這個類是一個Mixin類
'''/<code>

抽象類(瞭解)

<code>抽象類是一個特殊的類:
1. 抽象類不能用,不能直接實例化成為一個對象。
2. 抽象類中包含了抽象方法,抽象方法就是沒有實現代碼的方法。
3. 抽象類需要子類繼承,並重寫父類的抽象方法。才可以使用。/<code>
<code>抽象類,一般應用在程序設計,程序設計中一般是要對功能和需求進行規劃,其中有一些需求是明確的並且可以完成的,
但是也可能會有一些需求是不明確的,或者不確定具體需要怎麼實現,
那麼此時就可以把這個不確定怎麼實現或者需要後面再去實現的方法,定義為抽象方法(只定義方法名,不寫具體代碼)

抽象類的應用:
例如要開發一個框架,這個框架要有一些基本功能和擴展功能。。。。
但是你具體用這個框架開發什麼樣的產品,開發框架的人並不清楚或者確定。
因此框架就具備一定的功能,並且留下來一些方法的定義,剩下的就是需要自己在方法中具體實現自己業務邏輯。/<code>

抽象類的定義:

<code>import abc

# 如果要定義為抽象類,那麼這個類的 metaclass屬性必須是 metaclass=abc.ABCMeta
class WriteCode(metaclass=abc.ABCMeta):

#需要抽象的方法,使用裝飾器進行裝飾
@abc.abstractmethod
def write_php(self):
pass

def write_java(self):
print('實現了java代碼的開發')

def write_python(self):
print('實現了python代碼的開發')


# 抽象類不能直接實例化對象
# obj = WriteCode()
# print(obj)
#TypeError: Can't instantiate abstract class WriteCode with abstract methods write_php


# 定義子類,繼承抽象類,並實現抽象類中的抽象方法

class Demo(WriteCode):
def write_php(self):
print('實現了php代碼的開發')

a = Demo()
print(a)
a.write_java()
a.write_php()
a.write_python()/<code>


分享到:


相關文章: