如何優雅地面向對象

概念

類(Class) :用來描述具備相同屬性和方法的對象的集合。它定義了該集合中每一個對象所共有的屬性和方法。其中的對象被稱做類的實例。
實例/對象:經過類定義的初始化方法,賦予具體的值,成爲一個"有血有肉的實體"。
實例化:類--->對象 的過程或操做。python

類變量:類變量是全部實例公有的變量。類變量定義在類中,但在方法體以外。
實例變量:定義在實例中的變量,只做用於當前實例。api

實例方法:至少有一個參數而且以實例對象(self)做爲其第一個參數的方法。
靜態方法:不須要實例化就能夠由類執行的方法。
類方法:類方法是將類自己做爲對象進行操做的方法。安全

封裝:將內部實現包裹起來,對外透明,提供api接口進行調用的機制。
繼承:即一個派生類(derived class)繼承父類(base class)的變量和方法。
多態:根據對象類型的不一樣以不一樣的方式進行處理。數據結構

類的兩種做用

1.屬性引用(類名.屬性)
class Person:   # 定義一個Person類
    role = 'person'  # 靜態屬性就是直接在類中定義的變量
    
    def __init__(self,name):
        self.name = name  # 每個角色都有本身的暱稱;
        
    def walk(self):  # 動態屬性就是定義在類中的方法
        print("person is walking...")

print(Person.role)  #查看人的role屬性
print(Person.walk)  #引用人的走路方法,注意,這裏不是在調用



2.實例化:類名加括號,自動觸發__init__函數的運行,爲每一個實例定製本身的特徵
class 類名:
    類屬性 = None
    def __init__(self,參數1,參數2):
        self.對象屬性1 = 參數1
        self.對象屬性2 = 參數2

    def 方法名(self):
        pass


實例/對象名 = 類名(參數)  # 對象就是實例,表明一個具體的東西
                          # 類名() : 類名+括號就是實例化一個類,至關於調用了__init__方法
                          # 括號裏傳參數,參數不須要傳self,其餘與init中的形參一一對應
                          # 結果返回一個對象
實例/對象名.對象屬性   # 查看對象的屬性
實例/對象名.方法名()      # 調用類中的方法

類的屬性和方法

類屬性/實例屬性(也叫類變量、實例變量)

  1:實例屬性:

    最好在__init__(self,...)中初始化

    內部調用時都須要加上self.

    外部調用時用instancename.propertyname

  2:類屬性:

    在__init__()外初始化

    在內部用classname.類屬性名調用

    外部既能夠用classname.類屬性名又能夠用instancename.類屬性名來調用

  3:私有屬性:

    1):單下劃線_開頭:只是告訴別人這是私有屬性,外部依然能夠訪問更改

    2):雙下劃線__開頭:外部不可經過instancename.propertyname來訪問或者更改

      實際將其轉化爲了_classname__propertyname

實例方法/靜態方法/類方法

實例方法:類的實例方法由實例調用,至少包含一個self參數,且爲第一個參數。執行實例方法時,會自動將調用該方法的實例賦值給self。self表明的是類的實例,而非類自己
靜態方法:靜態方法由類調用,無默認參數。將實例方法參數中的self去掉,而後在方法定義上方加上@staticmethod,就成爲靜態方法。它屬於類,和實例無關。建議只使用類名.靜態方法的調用方式。(雖然也可使用實例名.靜態方法的方式調用)函數

class Foo:

    @staticmethod
    def static_method():
        pass

#調用方法
Foo.static_method()

類方法:類方法由類調用,採用@classmethod裝飾,至少傳入一個cls(代指類自己,相似self)參數。執行類方法時,自動將調用該方法的類賦值給cls。建議只使用類名.類方法的調用方式。(雖然也可使用實例名.類方法的方式調用)code

class Foo:

    @classmethod
    def class_method(cls):
        pass

Foo.class_method()

綜合例子:orm

class Foo: 

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

    def ord_func(self):
        """定義實例方法,至少有一個self參數 """
        print('實例方法')

    @classmethod
    def class_func(cls):
        """ 定義類方法,至少有一個cls參數 """
        print('類方法')

    @staticmethod
    def static_func():
        """ 定義靜態方法 ,無默認參數"""
        print('靜態方法') 

# 調用實例方法
f = Foo("Jack")
f.ord_func()
Foo.ord_func(f) # 請注意這種調用方式,雖然可行,但建議不要這麼作!

# 調用類方法
Foo.class_func()
f.class_func()  # 請注意這種調用方式,雖然可行,但建議不要這麼作!

# 調用靜態方法
Foo.static_func()
f.static_func() # 請注意這種調用方式,雖然可行,但建議不要這麼作!

封裝、繼承、多態

封裝

封裝是指將數據與具體操做的實現代碼放在某個對象內部,使這些代碼的實現細節不被外界發現,外界只能經過接口使用該對象,而不能經過任何形式修改對象內部實現,正是因爲封裝機制,程序在使用某一對象時不須要關心該對象的數據結構細節及實現操做的方法。使用封裝能隱藏對象實現細節,使代碼更易維護,同時由於不能直接調用、修改對象內部的私有信息,在必定程度上保證了系統安全性。類經過將函數和變量封裝在內部,實現了比函數更高一級的封裝。對象

繼承

Python3的繼承機制繼承

  • 子類在調用某個方法或變量的時候,首先在本身內部查找,若是沒有找到,則開始根據繼承機制在父類裏查找。
  • 根據父類定義中的順序,以深度優先的方式逐一查找父類!
    繼承參數的書寫有前後順序,寫在前面的被優先繼承。

多態

多態指的是一類事物有多種形態索引

import abc
class Animal(metaclass=abc.ABCMeta): #同一類事物:動物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #動物的形態之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #動物的形態之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #動物的形態之三:豬
    def talk(self):
        print('say aoao')
        
peo=People()
dog=Dog()
pig=Pig()

# peo、dog、pig都是動物,只要是動物確定有talk方法
# 因而咱們能夠不用考慮它們三者的具體是什麼類型,而直接使用
peo.talk()
dog.talk()
pig.talk()

#更進一步,咱們能夠定義一個統一的接口來使用
def func(obj):
    obj.talk()  # 調用的邏輯都同樣,執行的結果卻不同

talk(peo)
talk(dog)
talk(pig)

特殊成員和魔法方法

__init__ :      構造函數,在生成對象時調用
__del__ :       析構函數,釋放對象時使用
__repr__ :      打印,轉換
__setitem__ :   按照索引賦值
__getitem__:    按照索引獲取值
__len__:        得到長度
__cmp__:        比較運算
__call__:       調用
__add__:        加運算
__sub__:        減運算
__mul__:        乘運算
__div__:        除運算
__mod__:        求餘運算
__pow__:        冪

經常使用以下

1.__getitem__()、__setitem__()、__delitem__()
a = 標識符[] :   執行__getitem__方法
標識符[] = a  :   執行__setitem__方法
del 標識符[] :   執行__delitem__方法

class Foo:

    def __getitem__(self, key):
        print('__getitem__',key)

    def __setitem__(self, key, value):
        print('__setitem__',key,value)

    def __delitem__(self, key):
        print('__delitem__',key)


obj = Foo()

result = obj['k1']      # 自動觸發執行 __getitem__
obj['k2'] = 'jack'      # 自動觸發執行 __setitem__
del obj['k1']           # 自動觸發執行 __delitem__

2.__str__(),__repr__()改變對象的字符串顯示
3.__format__自定製格式化字符串
4.__del__()析構方法,當對象在內存中被釋放時,自動觸發執行
5.__new__單例模式
6.__call__()對象後面加括號,觸發執行
7.__len__() len()時執行
8.__module__ 表示當前操做的對象在屬於哪一個模塊
9.__class__ 表示當前操做的對象屬於哪一個類
10.__dict__列出類或對象中的全部成員!很是重要和有用的一個屬性
11.__iter__這是迭代器方法!列表、字典、元組之因此能夠進行for循環,內部定義了 __iter__()這個方法
12.__slots__限制實例能夠添加的變量
...

反射

反射(或自省)主要是指程序能夠訪問、檢測、和修改它自己狀態或行爲的一種能力,在python中,經過字符串的形式操做對象相關的屬性,python中的一切事物都是對象,即均可以使用反射。
反射4個內置函數分別爲:getattr、hasattr、setattr、delattr 獲取成員、檢查成員、設置成員、刪除成員

hasattr:hasattr(object,name)判斷一個對象是否有name屬性或者name方法。有就返回True,沒有就返回False 
getattr:獲取對象的屬性或者方法,若是存在則打印出來。hasattr和getattr配套使用,須要注意的是,若是返回的是對象的方法,返回出來的是對象的內存地址,若是須要運行這個方法,能夠在後面添加一對()   
setattr:給對象的屬性賦值,若屬性不存在,先建立後賦值  
delattr:刪除該對象指定的一個屬性
  • isinstance(obj,cls)檢查是否obj是不是類 cls 的對象
  • issubclass(sub, super)檢查sub類是不是 super 類的派生類

其餘

關於self

self:在實例化時自動將對象/實例自己傳給__init__的第一個參數,你也能夠給他起個別的名字。
實例(對象)只有一種做用:屬性引用

關於super

若是子類中實現了調用父類的方法:        
    在類內:super(子類,self).方法名()  supper().__init__(參數) 
    在類外:super(子類名,對象名).方法名()

零碎

getattr(obj,"name") = obj.name
Python內置的@property裝飾器就是負責把一個方法變成屬性調用
super 是用來解決多重繼承問題的,直接用類名調用父類方法在使用單繼承的時候沒問題,
可是若是使用多繼承,會涉及到查找順序(MRO)、重複調用(鑽石繼承)等種種問題。
相關文章
相關標籤/搜索