Python面向?qū)ο蟪绦蛟O(shè)計(jì)類的多態(tài)用法詳解
本文實(shí)例講述了Python面向?qū)ο蟪绦蛟O(shè)計(jì)類的多態(tài)用法。分享給大家供大家參考,具體如下:
多態(tài)
1、多態(tài)使用
一種事物的多種體現(xiàn)形式,舉例:動(dòng)物有很多種
注意: 繼承是多態(tài)的前提
函數(shù)重寫(xiě)就是多態(tài)的體現(xiàn)形式
演示:重寫(xiě)Animal類
第一步:先定義貓類和老鼠類,繼承自object,在其中書(shū)寫(xiě)構(gòu)造方法和eat方法
第二步: 抽取Animal父類,定義屬性和eat方法,貓類與老鼠類繼承即可
第三步: 定義人類,在其中分別定義喂貓和喂老鼠的方法
第四步:使用多態(tài),將多個(gè)喂的方法提取一個(gè)。
# 測(cè)試類 from cat import Cat from mouse import Mouse from person import Person ''' 多態(tài): 一種事物的多種狀態(tài) 需求:人可以喂任何一種動(dòng)物 ''' #創(chuàng)建貓和老鼠的對(duì)象 tom = Cat("tom") jerry = Mouse("jerry") #調(diào)用各自的方法 tom.eat() jerry.eat() #定義了一個(gè)有name屬性和eat方法的Animal類,讓所有的動(dòng)物類都繼承自Animal. #定義一個(gè)人類,可以喂貓和老鼠吃東西 per = Person() #per.feedCat(tom) #per.feedMouse(jerry) #思考:人要喂100種動(dòng)物,難道要寫(xiě)100個(gè)feed方法嗎? #前提:tom和jerry都繼承自動(dòng)物 per.feedAnimal(tom) per.feedAnimal(jerry)
輸出:
tom吃
jerry吃
給你食物
tom吃
給你食物
jerry吃
#animal.py文件中的動(dòng)物類 class Animal(object): def __init__(self, name): self.name = name def eat(self): print(self.name + "吃")
#cat.py文件中的貓類 class Cat(Animal): def __init__(self, name): #self.name = name super(Cat,self).__init__(name)
#mouse.py中的老鼠類 class Mouse(Animal): def __init__(self, name): #self.name = name super(Mouse,self).__init__(name)
#person.py中的人類 class Person(object): def feedAnimal(self, ani): print("給你食物") ani.eat()
2、對(duì)象屬性與類屬性
對(duì)象屬性和類屬性的區(qū)別:
a.定義的位置不同,類屬性是直接在類中的屬性,對(duì)象屬性是在定義在構(gòu)造方法中的屬性;
b.對(duì)象屬性使用對(duì)象訪問(wèn),類屬性使用類名訪問(wèn);
c.在內(nèi)存中出現(xiàn)的時(shí)機(jī)不同[類屬性隨著類的加載而出現(xiàn),對(duì)象屬性隨著對(duì)象的創(chuàng)建而出現(xiàn)];
d.優(yōu)先級(jí)不同,對(duì)象屬性的優(yōu)先級(jí)高于類屬性。
class Person(object): #1.定義位置 #類屬性:直接定義在類中的屬性 name = "person" def __init__(self, name): #對(duì)象屬性:定義在構(gòu)造方法中的屬性 self.name = name #2.訪問(wèn)方式 print(Person.name) per = Person("tom") #對(duì)象屬性的優(yōu)先級(jí)高于類屬性 print(per.name) #動(dòng)態(tài)的給對(duì)象添加對(duì)象屬性 per.age = 18 #只針對(duì)當(dāng)前對(duì)象生效,對(duì)于類創(chuàng)建的其他對(duì)象沒(méi)有作用 print(Person.name) per2 = Person("lilei") #print(per2.age) #沒(méi)有age屬性 #刪除對(duì)象中的name屬性,再調(diào)用會(huì)使用到同名的類屬性 del per.name print(per.name) #注意事項(xiàng):不要將對(duì)象屬性與類屬性重名,因?yàn)閷?duì)象屬性會(huì)屏蔽掉類屬性,但是當(dāng)刪除對(duì)象屬性之后,再使用就能使用到類屬性了.
輸出:
person
tom
person
person
3、動(dòng)態(tài)添加屬性和方法
正常情況下,我們定義了一個(gè)class,創(chuàng)建一個(gè)class的實(shí)例后,我們可以給該實(shí)例綁定任何的的屬性和方法,這就是動(dòng)態(tài)語(yǔ)言的靈活性。
python語(yǔ)言的特點(diǎn):靈活。
這里說(shuō)的動(dòng)態(tài)添加屬性和方法主要指的是關(guān)于slots
函數(shù)的使用
from types import MethodType #定義一個(gè)空類 ''' class Person(): pass ''' class Person(object): __slots__ = ("name","age","speak","hobby") pass # 動(dòng)態(tài)添加屬性[體現(xiàn)了動(dòng)態(tài)語(yǔ)言的特點(diǎn):靈活性] per = Person() per.name = "tom" print(per.name) #動(dòng)態(tài)添加方法 def say(self): print("my name is "+ self.name) per.speak = say per.speak(per) #這樣實(shí)現(xiàn)不好,所以引入MethodType def hobby(self): print("my hobby is running") per.hobby = MethodType(hobby,per) per.hobby()
輸出:
tom
my name is tom
my hobby is running
但是,給一個(gè)實(shí)例綁定的方法對(duì)另外一個(gè)實(shí)例是不起作用的。
為了給所有的實(shí)例都綁定方法,可以通過(guò)給class綁定方法
#動(dòng)態(tài)添加方法 def say(self,name): self.name = name print("my name is "+ self.name) Person.speak = say per2 = Person() per2.speak('hh')
輸出:
my name is hh
給class綁定方法后,所有的實(shí)例均可調(diào)用。
4、slots
通常情況下,上面的say方法可以直接定義在class中,但動(dòng)態(tài)綁定允許我們?cè)诔绦蛟谶\(yùn)行的過(guò)程中動(dòng)態(tài)的給class添加功能,這在靜態(tài)語(yǔ)言中很難實(shí)現(xiàn)。
如果我們想限制實(shí)例的屬性怎么辦?
比如,只允許給Person實(shí)例添加name,age屬性,為了達(dá)到限制的目的,Python中允許在定義class的時(shí)候,定義一個(gè)特殊的變量【slots】變量,來(lái)限制該class添加的屬性
class Person(object): __slots__=("name","age") #[不想無(wú)限制的任意添加屬性] #比如,只允許給對(duì)象添加name, age屬性 #解決:定義類的時(shí)候,定義一個(gè)特殊的屬性(__slots__),可以限制動(dòng)態(tài)添加的屬性范圍 per = Person() per.height = 170 print(per.height)
這樣做會(huì)報(bào)錯(cuò)
AttributeError: 'Person' object has no attribute 'height'
使用slots的時(shí)候需要注意,slots定義的屬性僅僅對(duì)當(dāng)前類的實(shí)例起作用,對(duì)繼承的子類是不起作用的。
除非在子類中也定義slots,這樣子類實(shí)例允許定義的屬性就是自身的slots加上父類的slots。
總結(jié):
__slots__
:
語(yǔ)法:
__slots__ = (屬性名1,屬性名2,...)
作用:
限制類的屬性名
注意:當(dāng)子類沒(méi)有添加slots時(shí),子類繼承父類的時(shí)候,它的屬性名不受父類的影響
若子類中也添加slots,子類的限制應(yīng)該是父類的slots與子類slots的并集
5、@property
綁定屬性時(shí),如果我們直接把屬性暴露出去,雖然寫(xiě)起來(lái)簡(jiǎn)單,但是沒(méi)有辦法檢查參數(shù),導(dǎo)致可以隨意的更改。
比如:
p = Person() p.age = -1
這顯然不合常理,為了限制age的范圍,我們可以通過(guò)setAge()
的方法來(lái)設(shè)置age,再通過(guò)getAge()
的方法獲取age,這樣在setAge()
中就可以檢查輸入的參數(shù)的合理性了。
class Person(object): def __init__(self, name, age): # 屬性直接對(duì)外暴露 # self.age = age # 限制訪問(wèn) self.__age = age self.__name = name # self.__name = name def getAge(self): return self.__age def setAge(self, age): if age < 0: age = 0 self.__age = age # 通過(guò)@property和@age.setter改變?cè)瓉?lái)的get/set方法 # 方法名為受限制的變量去掉雙下劃線 # 相當(dāng)于get方法 @property def age(self): return self.__age # 相當(dāng)于set的方法 @age.setter # 去掉下劃線.setter def age(self, age): if age < 0: age = 0 self.__age = age @property def name(self): return self.__name @name.setter def name(self, name): self.__name = name per = Person("lili", 18) # 屬性直接對(duì)外暴露 # 不安全,沒(méi)有數(shù)據(jù)的過(guò)濾 # per.age = -10 # print(per.age) # 使用限制訪問(wèn),需要自己寫(xiě)set和get的方法才能訪問(wèn) # 劣勢(shì):麻煩,代碼不直觀 # 思考問(wèn)題:如果我就想使用對(duì)象"."的方式訪問(wèn)對(duì)象的私有屬性,怎么辦? # per.setAge(15) # print(per.getAge()) # property:可以讓你對(duì)受限制訪問(wèn)的屬性使用"."語(yǔ)法 per.age = 80 # 相當(dāng)于調(diào)用setAge print(per.age) # 相當(dāng)于調(diào)用getAge print(per.name)
輸出:
80
lili
property
總結(jié)語(yǔ)法:
針對(duì)私有化的屬性添加的。
@property def 屬性名(self): return self.__屬性名
@屬性名.setter def 屬性名(self, 值): #業(yè)務(wù)邏輯處理 self.屬性名 = 值
總結(jié):
a.裝飾器(decorator)可以給函數(shù)動(dòng)態(tài)加上功能,對(duì)于類的方法,裝飾器一樣起作用,python內(nèi)置的@property裝飾器就是負(fù)責(zé)把一個(gè)方法變成屬性調(diào)用的。
b.@property的實(shí)現(xiàn)比較復(fù)雜,我們先考慮如何使用,把一個(gè)getter方法變成屬性,只需要加上@property就可以了,此時(shí)@property本身又創(chuàng)建了另一個(gè)裝飾器@屬性setter,負(fù)責(zé)把一個(gè)setter方法變成屬性賦值.
c.@property廣泛應(yīng)用在類的定義中,可以讓調(diào)用者寫(xiě)出簡(jiǎn)短的代碼,同時(shí)保證對(duì)參數(shù)進(jìn)行必要的檢查,這樣,程序運(yùn)行時(shí)就減少了出錯(cuò)的可能性。
6、運(yùn)算符重載
類可以重載加減運(yùn)算,打印,函數(shù)調(diào)用,索引等內(nèi)置運(yùn)算,運(yùn)算符重載使我們的對(duì)象的行為與內(nèi)置函數(shù)一樣,在python調(diào)用時(shí)操作符會(huì)自動(dòng)的作用于類的對(duì)象,python會(huì)自動(dòng)的搜索并調(diào)用對(duì)象中指定的方法完成操作。
1、常見(jiàn)運(yùn)算符重載方法
常見(jiàn)運(yùn)算符重載方法
方法名 | 重載說(shuō)明 | 運(yùn)算符調(diào)用方式 |
---|---|---|
init | 構(gòu)造函數(shù) | 對(duì)象創(chuàng)建: X = Class(args) |
del | 析構(gòu)函數(shù) | X對(duì)象收回 |
add sub | 加減運(yùn)算 | X+Y, X+=Y/X-Y, X-=Y |
or | 運(yùn)算符| | X|Y, X|=Y |
str_ repr | 打?。D(zhuǎn)換 | print(X)、repr(X)/str(X) |
call | 函數(shù)調(diào)用 | X(*args, **kwargs) |
getattr | 屬性引用 | X.undefined |
setattr | 屬性賦值 | X.any=value |
delattr | 屬性刪除 | del X.any |
getattribute | 屬性獲取 | X.any |
getitem | 索引運(yùn)算 | X[key],X[i:j] |
setitem | 索引賦值 | X[key],X[i:j]=sequence |
delitem | 索引和分片刪除 | del X[key],del X[i:j] |
len | 長(zhǎng)度 | len(X) |
bool | 布爾測(cè)試 | bool(X) |
lt gt le ge eq ne | 特定的比較 | 依次為XY,X<=Y,X>=Y, X==Y,X!=Y 注釋:(lt: less than, gt: greater than, le: less equal, ge: greater equal, eq: equal, ne: not equal ) |
radd | 右側(cè)加法 | other+X |
iadd | 實(shí)地(增強(qiáng)的)加法 | X+=Y(or else add) |
iter next | 迭代 | I=iter(X), next() |
contains | 成員關(guān)系測(cè)試 | item in X(X為任何可迭代對(duì)象) |
index | 整數(shù)值 | hex(X), bin(X), oct(X) |
enter exit | 環(huán)境管理器 | with obj as var: |
get set delete | 描述符屬性 | X.attr, X.attr=value, del X.attr |
new | 創(chuàng)建 | 在init之前創(chuàng)建對(duì)象 |
# 舉例 # 數(shù)字和字符串都能相加 #print(1 + 2) #print("1" + "2") # 不同的類型用加法會(huì)有不同的解釋 class Person(object): def __init__(self, num): self.num = num # 運(yùn)算符重載 def __add__(self, other): return Person(self.num + other.num) # 方法重寫(xiě) def __str__(self): return "num = " + str(self.num) # 如果兩個(gè)對(duì)象相加會(huì)怎樣? # 對(duì)象相加,編譯器解釋不了,所以就要用到運(yùn)算符重載 per1 = Person(1) per2 = Person(2) print(per1 + per2) # 結(jié)果為地址:per1+per2 === per1.__add__(per2),如果想得到num的和則重寫(xiě)str方法 # 上述打印就等價(jià)于:print(per1.__add__(per2)),只不過(guò)add方法會(huì)自動(dòng)調(diào)用 print(per1) print(per2)
輸出:
num = 3
num = 1
num = 2
更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python面向?qū)ο蟪绦蛟O(shè)計(jì)入門(mén)與進(jìn)階教程》、《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》、《Python編碼操作技巧總結(jié)》及《Python入門(mén)與進(jìn)階經(jīng)典教程》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
- Python面向?qū)ο蟪绦蛟O(shè)計(jì)之私有變量,私有方法原理與用法分析
- Python面向?qū)ο蟪绦蛟O(shè)計(jì)之繼承、多態(tài)原理與用法詳解
- Python面向?qū)ο蟪绦蛟O(shè)計(jì)之靜態(tài)方法、類方法、屬性方法原理與用法分析
- Python面向?qū)ο蟪绦蛟O(shè)計(jì)之類和對(duì)象、實(shí)例變量、類變量用法分析
- Python3.5面向?qū)ο蟪绦蛟O(shè)計(jì)之類的繼承和多態(tài)詳解
- Python面向?qū)ο蟪绦蛟O(shè)計(jì)類的封裝與繼承用法示例
- Python面向?qū)ο蟪绦蛟O(shè)計(jì)類變量與成員變量、類方法與成員方法用法分析
- Python面向?qū)ο蟪绦蛟O(shè)計(jì)構(gòu)造函數(shù)和析構(gòu)函數(shù)用法分析
- Python面向?qū)ο蟮某绦蛟O(shè)計(jì)詳情
相關(guān)文章
Python Sqlalchemy如何實(shí)現(xiàn)select for update
這篇文章主要介紹了Python Sqlalchemy如何實(shí)現(xiàn)select for update,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10pytorch 如何使用amp進(jìn)行混合精度訓(xùn)練
這篇文章主要介紹了pytorch 使用amp進(jìn)行混合精度訓(xùn)練的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-05-05django 多數(shù)據(jù)庫(kù)及分庫(kù)實(shí)現(xiàn)方式
這篇文章主要介紹了django 多數(shù)據(jù)庫(kù)及分庫(kù)實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04python自動(dòng)填寫(xiě)問(wèn)卷星問(wèn)卷以及提交問(wèn)卷等功能
這篇文章主要給大家介紹了關(guān)于python自動(dòng)填寫(xiě)問(wèn)卷星問(wèn)卷以及提交問(wèn)卷等功能的相關(guān)資料,包括使用Selenium庫(kù)模擬瀏覽器操作、定位元素、填寫(xiě)表單等,通過(guò)本文的學(xué)習(xí),讀者可以了解如何利用Python自動(dòng)化技術(shù)提高問(wèn)卷填寫(xiě)效率,需要的朋友可以參考下2023-03-03pandas?實(shí)現(xiàn)?in?和?not?in?的用法及使用心得
pandas按條件篩選數(shù)據(jù)時(shí),除了使用query()方法,還可以使用isin和對(duì)isin取反進(jìn)行條件篩選,今天通過(guò)本文給大家介紹pandas?實(shí)現(xiàn)?in?和?not?in?的用法及使用心得,感興趣的朋友跟隨小編一起看看吧2023-01-01