python基礎之 面向對象

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就是普通函數,也是靜態函數

classmethod文章1
classmethod文章2 3

 

 

 

 

 

 返回系列

相關文章
相關標籤/搜索