1.什麼是面向對象?html
在大學學習c#的時候接觸面向對象,知道好像有什麼方法,屬性,人狗大戰啥的。可是都忘記了,也不知道面向對象究竟是個啥! 在python中一切都是對象,linux中一切都是文件(忽然想起來了) 什麼是class?什麼是對象?什麼是屬性? 人狗大戰走起!!!
class People:
a =1000 #靜態變量,是全部對象共同使用的值,經過類名或方法名來調用,可是靜態變量通常使用類名來調用 def __init__(self, name, hp, ad, sex): #執行__ini__等價於People(),實例化這個類,主動調用。誰在實例化這個類,self這個參數就屬於誰
#self指向了一塊內存地址,裏面存的是字典,用於接收傳遞進來的參數,而後自動返回給調用者
#調用__init__,自動傳入了self函數,其餘參數都是按照順序傳遞進來的,而且添加到這個字典中
#執行__init__函數
#返給給調用者self self.name = name #實例變量 對象屬性 self.hp = hp self.ad = ad self.sex = sex def fight(self, dog): #動態變量 對象方法 dog.hp -= self.ad print("%s打了%s,%s掉了%s血" % (self.name, dog.name, dog.name, self.ad)) class Dog(): def __init__(self, name, hp, ad, sex): self.name = name self.hp = hp self.ad = ad self.sex = sex def bite(self, people): people.hp -= self.ad print("%s咬了%s,%s掉了%s血" % (self.name, people.name, people.name, self.ad)) jordan = People("alex", 300, 20, '男') #建立一個對象也是在建立一個實例 james = Dog('hei', 400, 10, '女') jordan.fight(james) #對象名調用類裏面的函數,並將調用者的名字和參數傳遞到方法裏面,jordan=self
jordan.hp = 20000 #經過對象名添加屬性,這個屬性只是這個對象擁有
del jordan.sex #經過對象名刪除屬性 print(james.__dict__) #經過dict查看對象中的變量
1.class:用於定義一個具備相同方法和屬性的一類事物
2.對象:根據一個模板(class)刻畫出來的一個實體,具備模板裏面的一切方法和屬性
3.不一樣的對象有不一樣的屬性,好比咱們都有手,可是個人手比你的好看,這就是屬性
查看類當中的全部變量:print(類名.__dict__)
查看對象當中的指定變量:
方法一:print(對象.__dict__['變量'])
方法二:print(對象.變量) ---經常使用
面向對象編程:把屬性和動做封裝到一個類型當中,就是面向對象編程(經過Dog類生成好多具備Dog類屬性和方法的狗)
2.類的加載順序java
class A: def __init__(self): #初始化函數是在對象實例化的時候執行 print('執行我了') def func(self): print('1111111') def func(self): print('2222222',A.country) country = 'China' country = 'English' a = A() #執行初始化函數 a.func() #按照類的加載順序來看,內存中先是開闢一個__init__的空間,而後開闢函數func的空間,看見第二個函數func的名字和第一個相同,就將第一個func的指向斷掉,將func指向第二個內存地址 #而後接受兩個country,因此打印出來的就是初始化函數的print(),第二個func函數,其中A.country的值是第二個country的值
注意類的加載順序以下:
1.類內部的一個縮進的全部代碼塊都是在py文件從上到下解釋的時候就已經被執行了
2.類中的代碼永遠是從上到下依次執行的
3.類的對象的命名空間python
class B: l = [0] #可變變量 def __init__(self,name): #類在實例化是被執行 self.name = name b1 = B('科比') b2 = B('jordan') print(B.l) print(b1.l) print(b2.l) b1.l[0] += 1 #至關於使用類B裏面的靜態變量可變的L,而且爲L的第0個元素賦值爲1,因此全部使用類B實例化的對象都會隨之改變 print(b2.l[0]) #是對象b2調用類裏面的可變的數據變量l,再上一步中b1已經修改過了此變量,因此結果爲1 b1.l = [123] #至關於在b1的空間裏面新定義了一個l,而且賦值爲123,和類B沒有關係,和使用類B進行實例化的對象也沒有關係 print(b2.l) #因此b2.l仍是爲1
1.類中的內容和對象中的內容是分別存儲的,是存儲在兩個不一樣的內存空間
2.類是所有加載完整個類以後,類名才指向給類開闢的內存地址
而函數不是,函數從Python解釋器解釋到定義函數以後,就將函數名和參數一塊兒分配一個內存地址了,可是函數內部代碼在函數被調用的時候纔會分配內存地址
3.在對象的內存空間的上方有一個指向類的內存地址的指針,叫類指針,是一個變量
4.對象去類空間中找名字:一般是在本身的空間中沒有這個名字的時候,經過類指針找到類的內存地址而後在類中去找變量
注意:在操做類裏面的靜態變量的時候,應該儘可能使用類名操做而不是對象名
對於可變的數據類型,最好使用對象名來進行調用
對於不可變的數據類型最好使用類名來進行調用
結論:只要是一個[對象.名字]直接賦值,那麼就是在這個對象的空間內建立了一個新的屬性
只要是對一個可變的數據類型內部進行改變,那麼會改變全部的經過該類進行實例化的屬性
other:(重要)
1.若是有同名的方法和屬性,老是後面的先執行
2.對象的命名空間在對象初始化的時候就已經開始建立
類指針:__init__初始化之間就存在了
對象的屬性:在__init__初始化以後執行的
4.組合:一個類的對象是另外一個類的屬性(每每表達的什麼裏面有什麼)linux
class Student: def __init__(self,name,num,course): self.name = name self.num = num self.course = course class Course: def __init__(self,name,price,period): self.name = name self.price = price self.period = period python = Course('python',20000,'6 months') s1 = Student('kobe',10086,python) #s1中的python是使用Course實例化出來的對象,s1.course.(name|price|period) 是s1這個學生的課程具備了python對象的name,price,period屬性 ju = Student('james',10010,python) s3 = Student('jordan',10011,python) # print(s1.name) # print(s1.num) # print(s1.course.period) print(s1.__dict__) #打印這個對象的變量,能夠看到course裏面存放的是python對象的內存地址(一個類的對象能夠傳給另外一個類的屬性)
類和類嵌套的組合
組合的好處:可以減小重複代碼,最重要的是可以"鬆耦合"
5.面向對象的三大特性c++
繼承(每每表達的什麼是什麼的關係)面試
繼承:
繼承實現了 IS-A 關係,例如 Cat 和 Animal 就是一種 IS-A 關係,所以 Cat 能夠繼承自 Animal,從而得到 Animal 非 private 的屬性和方法。
繼承應該遵循里氏替換原則,子類對象必須可以替換掉全部父類對象。
Cat 能夠當作 Animal 來使用,也就是說可使用 Animal 引用 Cat 對象。父類引用指向子類對象稱爲 向上轉型 。
繼承的好處是:
1.提升代碼的利用率,減小代碼的冗餘
2.繼承最大的好處是子類得到了父類的所有功能(屬性和方法)
3.繼承的另外一個好處是多態
當子類和父類同時都存在run()方法的時候,一般都是子類的方法覆蓋父類的方法,在代碼運行的時候,老是執行子類的方法,這樣就得到了另一個好處:多態
爲何要學習繼承?
單繼承:能夠有效的幫助咱們提升代碼的重用性
多繼承:規範複雜的功能與功能之間的關係,多繼承的工做原理可以幫助咱們看源碼
單繼承
class A(要繼承的類的名字B):------>括號必須寫要繼承的類的名字
#B:--->父類,基類,超類
#A:--->子類,派生類
class Animal(object): #建立一個類,python中全部的類都默認基於object,叫新式類。 def __init__(self,name,kind): #Animal類的初始化方法,會和全部縮進4個空格的內容一塊兒寫進內存 self.name = name #Animal的屬性 self.kind = kind def eat(self,food): #Animal類的方法 print('%s eat %s'%(self.name,food)) class Cat(Animal): #基於Animal類建立Cat類,Cat類繼承Animal def __init__(self,name,kind,eyes_color): #Cat類中的初始化方法 # super(Cat,self).__init__(name,kind) # super().__init__(name,kind) # 至關於執行父類的init方法,必需要將Cat類得到的值傳遞給Cat的父類,只要傳遞共同擁有的就行,剩下子類中有的屬性而父類中沒有的屬性是子類的私有屬性 # Animal.__init(self,name,kind) self.eyes_color = eyes_color #派生屬性,是Cat這個類的特有屬性 Animal.__init__(self,name,kind) # 在子類和父類有同名方法的時候,默認只執行子類的方法,若是想要執行父類的方法和得到父類的屬性,能夠在子類的方法中再 指名道姓 的調用父類的方法 def climb(self): # 派生方法,Cat這個類的特有方法 print('%s climb tree'%self.name) hua = Cat('小花','橘貓','藍色') print(hua.__dict__) #當Cat本身擁有__init__的時候,就再也不調用父類的了
單繼承類的繼承順序:
1.若是子類和父類都有相同的方法,默認子類的方法覆蓋父類的方法:子類的對象會優先使用子類的方法
2.若是即想使用子類的方法,又想使用父類的方法,就要在子類中指名道姓的調用父類的方法,可是子類的派生類和派生方法只能本身擁有
super().方法名(arg1,arg2)
父類名.方法名(self,agr1,agr2)
other:
1.python3中全部的類都是新式類,由於默認全部類都繼承object類
2.python2中的類若是【類名()】擴號裏面不寫object,就是經典類(只有py2.x中存在),加上object就是新生類
3,也能夠說父類是object的類都是新式類
多繼承
多繼承是python種特有的一種繼承方式,雖然java,c#等都有繼承,可是不支持多繼承,c++支持多繼承
多繼承中的繼承順序
1.若是對象使用名字(方法和屬性)是子類中存在的,就和單繼承同樣,使用子類中有的名字
若是子類中沒有,就去父類中尋找,若是父類中也沒有,就是報錯
2.若是多個父類中都有,那麼使用哪個父類的那?
a.磚石繼承問題

上面的1234就是多繼承類的繼承順序。
首先子類D先在本身的空間內尋找func()方法,若是有的話,就使用本身的方法,若是沒有的話尋找父類的func()方法
其次D類繼承了兩個父類B,C。在python3中多繼承的繼承順序默認是用廣度優先的排序算法,又由於在繼承的時候C在B的前面因此先繼承C
其次繼承B,最後在繼承A,A類中也沒有的話,就去A的父類中object尋找,object也沒有的話就報錯。
因此繼承順序爲D-->C-->B-->A--object(如沒有就報錯)
b.烏龜繼承問題
上面的123456就是繼承順序
首先F類先在本身的空間內尋找func()方法,若是有,就繼承本身空間內的方法,若是沒有就尋找父類的空間內的func()方法
其次F類繼承了D,E兩個父類,因爲在python3中多繼承的繼承順序默認是廣度優先,又由於在F繼承的時候D在E前面,因此先繼承D的分支
依次向下繼承順序爲F-->D-->B-->E-->C-->A-->object(若是沒有就報錯)
other:
print(B.mro()) #用來顯示類的繼承順序
print(B.__bases__) #用來顯示B繼承了多少個類
先抽象,在繼承。
應該是先從具體的東西往不具體的東西走,叫作抽象。
對象-->類-->父類
越往基類上走就越抽象
基類-->繼承-->子類-->實例化-->對象
Python2.x
默認不繼承object,是經典類,除非在擴號裏面申明,纔是新式類
多個類之間尋找繼承關係的時候,遵循深度優先的算法
經典類沒有super()方法和mro()方法
C3算法:
#提取第一個點
# 若是從左到右第一個類,
# 在後面的繼承順序中也是第一個,或者再也不出如今後面的繼承順序中
# 那麼就能夠把這個點提取出來,做爲繼承順序中的第一個類
https://blog.csdn.net/u011467553/article/details/81437780
注意:
1.單繼承中,super()方法找的是子類的父類
2.多繼承中,super()方法找的是mro的順序,和子類父類無關

經典的累的繼承順序問題

1.Son()是在實例化一個對象,可是沒有接受的變量
2.在執行Son()的時候,會先尋找__init__函數,沒有的話回去尋找父類中的__init__方法
3.在執行父類中的__init__的時候會調用self.func(),可是這個self指的是類實例化的對象的
4.因此會執行Son()函數的print()方法算法
6.經典類和新式類的區別?編程
經典類: 1. 是py2.x版本纔有的 2. py2.x裏面的類默認不繼承object,須要默認在類後面的括號裏面添加(object) 3. 經典類的多繼承的繼承順序使用的是深度優先的排序算法 c3算法
4.沒有super()方法和mro方法 新式類: 1. 是py3.x纔有的 2. py3.x默認都是繼承的object類 3. 新式類的多繼承的繼承順序使用的是廣度優先的排序算法 4. 新式類具備super()方法 在單繼承中super()尋找的時父類 在多繼承中super()是根據mro排序來尋找累的順序 5. 新式類具備mro()方法,用來查看多繼承中類的繼承順序
7.type和抽象類json
什麼是type類型?什麼是元類?c#
class Course: def __init__(self,name,price): self.name = name self.price = price python = Course('python',20000) print(type(python)) #type一個對象時,結果返回的老是這個對象實例化的類 print(type(123)) #返回一個整形 print(type(Course)) #返回一個type類型 print(type(int)) #返回一個type類型 print(type(str)) #返回一個type類型
注意:
1.type在type一個對象的時候,返回的是這個對象所屬的類
2.type一個類的時候,類也變成了對象,全部用class創造出來的類都是type類型
3.類也是一個'對象',類是一個'類(type)'類型的對象
4.全部類的鼻祖是type類型,object也的類型也是type
5.對象是被創造出來的:被類實例化出來的
類也是被創造出來的:特殊的方法創造類
常規創造的類的特性:
1.可以被實例化
2.能有屬性
3.能有方法
元類:創造出來的不一樣尋常的類
特殊需求一:不能實例化
特殊需求二:只能有一個實例
抽象類
from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): #抽象類,至關因而子類的模板,告訴子類必需要按照我這個類裏面的來寫 @abstractmethod #約束全部子類,加上這個而裝飾器之後,後面全部繼承個人子類必需要有該方法,不然不能實例化 def pay(self): pass # 建立的這個pay並無內容, # 之因此寫一個pay是爲了提醒全部子類,必定要實現一個pay方法 @abstractmethod def back(self): pass class Wechatpay(Payment): def __init__(self,name,money): self.name = name self.money = money def pay(self): print('%s經過微信支付了%s元'%(self.name,self.money)) class Alipay(Payment): def __init__(self,name,money): self.name = name self.money = money def pay(self): print('%s經過支付寶支付了%s元'%(self.name,self.money))
def back(self):
print("退款") #歸一化設計 def pay(persion): #--->實例化的一個類的對象 persion.pay() alp = Alipay('p0st',2000000) pay(alp)
抽象類的特色:
1.class Payment(metaclass=ABCMeta):有metclass=ABCmeta的都是抽象類
2.抽象類約束全部子類,必需要實現abstractmethod裝飾的方法,給咱們的代碼指定規範
3.抽象類最主要的特色:不能被實例化,只是做爲子類的規範
抽象類中不必定包含抽象方法,包含抽象方法的類必定是抽象類。
文檔
8.多態
python中到處是多態,一切皆對象 # 鴨子類型 python當中寫程序的一種特殊的狀況 其餘語言中 正常的咱們說一個數據類型具備某個特色,一般是經過繼承來實現 繼承迭代器類,來證實本身自己是個迭代器 繼承可哈希的類,來證實本身本事是可哈希的 可是全部的這些都不是經過繼承來完成的 咱們只是經過一種潛規則的約定,若是具備__iter__,__next__就是迭代器 若是具備__hash__方法就是可哈希 若是具備__len__就是能夠計算長度的 這樣數據類型之間的關係並不只僅是經過繼承來約束的 而是經過約定俗成的關係來確認的
多個類似的類具備相同的方法名,可是又不經過抽象類/或者繼承父類的約束,這幾個具備相同方法的類就是鴨子類型 # 多態 廣義的多態: 一個類能表現出的多種形式 狹義的多態 在傳遞參數的時候,若是要傳遞的對象有多是多個類的對象 咱們又必須在語言中清楚的描述出究竟是那一個類型的對象 咱們就可使用繼承的形式,有一個父類做爲這些全部可能被傳遞進來的對象的基類 基礎類型就能夠寫成這個父類 因而全部子類的對象都是屬於這個父類的 在python當中,由於要傳遞的對象的類型在定義階段不須要明確,因此咱們在python中到處都是多態 數據的類型不須要經過繼承來維護統一 注意: python中多態體現的並不明顯,可是Python中到處都是多態 可是python中鴨子類型體現的明顯
9.namedtuple
from collections import namedtuple Course = namedtuple('Course',['name','price','period']) python = Course('python',20000,'6 month') print(python.name) print(python.price) print(python.period) print(type(python)) 其中的Course至關於在建立一個類,python至關於實例化一個Course的對象 經過print(type(python))來看python的類型發現,python是Course的子類<class '__main__.Course'> namedtuple實際上在幫助咱們建立了一個類,這個類只有方法,沒有屬性.而且屬性只能在初始化的時候寫,不能修改,namedtuple是一個特別的元類 nametuple的使用場景:在咱們須要描述一個只有屬性而且不須要修改的時候,使用namedtuple是最好的:例如撲克牌。 站在面向對象的角度來看:namedtuple的本質就是:一個元類+元組的特性
10.pickle
在之前學習的洗後,json能處理的是較爲簡單的,固定的數據類型,而pickle能處理任意的python的數據類型和對象,包括類 import pickle class Course: def __init__(self,name,price,period): self.name = name self.price = price self.period = period # python = Course('python',20000,'6 months') # linux = Course('linux',15800,'5 months') # import pickle # with open('pickle_file','ab') as f: # pickle.dump(python,f) # pickle.dump(linux,f) import pickle with open('pickle_file','rb') as f: # obj1 = pickle.load(f) # obj2 = pickle.load(f) while True: try: obj = pickle.load(f) print(obj.__dict__) except EOFError: break
pickle能夠序列化python中出現的全部的數據類型,甚至能序列化類的對象,和一些複雜的數據類型,相比於json,pickle的應用場景會更多!
11.封裝
封裝:
私有化的封裝:私有化的方法和屬性,將屬性封裝起來
廣義上的封裝:把方法和屬性根據類別封裝到類中
私有化:
方法\靜態變量\實例變量(對象屬性)均可以私有化
什麼是私有化:就是隻能在類的內部可見,類的外部不可見,不能訪問
class Goods:
__country = 'china' #靜態變量的私有化
def __init__(self,name,price):
self.name =name
# self.price =price
self.__price =price #私有的屬性,將價格變成類的私有化,只能在類的內部調用
def get_price(self):
print(self.__price)
def __get_name(self): #私有方法在外部不能被調用,只能在內部被使用
print(self.name)
#print(Goods.country)
apple = Goods('蘋果',5)
apple._Goods__get_name() #經過對象名來調用類的變量(類名.__dict__)裏面存的變量名
print(apple.name)
print(apple.price)
apple.get_price() #調用類內部方法得到私有化屬性
1. python在私有化的時候,都是從python語法角度上作的了修改,在內部定義了一個私有屬性,使用print(對象__dict__)能看到對象的變量
2. 全部的私有的變化都是類的內部定義的時候完成的,都是經過self所在的類名和私有化的名字存起來---> _類名__屬性名,經過print(對象__dict__)
3.注意:私有的概念,但凡在類的外面,都不能用,是不能夠被繼承的
私有的全部內容:實例變量(對象屬性),靜態屬性(類變量),方法等都不能被子類繼承,只能在類的內部使用
4.私有化總體的邏輯:_類名__方法名()---》來調用私有方法,也是私有方法在內存中的變量名
5.凡是在類的內部定義的私有的就都是私有的(_類名__私有變量名),可是在類的外部定義的時候就不是私有的了
關於私有化的經典面試題

此題的結果打印的是is foo。在Son()實例化以前,會在內存中開闢兩個類的內存空間和一個對象的內存空間,而且內存空間的類指針如上圖所示。
在實例化Son()以後,因爲此Son()沒有__init__方法,因此會去父類中區尋找,找到父類中的__init__()函數以後,會執行self__fun(),可是此時
的__func是私有化函數,而且在Foo類裏面的名字爲_Foo_func(),可是其實例化的類中沒有_Foo_func()方法,因此只再起父類中尋找,因此打印is foo
13.@property
@property 把裝飾的一個方法假裝成一個屬性
class Goods: discount = 0.8 def __init__(self, name, price): self.name = name self.__price = price @property # 只支持obj.price的方式查看這個結果,不支持修改,也不支持刪除 def price(self): p = self.__price * self.discount return p @price.setter def price(self,value): if type(value) is int or type(value) is float: self.__price = value apple = Goods('蘋果', 5) banana = Goods('香蕉', 10) apple.price = 16 #對應的調用的是被setter裝飾的price方法 print(apple.price) #對應調用的是被property裝飾的price方法
# 私有的 :經過過給__名字這樣的屬性或者方法加上當前所在類的前綴,把屬性隱藏起來了
# 只能在本類的內部使用,不能在類的外部使用,不能被繼承
# property 把一個方法假裝成屬性
# property和私有的兩個概念一塊兒用
# 定義一個私有的
# 再定義一個同名共有的方法,被property裝飾
# @方法名.setter
# @方法名.deleter
12.classmethod和staticmethod
classmethod是用來指定一個類的方法爲類方法,沒有此參數指定的類的方法爲實例方法
@classmethod:修飾符對應的函數不須要實例化,不須要 self 參數,但第一個參數是須要表示類自身的cls參數,能夠來調用類的屬性,類的方法,實例化對象等。 class A(object): # 屬性默認爲類屬性(能夠直接被類自己調用) num = "類屬性" # 實例化方法(必須實例化類以後才能被調用,對象方法) def func1(self): # self : 表示實例化類後的對象地址id print("func1") print(self) # 類方法(不須要實例化類就能夠被類自己調用) @classmethod def func2(cls): # cls : 表示沒用被實例化的類自己 print("func2") print(cls) print(cls.num) cls().func1()
# 不傳遞默認self參數的方法(該方法也是能夠直接被類調用的,可是這樣作不標準)也叫普通方法,若是加上self的話,類在調用的時候必需要將類自己傳遞進去 A.func3(A),纔不會報錯, def func4(): print("func4") print(A.num) # 屬性是能夠直接用類自己調用的 # A.func1() 這樣調用是會報錯:由於func1()調用時須要默認傳遞實例化類後的地址id參數,若是不實例化類是沒法調用的 A.func2() A.func4()
類方法總結:
1.類方法是給類來用的,是類的方法,類在使用的時候會將類自己傳遞給類方法的第一個參數,python爲咱們提供了classmethod內置方法來將類中的函數定義成類方法(也能夠說成將對象方法(函數)變成類方法)
2.在類調用類方法的時候不須要實例化就能夠直接調用,也不須要self參數
3.雖說類的方法能夠經過對象名來調用,可是一般都是使用類名來調用,由於是類的方法
總結:
一、實例方法,該實例屬於對象,該方法的第一個參數是當前實例,擁有當前類以及實例的全部特性。
二、類方法,該實例屬於類,該方法的第一個參數是當前類,能夠對類作一些處理,若是一個靜態方法和類有關可是和實例無關,那麼使用該方法。
類方法的使用場景:在使用類以前,須要使用類中的方法來進行一些計算
三、靜態方法,該實例屬於類,但該方法沒有參數,也就是說該方法不能對類作處理,至關於全局方法
靜態方法使用場景:編寫類時須要採用不少不一樣的方式來建立實例,而咱們只有一個__init__函數,此時靜態方法就派上用場了
@staticmethod就是普通函數,也是靜態函數