面向對象

今天要學習的是面向對象,Python從設計之初就已是一門面向對象的語言,正由於如此,在Python中建立一個類和對象是很容易的,咱們先一塊兒來看一下類和對象的定義~html

1.類和對象的定義

class 類的名稱:
    語句塊

# 舉例說明

    class Student:
        num = 100
        def showNum(self):
            return 200
    
    print(Student.num)       # 輸出:100
    print(Student.showNum)
    # 輸出:<function Student.showNum at 0x009A9468>

# Student就是類對象,num是類變量,showNum是方法,self爲類對象的實例, 類名稱通常須要大寫
  • :用來描述具備相同的屬性和方法的對象的集合;
  • 對象:經過類定義的數據結構實例;
  • 簡單來講就是在python中,用屬性表示特徵,用方法表示技能,於是具備相同特徵和技能的一類事物就是‘類’,對象是則是這一類事物中具體的一個;

2.實例的定義

講完類和對象,咱們來看一下實例,類是抽象的模板,而實例是根據類建立出來的一個個具體的「對象」,每一個對象都擁有相同的方法,但各自的屬性可能不一樣;python

class Student:
        def prt(self):
            print(self)
            print(self.__class__)
    
    s = Student()
    s.prt()
    
    # 執行結果:
    # <__main__.Student object at 0x0000000001D7CDA0>
    # <class '__main__.Student'>

上述例子中self表明的是類的實例,而self.__class__指向類,且self 不是 python 關鍵字,咱們把self換成其它的單詞也能夠正常運行;設計模式

3.什麼是實例化

咱們知道了什麼是實例,那實例化又是什麼呢,接着看下面這個例子,在Student類中有兩個方法,一個是初始化方法__init__,另外一個是我本身定義的方法showClass()方法數據結構

class Student:
    num = 100
    def __init__(self):
        self.name = '張三'
    def show(self):
        return '李四'

print(Student.num)         # 輸出:100
print(Student.show)
輸出:<function Student.show at 0x0000000002765BF8>

stu = Student()
# 這就是實例化,且實例化會自定調用__init__方法,self會自動傳遞,不能有return返回值

print(stu.name)            # 張三
  • 類名加括號就是實例化,實例化會自動調用__init__()方法,能夠用它來爲每一個實例定製本身的特徵(屬性);
  • init()方法被稱爲類的構造函數或初始化方法,須要注意的是__init__()方法不能有return返回值;

4.類變量和實例變量

而後咱們來說一下什麼是類變量和實例變量,看看這二者之間有什麼不一樣;函數

# 實例變量是實例特有的,類變量是類和實例共有的
class Student:
    num = 100
    def __init__(self, name):
        self.name = name

    def showClass(self):
        return 200

print(Student.num)
print(Student.showClass)

stu = Student('張三') # 實例化,會調用__init__方法,self會自動傳遞,不能有return返回值

print(stu.name) # 實例變量
print(stu.num) # 類變量

直接看這個實例可能還不太直觀,你們本身在程序裏面動手運行一下,使用Student類調用num和name兩個變量,就會發如今調用num的時候能正常輸出,而調用name的時候報錯了。而後再用實例對象stu調用這兩個變量試試,咱們就能得出下面兩個結論:性能

  • 類變量:類變量在整個實例化的對象中是公用的,也就是定義在類中且在函數體以外的變量(但類變量一般不做爲實例變量使用);
  • 實例變量:在類的聲明中,屬性是用變量來表示的,這種變量就稱爲實例變量,是定義在類裏面的方法中的變量;

5.類方法和實例方法

講完了類變量和實例變量,那咱們來看一下類方法和實例方法:學習

class Student:
    num = 100
    def __init__(self, name):
        self.name = name
    def showClass(self):
        return '張三'

    @classmethod
    def add(cls):
        print(cls)

stu = Student('張三')    # 實例化,會調用__init__方法,self會自動傳遞,不能有return返回值
print(stu.name)          # 實例變量 輸出:張三
print(stu.num)           # 類變量  輸出: 100

print(stu.__dict__)     # 類的屬性保存在本身的字典中,包括類變量和方法
print(stu.__dict__)     # 實例的屬性保存在本身的字典中,包括實例的變量

stu.add()              # 類方法能夠被實例對象調用  輸出:<class '__main__.Student'>
stu.__class__.add()

print(Student.showClass())     # 報錯,實例方法不能夠被類對象調用
print(Student.name)            # 實例能夠訪問類的屬性,類沒法訪問實例的屬性

咱們動手試一下,分別用Student類,和實例對象stu來調用類方法和實例方法,咱們就能得出一下結論:測試

  • 類方法使用裝飾器@classmethod,第一個參數必須是當前類對象,該參數名通常約定爲「cls」,經過它來傳遞類的屬性和方法(不能傳實例的屬性和方法);
  • 類方法能夠被實例對象和類對象調用;
  • 實例方法第一個參數必須是實例對象,該參數名通常約定爲「self」,經過它來傳遞實例的屬性和方法(也能夠傳類的屬性和方法);
  • 實例方法只能由實例對象調用;

6.靜態方法

除了類方法和實例方法以外,還有一個方法叫作靜態方法,咱們一塊兒來看一下靜態方法如何使用:編碼

class Student:
    def __init__(self, name):
        self.name = name

    @staticmethod
    def sub():
        print('static')

stu = Student('李四')

stu.sub()
# 實例能夠調用靜態方法    輸出:static

Student.sub()
# 類能夠調用靜態方法  輸出:static

而後咱們能夠得出一下兩個結論:設計

  • 靜態方法使用裝飾器@staticmethod,參數隨意,沒有「self」和「cls」參數,可是方法體中不能使用類或實例的任何屬性和方法;
  • 靜態方法能夠被實例對象和類對象調用;

7.私有屬性和保護屬性

  • 兩個下劃線__開頭的屬性爲私有屬性,不能在類的外部被使用或直接訪問;
  • 一個下劃線_開頭的爲保護屬性,只有類實例和子類實例能訪問,需經過類提供的接口進行訪問;

咱們來看一下私有屬性和保護屬性是如何使用的:

class Student(object):
    def __init__(self):
        self.__private_field = 200    # private
        self._protect_field = 200    # protect

stu = Student()
# print(stu.__private_field)  # 報錯,實例對象不能夠調用私有屬性
print(stu._protect_field)   # 輸出:200

8.屬性裝飾器

  • 第一種寫法:使用@property裝飾器,將類的方法變爲屬性;
class Student:
    num = 100
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

    @name.deleter
    def name(self):
        del self.__name

stu = Student('張三')
print(stu.name)     # 輸出:張三

stu.name = '李四'
print(stu.name)     # 輸出:李四
  • 第二種寫法:使用屬性函數property(),直接把方法當屬性來操做;
class Student:
    def __init__(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

    def set_name(self, value):
        self.__name = value

    def del_name(self):
        del self.__name
        print('實例的屬性被刪除了')

    # 這裏表示property是用來修飾name這個屬性的
    name = property(fget=get_name, fset=set_name, fdel=del_name, doc='hello')

stu = Student('張三')
print(stu.name)    # 張三

stu.name = '李四'  # 給name賦值
print(stu.name)   # 李四
  • 一個property對象包含三個方法:getter, setter, deleter,當一個函數被@property裝飾器修飾時,系統會自動建立一個包含對應訪問函數的同名屬性;

9.類的繼承

class Animal:
    def __init__(self):
        self.type = 'animals'
    def eat(self):
        print('{} 吃,Animal類中'.format(self.__class__.__name__))   # Animal 吃,Animal類中

class Person(Animal):
    def talk(self):
        print('講話')

# animal = Animal()
# animal.eat()
# 
# print(animal.type)  # animals
# animal.talk()       # 報錯,父類不能夠調用子類的方法

person = Person()
print(person.type)  # animals

# Person調用Animal類的eat方法
person.eat()       # 輸出:Person 吃,Animal類中
person.talk()      # 講話
  • 繼承是一種建立類的方法,一個類能夠繼承來自一個或多個父類,原始類稱爲基類或超類;
  • 繼承能夠很方便的幫助子類擁有父類的屬性和方法,減小代碼冗餘,子類能夠定義本身的方法和屬性,也能夠覆蓋父類的方法和屬性;
  • 實現繼承:指使用基類的屬性和方法而無需額外編碼的能力;
  • 接口繼承:指僅使用屬性和方法的名稱、可是子類必須提供實現的能力(子類重構父類方法);

10.屬性查找順序

class Animal:
    __name = 'animal'
    def __init__(self):
        self.type = 'animal'
    def eat(self):
        print('{} eat'.format(self.__class__.__name__))

class Person(Animal):
    def talk(self):
        print('talk')

person = Person()
print(person.__name)   # 子類不能訪問 父類的私有屬性
  • 父類的私有屬性沒法被子類訪問;
  • 屬性的查找順序:先從對象自身的__dict__中查找->而後從對象所在類的__dict__中查找->而後從父類的__dict__中查找,直至找到或者報錯沒有找到;

11.方法重寫

  • 子類能夠覆蓋父類的方法,能夠在覆蓋的方法中調用父類的方法,且父類的類方法,靜態方法也能夠被覆蓋
class Animal:
    def __init__(self):
        self.type = 'animal'

    def eat(self):
        print('{} eat'.format(self.__class__.__name__))

class Person(Animal):
    def eat(self):
        print('洗手')  # 洗手
        print('{} eat'.format(self.__class__.__name__))  # Person eat
        super().eat()  # 調用父類的方法                    # Person eat
        # super(Person, self).eat()   等價於 super().eat()

person = Person()
person.eat()
  • 繼承中的init方法
class Animal:
    def __init__(self):
        self.one = 'one'

class Person(Animal):
    def __init__(self):
        self.two = 'two'
        self.three = 'three'

    def show(self):
        print(self.one, self.two, self.three)

person = Person()
print(person.__dict__)  # 輸出:{'two': 'two', 'three': 'three'}
person.show()    # 報錯,person沒有one這個屬性
print(person.one)  #一樣報錯
  • 代碼修改,若是父類有__init__方法,且子類也有__init__方法,最好在子類的__init__方法中手動調用父類的__init__方法
class Animal:
    def __init__(self):
        self.one = 'one'

class Person(Animal):
    def __init__(self):
        self.two = 'two'
        self.three = 'three'
        super().__init__()  # 繼承父類的__init__方法,且父類init方法的調用寫在子類的什麼地方也有講究

    def show(self):
        print(self.one, self.two, self.three)

person = Person()
print(person.__dict__)
print(person.one)
# 輸出:one  ,由於繼承了父類的init__方法,因此能夠訪問one屬性
  • 若是你的父類方法的功能不能知足你的需求,就能夠在子類重寫你父類的方法;
  • 子類能夠覆蓋父類的方法,且能夠在覆蓋的方法中調用父類的方法;
  • super()函數是用於調用父類(超類)的一個方法, Python 3 可使用直接使用super().xxx 代替 super(Class, self).xxx

12.裝飾器

  • 裝飾器是一個很著名的設計模式,常常被用於有切面需求的場景,較爲經典的有插入日誌、性能測試、事務處理等;
  • python裝飾器本質上就是一個函數,它可讓其餘函數在不須要作任何代碼變更的前提下增長額外的功能,裝飾器的返回值也是一個函數對象(函數的指針);
  • 歸納的講,裝飾器的做用就是爲已經存在的對象添加額外的功能,也稱之爲擴展功能;

13.Mixin

  • Mixin是一種設計模式,經過多繼承的方式對類的功能進行加強;
  • Mixin能夠在不修改任何源代碼的狀況下,對已有類進行擴展;
  • 能夠根據須要使用已有的功能進行組合,來實現「新」類;
  • 還能很好的避免類繼承的侷限性,由於新的業務須要可能就須要建立新的子類;

14.Mixin類的注意點

  • 在Mixin類中,不能寫初始化的__init__方法,由於Mixin類不作爲獨立類使用;
  • Mixin類原則上必須做爲其餘類的基類,實現其餘類的功能加強;
  • Mixin類的基類必須也是Mixin類;
  • 使用Mixin方式加強功能的的類,必須將Mixin類寫在繼承列表的第一個位置;
  • Mixin比decorator更增強大;

參考:https://www.9xkd.com/user/plan-view.html?id=1853344716

相關文章
相關標籤/搜索