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

