類:就是把相同的事物的動做和特徵整合到一塊兒java
類是一個抽象的。python
對象:基於類建立的一個具體的事物,程序員
也是特徵和動做整合一塊兒的編程
#最初始的寫法 people = { 'name': '奧巴馬', 'gender': '男', 'age': '50', } def ment1(people): print('這個叫【%s】的人,正在跑步'%people['name']) def ment2(people): print('這個叫【%s】,今年【%s】歲的【%s】人,吃飯'%(people['name'], people['age'],people['gender'])) ment1(people) ment2(people)
#使用函數去編寫面向對象。 def peo(): def ment1(people): print('這個叫【%s】的人,正在跑步'%people['name']) def ment2(people): print('這個叫【%s】,今年【%s】歲的【%s】人,吃飯'%(people['name'], people['age'],people['gender'])) people = { 'name': '奧巴馬', 'gender': '男', 'age': '50', 'run':ment1, 'ect':ment2, } return people f = peo() f['run'](peo())
#使用函數去編寫面向對象。優化版 # def peo(name,gender,age): # #要作的事: # def ment1(people): # print('這個叫【%s】的人,正在跑步'%people['name']) # def ment2(people): # print('這個叫【%s】,今年【%s】歲的【%s】人,吃飯'%(people['name'], # people['age'],people['gender'])) # #將定義的字典,寫入函數中 # def init(name,gender,age): # people = { # 'name': name, # 'gender': gender, # 'age': age, # 'run': ment1, # 'ect': ment2, # } # return people #返回字典 # res = init(name,gender,age) #返回字典的這個函數 # return res # f = peo('奧巴馬','男','20') #這個返回值是init(name,gender,age) # f1 = peo('奧巴馬','男','20') # #f['run'](f):調用的是ment1這個函數,由於他有一個參數, # # 就是people,,這個字典,因此()中要跟一個參數就是peo('奧巴馬','男','20')的返回值 # f['run'](f) # f['ect'](f1)
PS. 編程語言
面向對象編程,和程序設計面向對象 ,是沒有關係的ide
面向對象編程,是使用class類來進行編程的函數
程序設計面向對象:是使用def()來編程的工具
class類的建立方法
聲明類的方式,類名的開頭最好是大寫字母
大前提: 1.只有在python2中才分新式類和經典類,python3中統一都是新式類 2.新式類和經典類聲明的最大不一樣在於,全部新式類必須繼承至少一個父類 3.全部類甭管是否顯式聲明父類,都有一個默認繼承object父類(講繼承時會講,先記住) 在python2中的區分 經典類: class 類名: pass 經典類: class 類名(父類): pass 在python3中,上述兩種定義方式全都是新式類
dir(類名):查出的是類的一個名字列表學習
類名.__dict__:查看的是一個類的屬性字典,優化
class student: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def jineng(self): print('%s 走路'%self.name) s1 = student('liu',23,'nan') s1.jineng()
重點:輸入輸出不要寫到特定的函數當中,若是寫到特定的函數中,那麼這樣的可讀性就低了,
二、類的使用:
引用類的特徵(類的變量)和技能(類的函數)
類:一:實例化,
二:引用名字(類名.變量名,類名.函數名)實例:引用名字(實例名.類的變量,實例名.綁定方法,實例名.實例本身的變量名)類.數據屬性,調用的都是在本身的屬性字典去調用的
class Student: def __init__(self,name,age): self.name = name self.age = age bart = Student('yang',20) print(bart.age)
實例屬性只有數據屬性,沒有函數屬性
類名加括號就是實例化,會自動觸發__init__函數的運行,能夠用它來爲每一個實例定製本身的特徵
class Student: def __init__(self,id): self.id = id s1 = Student('12345678890') print(s1.id)
實例化:類名+括號
class Student: def __init__(self,id): self.id = id s1 = Student('12345678890')
self的做用是在實例化時自動將對象/實例自己傳給__init__的第一個參數,self能夠是任意名字,可是瞎幾把寫別人就看不懂了。
一:咱們定義的類的屬性到底存到哪裏了?有兩種方式查看 dir(類名):查出的是一個名字列表 類名.__dict__:查出的是一個字典,key爲屬性名,value爲屬性值 二:特殊的類屬性 類名.__name__# 類的名字(字符串) 類名.__doc__# 類的文檔字符串 類名.__base__# 類的第一個父類(在講繼承時會講) 類名.__bases__# 類全部父類構成的元組(在講繼承時會講) 類名.__dict__# 類的字典屬性 類名.__module__# 類定義所在的模塊 類名.__class__# 實例對應的類(僅新式類中)
類名.__mro__#類的父級以及自己
數據封裝
##將上面的代碼,進行數據封裝使用 def foo(std): print('%s : %s' %(std.name,std.age)) foo(bart)
#對象/實例自己其實只有數據屬性 >>> g1.nickname '草叢倫' >>> g1.aggressivity >>> g1.life_value ''' 查看實例屬性 一樣是dir和內置__dict__兩種方式 特殊實例屬性 __class__ __dict__ .... '''
對象/實例自己只有數據屬性,可是python的class機制會將類的函數綁定到對象上,稱爲對象的方法,或者叫綁定方法,綁定方法惟一綁定一個對象,同一個類的方法綁定到不一樣的對象上,屬於不一樣的方法,內存地址都不會同樣
class Riven: camp='Noxus' #全部玩家的英雄(銳雯)的陣營都是Noxus; def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻擊力54; self.nickname=nickname #爲本身的銳雯起個別名; self.aggressivity=aggressivity #英雄都有本身的攻擊力; self.life_value=life_value #英雄都有本身的生命值; def attack(self,enemy): #普通攻擊技能,enemy是敵人; enemy.life_value-=self.aggressivity #根據本身的攻擊力,攻擊敵人就減掉敵人的生命值。
實例出一個Riven來
>>> r1=Riven('銳雯雯')
交互:銳雯雯攻擊草叢倫,反之同樣
>>> g1.life_value 455 >>> r1.attack(g1) >>> g1.life_value 401
建立一個類就會建立一個類的名稱空間,用來存儲類中定義的全部名字,這些名字稱爲類的屬性
而類有兩種屬性:數據屬性和函數屬性
其中類的數據屬性是共享給全部對象的
>>> id(r1.camp) #本質就是在引用類的camp屬性,兩者id同樣 4315241024 >>> id(Riven.camp) 4315241024
而類的函數屬性是綁定到全部對象的:
>>> id(r1.attack) 4302501512 >>> id(Riven.attack) 4315244200 ''' r1.attack就是在執行Riven.attack的功能,python的class機制會將Riven的函數屬性attack綁定給r1,r1至關於拿到了一個指針,指向Riven類的attack功能 除此以外r1.attack()會將r1傳給attack的第一個參數 '''
建立一個對象/實例就會建立一個對象/實例的名稱空間,存放對象/實例的名字,稱爲對象/實例的屬性
在obj.name會先從obj本身的名稱空間裏找name,找不到則去類中找,類也找不到就找父類...最後都找不到就拋出異常
使用多個類作的交互:
class Riven: camp='Noxus' def __init__(self,nickname, aggressivity=54, life_value=414, money=1001, armor=3): self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value self.money=money self.armor=armor def attack(self,enemy): damage_value=self.aggressivity-enemy.armor enemy.life_value-=damage_value class Garen: camp='Demacia' def __init__(self,nickname, #名稱 aggressivity=58, #初始攻擊力 life_value=455, #生命值 money=100, #價格 armor=10): #護甲 self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value self.money=money self.armor=armor def attack(self,enemy): #enemy:敵人 '攻擊力-護甲,生命值減攻擊,獲得新的生命值' damage_value=self.aggressivity-enemy.armor enemy.life_value-=damage_value class BlackCleaver: def __init__(self,price=475,aggrev=9,life_value=100): self.price=price #裝備的金額 self.aggrev=aggrev #加9點攻擊 self.life_value=life_value #生命 def update(self,obj): obj.money-=self.price #減錢 obj.aggressivity+=self.aggrev #加攻擊 obj.life_value+=self.life_value #加生命值 def fire(self,obj): #這是該裝備的主動技能,噴火,燒死對方 obj.life_value-=1000 #假設火燒的攻擊力是1000 r1=Riven('草叢倫') g1=Garen('蓋文') b1=BlackCleaver() print(r1.aggressivity,r1.life_value,r1.money) #r1的攻擊力,生命值,護甲 if r1.money > b1.price: r1.b1=b1 b1.update(r1) print(r1.aggressivity,r1.life_value,r1.money) #r1的攻擊力,生命值,護甲 print(g1.life_value) r1.attack(g1) #普通攻擊 print(g1.life_value) r1.b1.fire(g1) #用裝備攻擊 print(g1.life_value) #g1的生命值小於0就死了
class pro: sex = 'nan' sex1 = '男' def __init__(self,name,age): self.name = name self.age = age def tu_tan(self): print('隨地吐痰') s = pro('yang',23) #增 s.qian = '20000' print(s.qian) #刪 del s.qian print(s.__dict__) #改 s.sex = '11' print(s.sex) #查 print(s) #############從類外部的增刪改查 #改,經過外部的函數,去更改類下面的函數 def food(self): print('正在吃東西') pro.tu_tan = food pro.tu_tan(s) #增 ,將類外部定義的函數,增長到類當中 def eat_food(self,food): print('正在吃%s'%food) pro.eat = eat_food pro.eat(s,'香蕉')
根據實例去改變的話,我在去用類.變量,不會改變個人元素
class pro: sex = 'nan' sex1 = '男' def __init__(self,name,age): self.name = name self.age = age def tu_tan(self): print('隨地吐痰') s = pro('yang',23) #增 s.qian = '20000' print(s.qian) #刪 del s.qian print(s.__dict__) #改 s.sex = '11' print(s.sex) #查 print(s)
類和實例化裏面的屬性,都是經過.的方式去查看__dict__裏面的屬性,沒用經過.的方式去查看的那都不會調用類裏面的屬性,而是從全局做用域裏面去找的
class Chinese:
country='China'
def __init__(self,name):
self.name=name
def play_ball(self,ball):
print('%s 正在打 %s' %(self.name,ball))
p1=Chinese('alex')
print(p1.country)
p1.country='日本' #經過實例去更改屬性,用實例去打印的更改了,類.的打印沒有更改
print('類的--->',Chinese.country)
print('實例的',p1.country)
country='中國'
class Chinese:
def __init__(self,name):
self.name=name
def play_ball(self,ball):
print('%s 正在打 %s' %(self.name,ball))
p1=Chinese('alex')
print(p1.country) #在類的外部定義一個變量,再經過類.變量打印,是報錯的,提示在當前這個類裏面沒有country這個屬性
動態讀取:
country='中國'
class Chinese:
def __init__(self,name):
self.name=name
def play_ball(self,ball):
print('%s 正在打 %s' %(self.name,ball))
def shi_li_hua():
name=input('>>: ') #想動態添加,最好是在類的外部從新定義一個函數,去進行添加。直接在類的內部添加,可讀性不高,別人會把你當傻逼
p1=Chinese(name)
# print(p1.country)
print(p1.name)
shi_li_hua()
只要是.的方式去調用的都是類或實例的引用,其餘的都不是調用類裏面的內容
country='中國-------------------' class Chinese: country='中國' def __init__(self,name): self.name=name print('--->',country) #沒用調用類裏面的內容 def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball)) print(Chinese.__dict__) #只要是.的方式不是類的調用,就是實例的調用, print(Chinese.country) p1=Chinese('alex') print('實例--------》',p1.country)
繼承:就是將不一樣的類的相同性,放到父級類裏面,在從父級類調用到子類裏面,最大化的減小相同的代碼。
例如:
class Hero: #父級類 def __init__(self, nickname,aggressivity,life_value): self.nickname = nickname #名字 self.aggressivity = aggressivity #攻擊力 self.life_value = life_value #生命 def attack(self,enemy): #攻擊 print('from Hero attack') enemy.life_value-=self.aggressivity class Garen(Hero): #子類調用分類 camp='Demacia' class Riven(Hero): #子類調用分類 camp='Noxus' g1 = Garen('yang',200,454) #子類寫入本身的屬性 r1 = Riven('r1',250,500) #子類寫入本身的屬性 print(g1.nickname) print(g1.aggressivity) print(g1.life_value) print(r1.nickname) print(r1.aggressivity) print(r1.life_value)
==========================第一部分 例如 貓能夠:喵喵叫、吃、喝、拉、撒 狗能夠:汪汪叫、吃、喝、拉、撒 若是咱們要分別爲貓和狗建立一個類,那麼就須要爲 貓 和 狗 實現他們全部的功能,僞代碼以下: #貓和狗有大量相同的內容 class 貓: def 喵喵叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something class 狗: def 汪汪叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something ==========================第二部分 上述代碼不難看出,吃、喝、拉、撒是貓和狗都具備的功能,而咱們卻分別的貓和狗的類中編寫了兩次。若是使用 繼承 的思想,以下實現: 動物:吃、喝、拉、撒 貓:喵喵叫(貓繼承動物的功能) 狗:汪汪叫(狗繼承動物的功能) 僞代碼以下: class 動物: def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something # 在類後面括號中寫入另一個類名,表示當前類繼承另一個類 class 貓(動物): def 喵喵叫(self): print '喵喵叫' # 在類後面括號中寫入另一個類名,表示當前類繼承另一個類 class 狗(動物): def 汪汪叫(self): print '喵喵叫' ==========================第三部分 #繼承的代碼實現 class Animal: def eat(self): print("%s 吃 " %self.name) def drink(self): print ("%s 喝 " %self.name) def shit(self): print ("%s 拉 " %self.name) def pee(self): print ("%s 撒 " %self.name) class Cat(Animal): def __init__(self, name): self.name = name self.breed = '貓' def cry(self): print('喵喵叫') class Dog(Animal): def __init__(self, name): self.name = name self.breed='狗' def cry(self): print('汪汪叫') # ######### 執行 ######### c1 = Cat('小白家的小黑貓') c1.eat() c2 = Cat('小黑的小白貓') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat()
在開發程序的過程當中,若是咱們定義了一個類A,而後又想新創建另一個類B,可是類B的大部份內容與類A的相同時
咱們不可能從頭開始寫一個類B,這就用到了類的繼承的概念。
經過繼承的方式新建類B,讓B繼承A,B會‘遺傳’A的全部屬性(數據屬性和函數屬性),實現代碼重用
派生:就是在調用父級的內容以後,在繼續使用父級的內容,還能夠在子類裏面添加本身的新功能,且還能保留父類的功能
固然子類也能夠添加本身新的屬性或者在本身這裏從新定義這些屬性(不會影響到父類),須要注意的是,一旦從新定義了本身的屬性且與父類重名,那麼調用新增的屬性時,就以本身爲準了。
class Hero: #父級類 def __init__(self, nickname,aggressivity,life_value): self.nickname = nickname #名字 self.aggressivity = aggressivity #攻擊力 self.life_value = life_value #生命 def attack(self,enemy): #攻擊 print('from Hero attack') enemy.life_value-=self.aggressivity class Garen(Hero): #子類調用分類 def __init__(self, nickname, aggressivity, life_value, script): Hero.__init__(self, nickname, aggressivity, life_value) self.script = script def attack(self, enemy): #在本身的子類中寫入父類的這個方法,在添加新的功能 Hero.attack(self, enemy) #調用父類的功能 print('from Garen attack') class Riven(Hero): #子類調用分類 pass g1 = Garen('yang',200,454,12012) #子類寫入本身的屬性 r1 = Riven('r1',250,500) #子類寫入本身的屬性 print(g1.script) g1.attack(g1) ''' 在保留父級的功能,本身寫的新功能也有 結果: from Hero attack from Garen attack '''
組合指的是,在一個類中以另一個類的對象做爲數據屬性,稱爲類的組合
''' 組合、繼承 組合是:有效的;利用相同的資源, 例如:老師是教授「python"課程的,學生是學習」python「課程的 那麼就能夠寫一個課程的類,來讓雙方調用。 ''' class Course: #課程的類 def __init__(self,course_name,course_price,course_time): self.course_name = course_name self.course_price = course_price self.course_time = course_time class Hero: #父類 def __init__(self,name,age,course): self.name = name self.age = age self.course = course #course = course_obj = Course('python',15800,'7m') ''' def kecheng(self): '這裏就至關於被調用的子類均可以使用,可是是被寫死的,學生就不能使用了,使用課程下的類的實例化屬性,' print('%s正在教授【%s】課程....'%(self.name,self.course.course_name)) ''' class Teacher(Hero): teacher = '老師' def kecheng(self): '寫在本身的類當中,寫死了也沒毛病,學生要用在本身去寫去,使用課程下的類的實例化屬性,' print('%s正在教授【%s】課程....' % (self.name, self.course.course_name)) class Student(Hero): student = '學生' def kecheng(self): print('%s正在學習【%s】課程....' % (self.name,self.course.course_name)) course_obj = Course('python',15800,'7m') #將課程的類實例化,在引入你須要的類當中 t1 = Teacher('engo',20,course_obj) # s1 = Student('engo',20,course_obj) t1.kecheng() #調用老師裏面的課程的函數屬性。 ''' 其實還能夠在將課程的這個函數屬性寫入父類裏面,在進行調用,若是在須要單獨添加功能,再去本身類的函數中添加 以下: #父類的 def kecheng(self): print('這是父類寫的功能) #子類的 def kecheng(self): print('%s正在學習【%s】課程....' % (self.name,self.course.course_name)) '''
使用python模擬一個藉口的效果
class Hero: def run(self): #模擬跑的接口 pass class People(Hero): def run(self): #人在跑 print('people running') class Dog(Hero): def run(self): #狗在跑 print('dog running') p1 = People() p1.run() d1 = Dog() d1.run() ''' 結果: people running dog running '''
強行讓子類使用某個功能,添加拋出異常
class Animal: def run(self): raise AttributeError('子類必須實現這個方法') #拋出異常 def speak(self): raise AttributeError('子類必須實現這個方法')、
class People(Animal): def run(self): print('人正在走') def speak(self): print('說話') class Pig(Animal): def run(self): print('pig is walking') def speak(self): print('哼哼哼') peo1=People() # peo1.run() peo1.speak() #如今people這個類的speak函數是沒有的,就在父類裏面找,父類裏面的speak函數是一個報錯異常,就是讓子類必須執行speak這個函數
1 什麼是抽象類
與java同樣,python也有抽象類的概念可是一樣須要藉助模塊實現,抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化
2 爲何要有抽象類
若是說類是從一堆對象中抽取相同的內容而來的,那麼抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。
好比咱們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類,你吃水果時,要麼是吃一個具體的香蕉,要麼是吃一個具體的桃子。。。。。。你永遠沒法吃到一個叫作水果的東西。
從設計角度去看,若是類是從現實對象抽象而來的,那麼抽象類就是基於類抽象而來的。
從實現角度來看,抽象類與普通類的不一樣之處在於:抽象類中只能有抽象方法(沒有實現功能),該類不能被實例化,只能被繼承,且子類必須實現抽象方法。這一點與接口有點相似,但實際上是不一樣的,即將揭曉答案
3. 在python中實現抽象類
import abc #使用abc模塊進行強制拋出異常, #就是在父類的函數上加一個@abc.abstractmethod裝飾器的功能,那麼子類就必需要使用這個函數功能,不然就報錯。 class Hero: @abc.abstractmethod def run(self): pass @abc.abstractmethod def speak(self): pass class Teacher(Hero): def run(self): print('run') def speak(self): print('speak') class Student(Hero): def run(self): print('run') def speak(self): print('speak') t1 = Teacher() s1 = Student() t1.run()
在python3中是廣度優先,看下面的實例
能夠經過.__mro__的方式去查看綁定的父類
#python3是以廣度優先的原理來進行繼承的關係,廣度優先是的分支不會一次性找到最後一個 # class A(object): # def test(self): # print('from A') # pass # class B(A): # # def test(self): # # print('from B') # pass # class C(A): # # def test(self): # # print('from C') # pass # class D(B): # # def test(self): # # print('from D') # pass # class E(C): # # def test(self): # # print('from E') # pass # # class F(D,E): # # def test(self): # # print('from F') # pass # f1=F() # f1.test() #f->d->b->e->c->a->a(object) ''' python3中是廣度優先的分支不會一次性找到最後一個, 這是隻有一個父類的時候。 到了最後一個就會找第二個分支的父類,一直找到最後一個也沒找到的話, 就繼續往下一個分支去找,一直找到最後一個分支都沒有找到就找最後的那個頭 '''
class x(object): def test(self): print('from X') class y(object): def test(self): print('from Y') class b(x): def test(self): print('from B') class c(y): def test(self): print('from C') class d(b): def test(self): print('from D') class e(c): def test(self): print('from E') class f(d,e): def test(self): print('from F') print(f.mro()) #f>>>d>>>b>>>x>>>e>>>c>>>y>>>(object) ''' python3中,當有兩個父類的時候, 就不太同樣了,先找第一條的路線,一直找到最後一個, 當最後一個也沒有的話就找第二條的父類,直到找到爲止。
圖詳解:
在python2中(深度優先)
在python2不能使用.__mro__的方式去查看
#python2中經典類的繼承,在查找屬性時遵循:深度優先 # class A: # # def test(self): # # print('from A') # pass # class B(A): # # def test(self): # # print('from B') # pass # class C(A): # # def test(self): # # print('from C') # pass # class D(B): # # def test(self): # # print('from D') # pass # # class E(C): # # def test(self): # # print('from E') # pass # class F(D,E): # # def test(self): # # print('from F') # pass # f1=F() # f1.test() # F->D->B->A->E->C ''' python2中經典類的繼承,在查找屬性時遵循:深度優先 在python2中是從一條道走到最後的父類,若是沒有找到的話,在走第二條路線。這樣找的。 '''
子類繼承了父類的方法,而後想進行修改,注意了是基於原有的基礎上修改,那麼就須要在子類中調用父類的方法
# #super在python2中的用法: # # 1:super(本身的類,self).父類的函數名字 # # 2:super只能用於新式類 # class People(object): # def __init__(self,name,sex,age): # self.name=name # self.age=age # self.sex=sex # def walk(self): # print('%s is walking' %self.name) # class Chinese(People): # country='China' # def __init__(self,name,sex,age,language='Chinese'): # # self.name=name # # self.sex=sex # # self.age=age # # People.__init__(self,name,sex,age) # super(Chinese,self).__init__(name,sex,age) # self.language=language # c=Chinese('egon','male',18) # print c.name,c.age,c.sex,c.language
#python3的super()用法 class people: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex class chinese(people): def __init__(self,name,age,sex,language): # super(people,self) 就至關於實例自己 在python3中super()等同於super(Subway,self) super().__init__(name,age,sex) self.language = language print(chinese.mro()) #一個新的方法,查看父類。 c = chinese('yang','male',20,'chinese') print(c.name,c.age,c.sex,c.language) ''' 使用super調用的全部屬性,都是從mro列表當前的位置日後找,千萬不要經過看代碼去找繼承關係,必定要看mro列表 '''
不少人喜歡將兩者混爲一談,而後百思不得其解,其實只要分開看,就會很明朗
多態指的就是常使用的多態性,方法相同結果不一樣。
多態指的是一類事物有多種形態,(一個抽象類有多個子類,於是多態的概念依賴於繼承)
#多態:同一種事物的多種形態動物分爲人類、狗類等等.... class Hero: def run(self): print('這是父類定義的這個函數') class People(Hero): def run(self): print('人在跑') class Dog(Hero): def run(self): print('人在跑') class Big(Hero): def run(self): print('人在跑') ''' 同一種類的不一樣對象,調用的屬性相同 '''
多態指的是一類事物有多種形態,(一個抽象類有多個子類,於是多態的概念依賴於繼承)
其實你們從上面多態性的例子能夠看出,咱們並無增長什麼新的知識,也就是說python自己就是支持多態性的,這麼作的好處是什麼呢?
1.增長了程序的靈活性
以不變應萬變,不論對象變幻無窮,使用者都是同一種形式去調用,如func(animal)
2.增長了程序額可擴展性
經過繼承animal類建立了一個新的類,使用者無需更改本身的代碼,仍是用func(animal)去調用
#定義類的階段,多態性 class Hero: def run(self): print('這是父類定義的這個函數') class People(Hero): def run(self): print('人在跑') class Dog(Hero): def run(self): print('人在跑') class Big(Hero): def run(self): print('人在跑') p1 = People() p2 = Big() d1 = Dog() # p1.run() # p2.run() # d1.run() #多態性,一種調用方式,不一樣的執行效果 #這是至關定義一個接口,那麼使用者就能夠直接傳參就可使用了。。 def func(obj):#obj這個參數沒有類型限制,能夠傳入不一樣類型的值 obj.run()#調用的邏輯都同樣,執行的結果卻不同 ''' 同一個接口,傳入的參數沒有類型限制,實現不一樣的功能。。 多態性: 1.繼承父類的共同特徵和技能 2. 多態性:定義贊成的接口,能夠傳入不一樣類型的值,可是調用的邏輯都同樣,執行結果卻不一樣, ''' func(p1) func(p2) func(d1)
封裝數據的主要緣由是:保護隱私(做爲男人的你,臉上就寫着:我喜歡男人,你懼怕麼?)
封裝方法的主要緣由是:隔離複雜度(快門就是傻瓜相機爲傻瓜們提供的方法,該方法將內部複雜的照相功能都隱藏起來了,好比你沒必要知道你本身的尿是怎麼流出來的,你直接掏出本身的接口就能用尿這個功能)
你的身體沒有一處不體現着封裝的概念:你的身體把膀胱尿道等等這些尿的功能隱藏了起來,而後爲你提供一個尿的接口就能夠了(接口就是你的。。。,),你總不能把膀胱掛在身體外面,上廁所的時候就跟別人炫耀:hi,man,你瞅個人膀胱,看看我是怎麼尿的。還有你的頭把你的腦子封裝到了腦袋裏,而後提供了眼睛這個接口....
提示:在編程語言裏,對外提供的接口(接口可理解爲了一個入口),就是函數,稱爲接口函數,這與接口的概念還不同,接口表明一組接口函數的集合體。
class A: __x = 1 #_A__x = 1 變形 def __init__(self,name): self.name = name def __test(self): print('from a') a = A('yang') A.__x = 2 a.__x = 2 print(A.__dict__) print(a.__dict__) # print(A.__x) # print(a._A__x) print(A._A__x) print(a.__x) a._A__test() # A._A__test(1111) ''' 這是引用變量的方式 print(A._A__x):在類裏面已經變形了,因此在打印的時候是類名._A__x的方式去打印出來 print(a.__x) :在類裏面已經變形了,因此在打印的時候是對象._A__x的方式去打印出來 這是引用函數的方式 a._A__test() :在類裏面已經變形了,因此在打印的時候是對象._A__x的方式去打印出來 A._A__test(1111) :也是同樣的,在類裏面就已經變形了,因此引用的方式是類名._A__x的方式 在外部增長新的變量, A.__x = 2 :這是經過類名.__x的方式去增長的,因此你打印的話也是這樣去引用,並無在類裏面去定義, a.__x = 2 :這是經過對象.__x的方式去增長的,因此你打印的話也是這樣去引用,這也是定義在實例化裏面 ............................... 以上的方式均可以經過類.__dict__的方式去查看, 以上的方式均可以經過對象.__dict__的方式去查看, #__名字,這種語法,只在定義的時候纔會有變形的效果,若是類或者對象已經產生了,就不會有變形的效果了 '''
封裝的
class A: def fa(self): print('from a') def test(self): self.fa() class B(A): def fa(self): print('from b') pass b = B() # b.fa() b.test()#尋找的過程是b.test-->B-->A--->A.test()--->A. ''' b.test() 這個找的過程是。 b.test-->B-->A--->A.test-->b.fa() 若是B裏面沒有fa()這個函數,那麼就在A裏面去找了,A裏面也沒有那麼久報錯了 '''
在內部能夠直接用__名字來訪問到變形的屬性
class A: def __init__(self): self.__x = 1 def tell(self): print(self.__x) #在內部能夠直接用__名字來訪問到變形的屬性 a = A() # print(a.__dict__) A.tell(a)
class A: def __fa(self): print('from a') def test(self): self.__fa() class B(A): def __fa(self): print('from b') pass b = B() b.test() ''' #由於在定義的階段已經變形了因此子類在引用父類的時候, # 調用的test()裏面的__fa()就已是_A__fa() '''
1 #property優先級,是先找property下面的,在找類裏面的 2 #@property(age):在函數上面加一個相似裝飾器的函數,再次執行類裏面的函數就可使用對象.屬性的方式去執行,不須要加()執行了 3 #@age.setter:能夠進行動態修改已經被修裝的屬性對象, 4 # @age.deleter:綁定方法刪除功能 5 6 #檢測BMI指數 7 # class people: 8 # def __init__(self,name,width,height): 9 # self.name = name 10 # self.width = width 11 # self.height = height 12 # @property 13 # #在函數上面加一個相似裝飾器的函數,再次執行類裏面的函數就可使用對象.屬性的方式去執行,不須要加()執行了 14 # def bodyindex(self): 15 # return self.width/(self.height ** 2) 16 # 17 # p = people('yang',80,1.80) 18 # print(p.bodyindex) 19 20 21 22 class People: 23 def __init__(self,name,age): 24 self.name = name 25 # self.__age = age #這個就不會再初始化的時候執行函數屬性的額方法 26 self.age = age #若是是這樣的話,在初始化的時候,就會執行函數屬性的方法,p.age = 222 27 28 @property 29 #在函數上面加一個相似裝飾器的函數,再次執行類裏面的函數就可使用對象.屬性的方式去執行,不須要加()執行了 30 def age(self): 31 return self.__age #p.__age = 222 32 33 ''' 34 1.這個是能夠進行動態修改已經被修裝的屬性對象, 35 2.將已經有的函數進行一個裝飾器的方法,這是固定的,將已有的函數進行更改,該函數下面有一個setter固定的方法 36 3.須要更改內容,是要添加一個參數, 37 4.咱們要改什麼屬性,就將該屬性 = 你的參數 38 ''' 39 @age.setter 40 def age(self,value): 41 if not isinstance(value,int): #判斷若是不是你指定的類型, 42 raise TypeError('必須是整數型') #進行一個主動拋出異常的用法 43 self.__age = value #須要修改的類型,是這個已經變形的對象 p.__age = 222 44 45 @age.deleter 46 def age(self): 47 del self.__age #del p.__age 48 49 #在初始化的時候,__對象的方式是不會被修改的,可是.對象的方式就會修改,是將一個函數的以屬性的方式給的 50 p = People('yang',222) 51 # print(p.age) 52 # p.age = 222 53 print(p.age) 54 del p.age 55 print(People.__dict__)
有三種方法:
1.靜態方法
初識staticmethod
class Foo: def spam(x,y,z): #類裏面的一個函數,而且x,y,z都是參數和self都是同樣的 print(x,y,z) spam=staticmethod(spam)#把spam函數作成靜態方法 l = Foo() l.spam(1,2,3)
將staticmethod設置成裝飾器
1 class Foo: 2 @staticmethod #spam = staticmethod(spam) 使用這個裝飾器,那麼當前的這個函數就適用於這個類 3 def spam(x,y,z): 4 print(x,y,z) 5 print(type(Foo.spam)) #他的類型就是一個函數, 6 Foo.spam(1,2,3) #使用類.函數屬性(加上你的參數) 7 f = Foo() 8 f.spam(1,2,3) #使用實例對象.函數屬性(加上你的參數) 9 ''' 10 <class 'function'> 11 1 2 3 12 1 2 3 13 '''
應用場景:編寫類時須要採用不少不一樣的方式來建立實例,而咱們只有一個__init__函數,此時靜態方法就派上用場了
1 import time 2 class Date: 3 def __init__(self,year,month,day): 4 self.year = year 5 self.month = month 6 self.day = day 7 @staticmethod #若是不加staticmethod方法類能夠調用,實例就不能調用了 8 def today():#用Date.today()的方式去建立實例,該實例用的是當前的時間 9 #獲取結構化的時間{time.struct_time(tm_year=2017, tm_mon=4, tm_mday=22, tm_hour=16, tm_min=27, tm_sec=46, tm_wday=5, tm_yday=112, tm_isdst=0)} 10 t = time.localtime() 11 # print(t) 12 obj = Date(t.tm_year,t.tm_mon,t.tm_mday)#直接在內部進行了實例化,而且給一個返回值 13 return obj 14 @staticmethod 15 def tomorrow(): 16 t = time.localtime(time.time()+86400) 17 # print(t) 18 obj = Date(t.tm_year,t.tm_mon,t.tm_mday)#直接在內部進行了實例化,而且給一個返回值 19 return obj 20 21 #但凡是定義在類的內部,而且沒有被任何裝飾器修飾過的方法,都是綁定方法:有自動傳值功能 22 a = Date(2017,2,4)#定義本身的時間 23 a1 = a.today() #綁定的方法也是有返回值的。在進行調用 24 print(a1.year,a1.day) 25 print(Date.today()) 26 27 #但凡是定義在類的內部,而且被staticmethod裝飾器修飾過的方法,都是解除綁定的方法,實際上就函數:就沒有自動傳值功能了 28 b = Date.today()#採用當前的時間 29 c = Date.tomorrow() 30 print(b.day,b.month,b.year) 31 print(c.day,c.month,c.year)
2.類方法
@classmethod也不須要self參數,但第一個參數須要是表示自身類的cls參數。
class Foo: def bar(self):#這個屬於綁定方法,直接.的方式就能使用 pass # 把一個方法綁定給類,類.綁定到類的方法(), # 會把類自己當作第一個參數自動給綁定到類的方法 @classmethod def test(cls,x): print(cls,x)#拿掉一個類的內存地址後,就可實例化或者引用類的屬性了 f = Foo() Foo.test(1)# f.test(1)#也是直接將對象傳給第一個值,
classmethod的使用場景
import time class Date: def __init__(self,year,month,day): self.year = year self.month = month self.day = day @classmethod #產生新的實例去使用 def now(cls):#用Date.now()的形式去產生實例,該實例用的是當前時間 print(cls) t = time.localtime()#獲取結構化的時間格式 obj = cls(t.tm_year,t.tm_mon,t.tm_mday)#新建實力而且返回 return obj @classmethod def tomorrow(cls):#用Date.tomorrow()的形式去產生實例,該實例用的是當前時間 print(cls) t = time.localtime(time.time()+86400) obj = cls(t.tm_year, t.tm_mon, t.tm_mday) return obj class Eurodate(Date): def __str__(self): return '年:%s,月:%s,日:%s'%(self.year,self.month,self.day) # f = Eurodate(2017,1,1) f = Eurodate.now() print(f)
一:綁定到對象的方法 只要是在類內部定義的,而且沒有被任何裝飾器修飾過的方法,都是綁定到對象的 class Foo: def test(self): #綁定到對象的方法 pass def test1(): #也是綁定到對象的方法,只是對象.test1(),會把對象自己自動傳給test1,因test1沒有參數因此會拋出異常 pass 綁定到對象,指的是:就給對象去用, 使用方式:對象.對象的綁定方法(),不用爲self傳值 特性:調用時會把對象自己當作第一個參數傳給對象的綁定方法 二:綁定到類的方法:classmethod 在類內部定義的,而且被裝飾器@classmethod修飾過的方法,都是綁定到類的 class Foo: def test(self): #綁定到對象的方法 pass def test1(): #也是綁定到對象的方法,只是對象.test1(),會把對象自己自動傳給test1,因test1沒有參數因此會拋出異常 pass 綁定到對象,指的是:就給對象去用, 使用方式:對象.對象的綁定方法() 特性:調用時會把對象自己當作第一個參數傳給對象的綁定方法 三:解除綁定的方法:staticmethod 既不與類綁定,也不與對象綁定,不與任何事物綁定 綁定的特性:自動傳值(綁定到類的就是自動傳類,綁定到對象的就自動傳對象) 解除綁定的特性:無論是類仍是對象來調用,都沒有自動傳值這麼一說了 因此說staticmethod就是至關於一個普通的工具包 class Foo: def test1(self): pass def test2(): pass @classmethod def test3(cls): pass @classmethod def test4(): pass @staticmethod def test5(): pass test1與test2都是綁定到對象方法:調用時就是操做對象自己 <function Foo.test1 at 0x0000000000D8E488> <function Foo.test2 at 0x0000000000D8E510> test3與test4都是綁定到類的方法:調用時就是操做類自己 <bound method Foo.test3 of <class '__main__.Foo'>> <bound method Foo.test4 of <class '__main__.Foo'>> test5是不與任何事物綁定的:就是一個工具包,誰來均可以用,沒說專門操做誰這麼一說 <function Foo.test5 at 0x0000000000D8E6A8>
1.面向對象的程序設計看起來高大上,因此我在編程時就應該保證通篇class,這樣寫出的程序必定是好的程序(面向對象只適合那些可擴展性要求比較高的場景)
2.不少人喜歡說面向對象三大特性(這是從哪傳出來的,封裝,多態,繼承?漏洞太多太多,好吧暫且稱爲三大特性),那麼我在基於面向對象編程時,我必定要讓我定義的類中完整的包含這三種特性,這樣寫確定是好的程序
好傢伙,我說降龍十八掌有十八掌,那麼你每次跟人幹仗都要從第一掌打到第18掌這才顯得你會了是麼,我來一萬我的你須要打10000*18掌對麼,傻叉
3.類有類屬性,實例有實例屬性,因此咱們在定義class時必定要定義出那麼幾個類屬性,想不到怎麼辦,那就使勁的想,定義的越多越牛逼
這就犯了一個嚴重的錯誤,程序越早面向對象,死的越早,爲啥面向對象,由於咱們要將數據與功能結合到一塊兒,程序總體的結構都沒有出來,或者說須要考慮的問題你都沒有搞清楚個八九不離十,你就開始面向對象了,這就致使了,你在那裏幹想,自覺得想通了,定義了一堆屬性,結果後來又都用不到,或者想不通到底應該定義啥,那就一直想吧,想着想着就瘋了。
你見過哪家公司要開發一個軟件,上來就開始寫,確定是頻繁的開會討論計劃,請看第八節
4.既然這麼麻煩,那麼我完全解脫了,咱們不要用面向對象編程了,你啊,你有大才,你能成事啊,傻叉。
抽象/實現
抽象指對現實世界問題和實體的本質表現,行爲和特徵建模,創建一個相關的子集,能夠用於 繪程序結構,從而實現這種模型。抽象不只包括這種模型的數據屬性,還定義了這些數據的接口。
對某種抽象的實現就是對此數據及與之相關接口的現實化(realization)。現實化這個過程對於客戶 程序應當是透明並且無關的。
封裝/接口
封裝描述了對數據/信息進行隱藏的觀念,它對數據屬性提供接口和訪問函數。經過任何客戶端直接對數據的訪問,無視接口,與封裝性都是背道而馳的,除非程序員容許這些操做。做爲實現的 一部分,客戶端根本就不須要知道在封裝以後,數據屬性是如何組織的。在Python中,全部的類屬性都是公開的,但名字可能被「混淆」了,以阻止未經受權的訪問,但僅此而已,再沒有其餘預防措施了。這就須要在設計時,對數據提供相應的接口,以避免客戶程序經過不規範的操做來存取封裝的數據屬性。
注意:封裝毫不是等於「把不想讓別人看到、之後可能修改的東西用private隱藏起來」
真正的封裝是,通過深刻的思考,作出良好的抽象,給出「完整且最小」的接口,並使得內部細節能夠對外透明
(注意:對外透明的意思是,外部調用者能夠順利的獲得本身想要的任何功能,徹底意識不到內部細節的存在)
合成
合成擴充了對類的 述,使得多個不一樣的類合成爲一個大的類,來解決現實問題。合成 述了 一個異常複雜的系統,好比一個類由其它類組成,更小的組件也多是其它的類,數據屬性及行爲, 全部這些合在一塊兒,彼此是「有一個」的關係。
派生/繼承/繼承結構
派生描述了子類衍生出新的特性,新類保留已存類類型中全部須要的數據和行爲,但容許修改或者其它的自定義操做,都不會修改原類的定義。
繼承描述了子類屬性從祖先類繼承這樣一種方式
繼承結構表示多「代」派生,能夠述成一個「族譜」,連續的子類,與祖先類都有關係。
泛化/特化
基於繼承
泛化表示全部子類與其父類及祖先類有同樣的特色。
特化描述全部子類的自定義,也就是,什麼屬性讓它與其祖先類不一樣。
多態與多態性
多態指的是同一種事物的多種狀態:水這種事物有多種不一樣的狀態:冰,水蒸氣
多態性的概念指出了對象如何經過他們共同的屬性和動做來操做及訪問,而不需考慮他們具體的類。
冰,水蒸氣,都繼承於水,它們都有一個同名的方法就是變成雲,可是冰.變雲(),與水蒸氣.變雲()是大相徑庭的過程,雖然調用的方法都同樣
自省/反射
自省也稱做反射,這個性質展現了某對象是如何在運行期取得自身信息的。若是傳一個對象給你,你能夠查出它有什麼能力,這是一項強大的特性。若是Python不支持某種形式的自省功能,dir和type內建函數,將很難正常工做。還有那些特殊屬性,像__dict__,__name__及__doc__