Python第七章-面向對象

面向對象編程基礎

一、面向對象概念

1.1 什麼是面向過程

​ 就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了。

​ 生活中的的例子舉例。

1.2 什麼是面向對象

​ 面向對象是把構成問題事務分解成各個對象,建立對象的目的不是為了完成一個步驟,而是為了描敘某個事物在整個解決問題的步驟中的行為。

  1. 面向對象是一種思維方法
  2. 面向對象是一種編程方法
  3. 面向對象並不只針對某一種程式語言

1.3 面向對象和面向過程的區別和聯繫

面向過程,做什麼

面向過程過程側重整個問題的解決步驟,著眼局部或者具體,核心:過程的實現

【面向過程】:
為了把大象裝進冰箱,需要3個過程。

  1. 把冰箱門打開(得到打開門的冰箱)
  2. 把大象裝進去(打開門後,得到裡面裝著大象的冰箱)
  3. 把冰箱門關上(打開門、裝好大象後,獲得關好門的冰箱)
    每個過程有一個階段性的目標,依次完成這些過程,就能把大象裝進冰箱。

面向對象,誰來做

面向對象側重具體的功能,讓某個對象具有這樣的功能。更加側重於整體。核心:對象

【面向對象】:
為了把大象裝進冰箱,需要做三個動作(或者叫行為)。
每個動作有一個執行者,它就是對象。

  1. 冰箱,你給我把門打開
  2. 大象,你給我鑽到冰箱里去
  3. 冰箱,你給我把門關上
    依次做這些動作,就能把大象裝進冰箱。
各自的優缺點    面向過程的優點:        流程化使得編程任務明確,在開發之前基本考慮了實現方式和最終結果;        效率高,面向過程強調程式碼的短小精悍,善於結合數據結構來開發高效率的程式。。        流程明確,具體步驟清楚,便於節點分析。      缺點是:需要深入的思考,耗費精力,程式碼重用性低,擴展能力差,維護起來難度比較高,            對複雜業務來說,面向過程的模組難度較高,耦合度也比較高。    面向對象的優點:結構清晰,程式便於模組化,結構化,抽象化,更加符合人類的思維方式;      封裝性,將事務高度抽象,從而便於流程中的行為分析,也便於操作和自省;      容易擴展,程式碼重用率高,可繼承,可覆蓋;      實現簡單,可有效地減少程式的維護工作量,軟體開發效率高。      缺點是:效率低,面向對象在面向過程的基礎上高度抽象,從而和程式碼底層的直接交互非常少機會,                從而不適合底層開發和遊戲甚至多媒體開發;                複雜性,對於事務開發而言,事務本身是面向過程的,過度的封裝導致事務本身的複雜性提高。  

程式語言對面向對象的實現主流的有兩種方式:基於類的面向對象和基於原型的面向對象。

不管以什麼方式實現,都具有面向對象的三大特徵:

  • 封裝

    也就是把客觀事物封裝成抽象的類或具體的對象,並且類或對象可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行資訊隱藏。

  • 繼承

    可以讓某個類型的對象獲得另一個類型的對象的屬性的方法

  • 多態

    不同實例的相同方法在不同情形有不同表現形式。多態機制使具有不同內部結構的對象可以共享相同的外部介面。

1.4程式語言中面向對象的實現方式

1.4.1基於類的面向對象

典型的語言:Java、C#、 pthon 、c++等

對象(object)依靠 類(class)來產生

1.4.2基於原型的面向對象

典型的語言:JavaScript

對象(object)則是依靠 構造器(constructor)利用 原型(prototype)構造出來的

二、python中的類

python 是基於類的面向的對象語言, 所以學習 python 面向對象, 必須要先學習類

2.1類和實例的概念

通過前面的學習我們已經知道, 面向對象編程(Object Oriented Programming,簡稱OOP),是一種程式設計思想。OOP把對象作為程式的基本單元,一個對象包含了 數據和操作數據的函數(方法)

而面向對象的程式設計把電腦程式視為一組對象的集合,而每個對象都可以接收其他對象發過來的消息,並處理這些消息,電腦程式的執行就是一系列消息在各個對象之間傳遞。


2.1.1 類的理解

在 python 中所有類型的數據都可以看成對象, 包括我們我們以前學習的所有的內置類型int, float等.

像這些 int, float, list這些數據類型, 就是我們面向對象中的類

我們可以根據需要自定義很多類型出來, 這些自定義的類型也是我們面向對象中的類


2.1.2對象的理解

我們天天說對象, 到底該怎麼去理解對象?

a = 3 變數 a 賦值為整數 3, 這裡的 3 就是一個int類型的對象.
nums = [2, 4, 1] 這裡的 [2, 4, 1] 就是list類型的列表對象.

可以這麼說, 在 python 中任何類型的數據數據都是對象.(和其他語言 c++, java 等不太一樣, 他們有一些不是對象的基本數據類型)

對象其實就是對數據和操作數據的方法進行的封裝


2.1.3類與對象的關係

類與對象的關係, 就是我們每個人同人類的關係一樣.

人類是對每個具體的人的共同的特點的抽象. 比如都有手腳, 耳朵, 還都可以吃飯, 娛樂, 男人還可以去大保健. 人類就是程式語言中的類型.

而每個人是人類的一個個的具體的實現. 每個人都是程式語言中的一個對象


類是對對象的抽象描述, 而對象是類的具體實現. 他們是抽象與具體的關係.


例如:
汽車製造圖紙和一個個的汽車.

圖紙就是類, 汽車就是對象


2.1.4先有對象還是先有類

看情況:

  1. 在做面向對象分析的時候, 一般是先有對象, 然後把對象的共性抽象出來, 然後就形成了類.
  2. 編寫程式碼的時候, 一般是先完成類的編寫, 然後在需要的時候創建對象就可以了.

2.2 定義類

我們需要關鍵字class來定義類

class 類名:      # 類中定義的內容  

比如我們定義一個表示學生的Student類:

class Student:      pass  

說明:

  1. class 後面緊跟類名. 類名的命名使用駝峰命名法.
  2. 類名的後面跟一個冒號
  3. 然後是帶有正確縮進的類的主體.(上面的例子中使用 pass, 表示空的類主體)

2.3創建對象

定義類的目的就是使用類來創建對象.

在 python 中使用類創建對象與其他語言差別較大, 其他語言需要使用關鍵字new, python 精簡了創建方式, 我們不需要new.

2.3.1 python 就是以函數形式調用來創建對象的!


python 創建對象直接使用類名()就可以了.

class Student:      pass      s1 = Student()    # 使用類創建對象  print(s1)  

創建對象的過程是不是和方法調用一樣一樣的?!

2.4類定義的深入研究

類必須先定義再使用

類的定義與函數定義是一樣的, 都要遵循先定義再使用

s2 = Student()    # 此處無法使用類來創建對象      class Student:      pass  

可以在if語句中甚至函數中定義類

if True:      class Student:          pass      s1 = Student()  print(s1)  

使用類的時候, 一定要保證類被已經被定義了.


類的主體

類主體中都可以定義什麼?

在 python 中類可以定義:


1. 欄位(field, 在中國很多人叫屬性)

在 python 中欄位分 2 種:

  1. 實例變數(Instance Variables):
    他存儲的值是屬於每一個對象的, 或者對不同的對象來說他們是獨立. 就像人的私有財產
  2. 類變數(Class Variables):
    他存儲的值是屬於類本身的, 而不是屬於某一個具體的對象, 或者說他是被所有的對象所共有的. 就像公共財產

2. 方法

方法就是我們以前學習的函數, 只是同類扯上關係了而已!

在 python 中的方法分 3 種:

  1. 實例方法.
    和實例變數一樣, 他是屬於對象的.
    實例方法相比普通的函數有一個比較特殊的地方:第一個形參總是會自動傳入這個實例(對象)
  2. 類方法
    和類變數一樣, 是屬於類的.
    類方法與實例方法的區別在於第一個參數: 類方法的第一個參數總是這個類本身.
  3. 靜態方法
    靜態方法和實例沒有任何關係. 它僅僅是定義在類中而已, 這個時候類就相當於這個方法的命名空間.

2.4.1類中定義欄位

2.4.1.1類變數

類變數表示所有的對象所共有的一種數據.換句話說, 所有的對象都可以直接訪問到, 並且他們訪問到的是同一個變數(肯定是相同的值!)

把變數直接聲明在類的內部的變數.

訪問類變數直接通過類名.變數名來訪問.

class Student:      # 直接在類中定義變數,      # 這樣的變數就是類變數(類屬性, 類似 java 中的靜態屬性)      country = "china"      print(Student.country)  # 修改類變數的值  Student.country = "usa"  print(Student.country)  


當然你也可以在任何的地方通過類名.屬性=值的方式動態給類添加類屬性.

class Student:      # 直接在類中定義變數,      # 這樣的變數就是類變數(類屬性, 類似 java 中的靜態屬性)      country = "china"      Student.count = 1   # 動態添加類屬性  print(Student.count)  

但是, 建議在類中直接定義類屬性, 只有在需要的時候才使用動態的方式添加類屬性.


2.4.1.2 實例變數

實例變數表示每個對象都自己所獨有.

所以, 實例變數與每個具體的對象相關.

添加實例變數:

一般是在一個叫做__init__()的方法裡面來添加. (關於方法下節再細講, 現在你只需要知道他是一個函數就可以了)

__init__(), 注意他是左右分別有 2 個_. 這個方法不需要我們手動去調用, 當你使用類創建對象的時候, 會自動調用這個方法.

這個方法的第 1 個參數永遠是創建的那個對象(所有的實例方法都是這樣的), 我們可以通過這個參數來向對象中添加屬性, 這樣的屬性就是實例屬性.

作為國際慣例, 第 1 個參數的一般都命名為self, 當然你也可以給他起個別的名字, 但是何必呢!


class Student:      def __init__(self):          self.name = "李四"          self.age = 20      s = Student()  print(s.name)  print(s.age)  


上面這種寫法, 會導致使用這個類創建的對象的nameage這個兩個實例變數的值都會初始化為同樣的值, 這其實並不好.

我們想每個變數初始化的值都不一樣, 可以在創建的對象的是把具體的值溝通過實參傳遞過去, 則實參會自動傳到__init__()方法的第 2 個開始的參數中.

class Student:      def __init__(self, name, age):          self.name = name          self.age = age      zs = Student("張三", 20)  print(zs.name + " " + str(zs.age))  ls = Student("李四", 30)  print(ls.name + " " + str(ls.age))  

你會發現每個對象的值都不一樣的!


2.4.1.3 可以通過對象直接訪問類變數

python 允許通過對象直接訪問到類變數. (有些語言是不允許的, 比如 c#, JavaScript 就不允許, 但是 java 允許)

class Student:      country = "china"        def __init__(self, name, age):          self.name = name          self.age = age      zs = Student("張三", 20)  print(zs.name + " " + str(zs.age))  ls = Student("李四", 30)  print(ls.name + " " + str(ls.age))    print(zs.country) # 通過對象直接訪問類變數  print(ls.country)  


2.4.2 類中定義方法

2.4.2.1. 定義實例方法

實例方法和普通方法的區別:

  1. 實例方法必須定義在類中
  2. 實例方法的第一個參數永遠是那個具體的對象, 而且不需要調用者手動傳入, python 會自動傳入那個對象. 而且這個形參一般命名為self
  3. 實例方法調用方式:對象.實例方法(實參)

class Student:      def __init__(self, name):          self.name = name        # 定義一個實例方法:一個參數表示具體的對象, python 會默認傳入      def speak(self):          print("我的名字是:" + self.name)  # 在實例方法中訪問實例變數      s = Student("志玲")  s.speak()  


self的理解

學過其他語言的人都知道, python 的 self 其實就是在其他語言的this.

那麼self到底指代哪個對象?

通過哪個對象調用的這個實例方法, 那麼這個實例方法中的self就指代誰

而且 python 的self一朝綁定, 終身不變. (不像 javascript 中的那個 this 小婊砸隨著調用方式的不同而而更改綁定對象, 讓你眼花繚亂到懷疑人生)


class Student:      def __init__(self, name):          self.name = name        # 定義一個實例方法:一個參數表示具體的對象, python 會默認傳入      def speak(self):          print("我的名字是:" + self.name)  # 在實例方法中訪問實例變數      s = Student("志玲")  # s.speak 的 this 永遠指定的是 s 對象  foo = s.speak  # 重新定義一個變數, 指向了對象 s 的 speak 方法.  foo()  # 則 self 仍然綁定的是 s 對象  


class Student:      def __init__(self, name):          self.name = name        def speak(self):          print("我的名字是:" + self.name)  # 在實例方法中訪問實例變數      s1 = Student("志玲")  s2 = Student("鳳姐")    s2.speak = s1.speak    s2.speak()  

說明:
s1.speak表示的那個方法賦值給s2.speak, 雖然你調用的時候使用的是s2.speak(), 但是他指向的那個方法的self已經永遠的綁定到了s1


2.4.2.2. 定義類方法

類方法和實例方法的區別:

  1. 一個方法想成為類方法, 需要在方法的上面要添加一個內置裝飾器:@classmethod
  2. 實例方法的第一個參數是具體的對象, 而類方法的第一個參數是類對象.(python 中一切皆對象, 那麼類本身也是個對象, 我們一般稱之為類對象). 類對象也是 python 自動傳遞. (類對象一般起名為 cls)
  3. 調用實例方法使用對象.實例方法(實參), 而調用類方法使用類名.類方法(實參)
  4. 類方法中可以訪問類變數, 但是不能訪問實例變數, 因為沒有self啊.

class Student:      country = "china"        @classmethod      def say(cls):          print("我們的國家是:" + cls.country)    Student.say()  


什麼時候使用類方法

如果一個方法不需要操作實例變數, 則這個方法建議定義成類方法!


注意:

有一點注意, 類方法也可以通過實例對象來調用, 但是不管怎麼調用, 類方法的第一個參數總是類對象!

class Student:      country = "china"        @classmethod      def say(cls):          print("我們的國家是:" + cls.country)      Student.say()  s = Student()  s.say()  


2.3.2.3. 定義靜態方法

靜態方法其實是一種普通的函數, 他僅僅是處於類的命名空間中.(把當前類作為了他的命名空間, 其他情況與普通函數沒有任何區別).

  1. 定義靜態方法只需要在函數的上面添加一個裝飾器:@staticmethod
  2. 調動靜態方法: 類名.靜態方法(實參). 只需要把類名作為他的前綴來執行, 與普通函數相比, 沒有其他任何多餘的操作!

class Student:      @staticmethod  # 定義靜態方法      def create_student():          print("我是個靜態方法")      Student.create_student()  


靜態方法的應用場景

因為 python 的類中只有一個__init__()方法, 意味著創建對象的方式只能有一種.

我們可以使用靜態方法給外界提供其他的創建對象的方式.


import time  class Date:      """      創建一個表示日期的類      """  	def __init__(self, year, month, day):          self.year = year          self.month = month          self.day = day        @staticmethod      def now():          """          創建表示當前日期的對象          :return:          """          t = time.localtime()          return Date(t.tm_year, t.tm_mon, t.tm_mday)        @staticmethod      def tomorrow():          """          創建表示明日日期的對象          :return:          """          t = time.localtime(time.time() + 60 * 60 * 24)          return Date(t.tm_year, t.tm_mon, t.tm_mday)    now = Date.now()  print("現在是: %d年%d月%d日" % (now.year, now.month, now.day))  now = Date.tomorrow()  print("明天是: %d年%d月%d日" % (now.year, now.month, now.day))  

2.4.3 python中的__init()__方法

在任何一個 python 類中, 都存在一個__init__()的方法, 而且只能存在一個.

我們不管用什麼辦法創建對象, 都會自動的去執行__init__()方法

默認情況下的__init()__

默認情況, __init()__什麼都不做.

__init__()初始化實例變數

__init()__方法的主要作用就是添加並初始化實例變數. 初始化的實例變數的值在創建對象的時候通過實參傳遞過來.

__init__()的第一個形參總是新創建的那個實例對象.