1、多態
一、什麼是多態:一個類表現出的多種狀態--->經過繼承來實現的
例如:
class Animal:pass
class Dog(Animal):pass
class Cat(Animal):pass
Animal類表現出了Dog,Cat兩種形態java
好了,多態就是這樣,結束...python
哈哈,有沒看懂的吧,那麼咱們一步一步來解釋:算法
首先咱們來看:
s = 'abc'
print(type(s)) # <class 'str'>編程
s = 'abc' 咱們都知道s就是一個str類型,在python中一切皆對象,其實你打開源碼能夠看到str是一個類,
它在源碼中是這樣定義的:class str(object): ...
而咱們定義一個變量的時候s = 'abc' ,它在內部是進行了一個這樣的轉換:s = str('abc')
剛纔說了str是一個類,那str()就是實例化 , str('abc')就是把參數'abc'傳進去初始化一個對象,那麼s就是一個str類的對象,s的type就是str,
也就是說其實咱們平時定義的變量都是對象,例如:
a = 10 是一個int類的對象
b = [1,2,3] 是一個list類的對象等等app
那麼咱們本身定義的類也是同樣的:
class A:
pass函數
a = A()
print(type(a)) # 對象a的數據類型就是Apost
二、咱們引用一下java中的例子再來了解一下(假設下面是java代碼)this
class ApplePay: def pay(self): pass 在java中,參數是要指定類型的,其餘類型的數據是不能傳進去的,這裏指定了是ApplePay類型 def My_pay(ApplePay obj,int money): obj.pay() app = ApplePay() # app是ApplePay類的對象,因此app就是ApplePay類型的數據 My_pay(app,100) 這麼一看,好像沒什麼問題,可是這麼寫的話,這個My_pay函數只能給ApplePay類使用了,其餘類就不能使用了, 擴展性不好,那麼這個時候就須要多態了,且看下面例子: class Payment: # 首先定義一個父類 pass class ApplePay(Payment): #每一個子類都繼承父類 def pay(self): pass class Alipay(Payment): #每一個子類都繼承父類 def pay(self): pass #這裏obj類型寫父類的類型Payment,由於子類繼承父類,也繼承了父類的類型 def My_pay(Payment obj,int money): obj.pay() app = ApplePay() # 這裏app既是ApplePay類型,也是Payment類型 My_pay(app,100) alp = Alipay() # 這裏alp既是Alipay類型,也是Payment類型 My_pay(alp,200) 這樣利用多態就解決了參數類型的問題
三、總結(爲何在python中,咱們寫的函數都不用指定類型?)
1,
在java中:多態用來解決傳參數的時候數據類型的規範問題。
在java中的表現 : 在一個函數中須要給參數指定數據類型,若是這個地方能夠接收兩個以上類型的參數,
那麼這些類型應該有一個父類,這個父類是全部子類對象的類型spa
2,
在python中:函數的參數不須要指定數據類型,咱們也不須要經過繼承的形式來統一數據類型,
由於python中全部的對象都繼承了object類,因此都是object類型,所以在python當中到處都是多態。code
四、鴨子類型(拓展)
定義:
不是明確的經過繼承實現的多態,
而是經過一個模糊的概念來判斷這個函數能不能接收這個類型的參數
例如:
len() # str list tuple dict set range(3)--->這些對象就是鴨子類型
print() # print什麼類型都能打印,全部的對象都是鴨子類型
2、封裝
一、定義:
廣義上的封裝:把屬性和函數都放到類裏
狹義上的封裝:定義私有成員
1,廣義上的封裝:
class 類名:
def 方法1(self):pass
是爲了只有這個類的對象才能使用定義在類中的方法
2,狹義上的封裝: __屬性名 __方法名
把一個屬性(方法)藏在類中
class Goods:
__discount = 0.7 # 私有的靜態變量
print(Goods.__discount) # 報錯
在類的外部不能引用私有的靜態變量
class Goods:
__discount = 0.7 # 私有的靜態變量
print(__discount) # 類內部打印輸入
程序一執行就打印出0.7
由於:
類中的靜態變量和方法名在程序加載的過程當中就已經執行完了,不須要等待調用
在這個類加載完成以前,Goods這個名字尚未出如今內存空間中
私有的靜態屬性能夠在類的內部使用,用來隱藏某個變量的值
3,變形
其實用雙下劃線定義好一個私有變量後,python會自動把這個私有變量進行一個變形,例如私有變量__discount,變形以下:
__discount ---> _Goods__discount
也就是:
__私有變量 ---> _類名__私有變量
class Goods:
__discount = 0.7 # 私有的靜態變量
print(Goods.__dict__) # 查看Goods類的全部屬性和方法能夠看到裏面有個變量_Goods__discount的值是0.7
print(Goods._Goods__discount) # 能夠看到0.7 但從編程規範的角度上出發 咱們不能在類的外部使用私有的變量
二、
類中的私有成員:
1,私有的靜態屬性
2,私有的對象屬性
3,私有的方法
爲何要定義一個私有變量呢:
1,不想讓你修改這個值
2,不想讓你看到這個值
3,想讓你在修改這個值得時候有一些限制
4,有些方法或者屬性不但願被子類繼承
話很少說,請看例子理解
例1:私有的靜態屬性、不想讓你修改這個值
class Student: def __init__(self,name,age): self.__name = name # 設置了一個私有變量__name用來儲存學生的名字 self.age = age def name(self): # 由於私有變量在外部不能引用,全部設置一個方法用來打印學生的名字 return self.__name xiaoming = Student('xiaoming',18) print(xiaoming.age) # 18 print(xiaoming.name()) # xiaoming xiaoming.age = 28 print(xiaoming.age) # 28 xiaoming.name = 'xiaogou' #這裏只是爲這個對象新增了一個屬性name,並無修改本來的__name print(xiaoming.name()) #報錯 那麼確定會有人問,這樣作確實不能修改__name,可是別人能夠新增name啊,並且__name還要用name()這樣才能顯示,多麻煩。 好,這個問題等下在後面的內容會給你解決,如今你只要明確地知道你一開始傳進去的學生名字__name是修改不了就好了。
例2:私有的靜態屬性、修改的時候有限制
class Goods: __discount = 0.7 # 私有的靜態變量 def __init__(self,name,price): self.name = name self.__price = price def price(self): #折扣價 return self.__price * Goods.__discount def change_price(self,new_price): if type(new_price) is int: self.__price = new_price else: print('本次價格修改不成功') apple = Goods('蘋果',5) print(apple.price()) apple.change_price(6) print(apple.price())
例3:私有的對象屬性、不想讓你看到這個值(經過算法把密碼變成密文的形式)
class User: def __init__(self,username,password): self.username = username self.__pwd = password self.pwd = self.__getpwd() def __getpwd(self): # 把密碼轉換成哈希值 return hash(self.__pwd) obj = User('xiaoming','123456') # 用戶名:xiaoming 密碼: 123456 print(obj.username,obj.pwd) # 用戶名:xiaoming 密碼的密文:-1675187800842546722
三、私有變量能不能在外部被定義?(不能)
class A: __country = 'China' print(A.__dict__) A.__Language = 'Chinese' # 其實這裏只是定義了一個名爲__Language的屬性,並非私有變量 print(A.__dict__)
四、私有變量能不能被繼承?(能夠)
class A: __country = 'China' # __country 就是 _A__country def __init__(self,name): self.__name = name # __name 就是 _A__name 繼承A的B類的對象初始化的時候 self.__name 就是 # self._A__name # 此時爲self._A__name賦值,並無造成self的私有變量 # 而自己的A類對象初識化的時候,爲self.__name賦值,會造成self的私有變量 class B(A): def get_country(self): print(__country) # 報錯:name '_B__country' is not defined print(B._A__country) # China def get_name(self): return self.__name # 報錯:'B' object has no attribute '_B__name' b = B('xiaoming') b.get_country() print(b.get_name()) print(b.__dict__) # {'_A__name': 'xiaoming'}
總結:
子類能夠繼承父類的全部屬性(包括私有屬性)和方法,可是繼承的私有屬性是變形後的形式,在子類中再也不是私有屬性,
在子類中用 __私有屬性名 是找不到繼承的私有屬性的 由於它會默認去找本身類中的私有屬性,
要想調用父類的私有屬性則應該用變形後的變量 _父類名__變量名
3、內置函數property
一、
裝飾器的分類:
裝飾函數:
裝飾方法 : property
裝飾類:
裝飾器函數都怎麼用:
在函數、方法、類的上面一行直接@裝飾器的名字
二、property是一個裝飾器函數(就是用來解決剛纔私有屬性的問題)
property:將一個方法假裝成一個屬性
2-1學生信息中,姓名不想給別人修改,就設置爲私有屬性,在定義一個同名的方法假裝成屬性,便於查找
class Student: def __init__(self,name,age): self.__name = name self.age = age @property def name(self): # 聲明瞭@property使用此方法的時候就能夠不寫括號,就假裝成了屬性 return self.__name xiaoming = Student('小明',18) print(xiaoming.name) # 小明 xiaoming.name = '小狗' # 改:報錯 print(xiaoming.name)
2-2圓的半徑能夠修改,可是面積和周長應該是屬性的形式比較正確,可是直接設置爲屬性,圓的半徑改了後,
周長和麪積並不會改變
class Circle: def __init__(self,r): self.r = r self.area = 3.14 * self.r ** 2 self.perimeter = 2 * 3.14 * self.r c = Circle(6) print(c.area) # 113.04 print(c.perimeter) # 37.68 c.r = 3 # 改變了半徑 print(c.area) # 113.04 print(c.perimeter) # 37.68
2-3所以上面的圓能夠寫成這樣:
class Circle: def __init__(self,r): self.r = r @property def area(self): return 3.14 * self.r ** 2 @property def perimeter(self): return 2 * 3.14 * self.r c = Circle(6) print(c.area) # 113.04 print(c.perimeter) # 37.68 c.r = 3 # 改變了半徑 print(c.area) # 28.26 print(c.perimeter) # 18.84