13--Python 面向?qū)ο蠡A(chǔ)

@Author : Roger TX (425144880@qq.com)
@Link : https://github.com/paotong999

一、面向?qū)ο缶幊趟枷?/h2>

面向?qū)ο缶幊獭狾bject Oriented Programming,簡稱OOP,是一種程序設(shè)計(jì)思想。OOP把對象作為程序的基本單元,一個對象包含了數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)。
在Python中,所有數(shù)據(jù)類型都可以視為對象,當(dāng)然也可以自定義對象。自定義的對象數(shù)據(jù)類型就是面向?qū)ο笾械念悾–lass)的概念。

二、類與對象(實(shí)例)

在面向?qū)ο蟮某绦蛟O(shè)計(jì)過程中有兩個重要概念:類(class)和對象(object,也被稱為實(shí)例,instance)

  • 類是某一批對象的抽象,可以把類理解成某種概念
  • 對象是根據(jù)類創(chuàng)建出來的一個個具體的“對象”,一個具體存在的實(shí)體
類與對象

Python定義類的簡單語法如下:

class 類名(object):
    執(zhí)行語句...
    零個到多個類變量...
    零個到多個方法...

Python 的語法要求:如果從程序的可讀性方面來看,Python 的類名必須是由一個或多個有意義的單詞連綴而成的,每個單詞首字母大寫,其他字母全部小寫,單詞與單詞之間不要使用任何分隔符。

需要說明的是:

  • 類中各成員之間的定義順序沒有任何影響,各成員之間可以相互調(diào)用。
  • (object) 表示該類是從哪個類繼承下來的,繼承的概念我們后面再講,通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。

對象(實(shí)例)的創(chuàng)建和使用:

class Person :
    '這是一個學(xué)習(xí)Python定義的一個Person類'
    # 下面定義了一個類變量
    hair = 'black'
    def __init__(self, name = 'Charlie', age=8):
        # 下面為Person對象增加2個實(shí)例變量
        self.name = name
        self.age = age

    def say(self, content):
        # 下面定義了一個say方法
        print(content)
        print(self.age)

f = Person()
f.__init__(age=11)    # 等同于Person(age=11)
f.say('123')
  • 創(chuàng)建實(shí)例可以通過類名+() Person() 實(shí)現(xiàn)
  • 創(chuàng)建對象時默認(rèn)調(diào)用構(gòu)造方法,可以傳入?yún)?shù),初始化實(shí)例變量
  • __init__構(gòu)造函數(shù)只能 return None

Python 類所包含的最重要的兩個成員就是變量和方法:

  • 類變量屬于類本身,用于定義該類本身所包含的狀態(tài)數(shù)據(jù)
  • 實(shí)例變量則屬于該類的實(shí)例對象,用于定義實(shí)例對象所包含的狀態(tài)數(shù)據(jù)
  • 方法則用于定義該類的對象的行為或功能實(shí)現(xiàn)

在類中定義的方法默認(rèn)是實(shí)例方法,實(shí)例方法至少應(yīng)該定義一個參數(shù),該參數(shù)通常會被命名為 self。

  • 定義實(shí)例方法的方法與定義函數(shù)的方法基本相同,只是實(shí)例方法的第一個參數(shù)會被綁定到方法的調(diào)用者(該類的實(shí)例)。
  • 類所包含的類變量可以動態(tài)增加或刪除(程序在類體中為新變量賦值就是增加類變量),程序也可在任何地方為已有的類增加變量;程序可通過 del 語句刪除己有類的類變量。
  • 實(shí)例對象的實(shí)例變量也可以動態(tài)增加或刪除(只要對新實(shí)例變量賦值就是增加實(shí)例變量),因此程序可以在任何地方為己有的對象增加實(shí)例變量;程序可通過 del 語句刪除已有對象的實(shí)例變量。

使用 __slots__ 限制實(shí)例的屬性
Python允許在定義class的時候,定義一個特殊的 __slots__ 變量,來限制該class實(shí)例能添加的屬性

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱
  • 由于'score'沒有被放到 __slots__ 中,所以不能綁定score屬性,試圖綁定score將得到AttributeError的錯誤
  • 使用 __slots__ 要注意,__slots__ 定義的屬性僅對當(dāng)前類實(shí)例起作用,對繼承的子類是不起作用的

三、變量與方法

構(gòu)造方法

  1. 在實(shí)例方法中有一個特別的方法:__init__,這個方法被稱為構(gòu)造方法。構(gòu)造方法用于構(gòu)造該類的對象,Python 通過調(diào)用構(gòu)造方法返回該類的對象(無須使用 new)。

  2. 構(gòu)造方法是一個類創(chuàng)建對象的根本途徑,因此 Python 還提供了一個功能:如果開發(fā)者沒有為該類定義任何構(gòu)造方法,那么 Python 會自動為該類定義一個只包含一個 self 參數(shù)的默認(rèn)的構(gòu)造方法。

方法的調(diào)用

  1. 對象訪問方法或變量的語法是: 對象.變量|方法(參數(shù))。在這種方式中,對象是主調(diào)者,用于訪問該對象的變量或方法。

  2. 在使用類調(diào)用實(shí)例方法時,Python 不會自動為第一個參數(shù)綁定調(diào)用者。實(shí)際上也沒法自動綁定,因此實(shí)例方法的調(diào)用者是類本身,而不是對象。

  3. 如果程序依然希望使用類來調(diào)用實(shí)例方法,則必須手動為方法的每一個參數(shù)傳入?yún)?shù)值。

類變量和實(shí)例變量

變量

通常來說,實(shí)例變量是對于每個實(shí)例都獨(dú)有的數(shù)據(jù),而類變量是該類所有實(shí)例共享的屬性和方法。

  • 類變量又叫全局變量,是屬于類的特性,實(shí)例先找實(shí)例化變量,然后再去找類變量
  • 類變量可以用實(shí)例去調(diào)用
  • 如果類變量有多重繼承關(guān)系, 就需要按照指定的路線進(jìn)行查找

下面代碼定義了一個 Address 類,并為該類定義了多個類變量:

class Address :
    detail = '廣州'
    def __init__(self, detail):
        # 嘗試直接訪問類變量
        #print(detail) # 報錯
        # 通過類來訪問類變量
        print(Address.detail)    # 輸出 廣州

# 通過類來訪問Address類的類變量
print(Address.detail)
addr = Address('廣州')
# 修改addr實(shí)例的變量
addr.detail = '佛山'
print(Address.detail)    # 輸出 廣州
print(addr.detail)    # 輸出 佛山
  1. 因?yàn)閯討B(tài)語言的特性, addr.detail = '佛山' 并沒有改變類變量,只是新增了一個實(shí)例變量
  2. 在實(shí)例方法內(nèi)部,可以通過 類名.類變量 來訪問類變量,還可以使用 self__class__.類變量 來訪問類變量

四、實(shí)例方法與類方法、靜態(tài)方法

實(shí)例方法
普通實(shí)例方法,第一個參數(shù)需要是 self,它表示一個具體的實(shí)例本身。

class  Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    def __str__(self):
        return ("{year}-{month}-{day}").format(year=self.year,month=self.month,day=self.day)

    def yesterday(self):
        self.day-=1

date=Date(2018,10,20)
print(date)
date.yesterday()
print(date)

類方法
在一個方法的上面,加上裝飾器 @classmethod 就表示這個方法是類方法。
對于類方法,它的第一個參數(shù)不是self,是cls,它表示這個類本身。

靜態(tài)方法
在一個方法的上面,加上裝飾器 @staticmethod 就表示這個方法是靜態(tài)方法。
靜態(tài)方法對參數(shù)沒有要求,可以不傳。

class Foo(object):   
    X = 1
    Y = 14

    @staticmethod
    def averag(*mixes):
        # "父類中的靜態(tài)方法"
        return sum(mixes) / len(mixes)

    @staticmethod
    def static_method():
       # "父類中的靜態(tài)方法"
       print ("父類中的靜態(tài)方法")
       return Foo.averag(Foo.X, Foo.Y)

    @classmethod
    def class_method(cls):
       # 父類中的類方法
       print ("父類中的類方法" )
       return cls.averag(cls.X, cls.Y)

class Son(Foo):
     X = 3
     Y = 5

     @staticmethod
     def averag(*mixes):
        # "子類中重載了父類的靜態(tài)方法"
       print ("子類中重載了父類的靜態(tài)方法")
       return sum(mixes) / 3

p = Son()

print (p.averag(1,5))    # 最后一段,結(jié)果為2.0
print (p.static_method())    # static_method段,結(jié)果為7.5
print (p.class_method())    # class_method段,最后一段,結(jié)果為2.6

五、成員可見性

成員的可見性

  • 公開的,外部可以訪問的,public。
  • 私有的,外部不可訪問的,private。

可見性關(guān)鍵字

  • 其它語言中使用public和private關(guān)鍵字,python中沒有
  • python中使用 __ 雙下劃線區(qū)分公開和私有,__ 表示成員是私有的
class Student():
    sum1 = 1
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.score = 0   #增加變量score,并給他初始值
    def __marking(self,score):
        #加雙下劃線,變?yōu)樗接?,在外部不能引?        if score < 0:
            return "該同學(xué)的成績不正常"
        self.score = score
        print(self.name,"同學(xué)本次考試的成績?yōu)?",self.score)

student = Student('李華',18)
result = student.__marking(10)
print(result)

執(zhí)行后 'Student' object has no attribute '__marking' 錯誤,因?yàn)?__marking 對于 Student 不可見
通過 student.__dict__ 以及 dir(student) 兩個內(nèi)置的函數(shù)可以查看到stu對象的所有數(shù)據(jù)成員和方法

{'name': '李華', 'age': 18, 'score': 0}
['_Student__marking', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', 
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', 
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'score', 'sum1']

可以看到原本的 __marking 變成了 _Student__marking

訪問私有成員

class Address :
    def __init__(self, detail):
        self.__detail = detail

addr = Address('廣州')
addr.__detail = '佛山'
print(addr.__dict__)    # {'_Address__detail': '廣州', '__detail': '佛山'}
print(addr._Address__detail)    # 廣州

私有變量可以使用 _Address__detail 進(jìn)行強(qiáng)制訪問
私有變量不可見,但是對其進(jìn)行賦值操作,并不會報錯,這是因?yàn)镻ython動態(tài)語言的特性, 賦值只是新增了一個實(shí)例變量,并沒有改變原有的私有變量

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容