面向對象編程基礎 一、面向對象概念 1.1 什麼是面向過程 就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了。 生活中的的例子舉例。 1.2 什麼是面向對象 面向對象是把構成問題事務分解成各個對象,建立對象的目的不是為了完成一個步驟,而 ...
面向對象編程基礎
一、面向對象概念
1.1 什麼是面向過程
就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了。
生活中的的例子舉例。
1.2 什麼是面向對象
面向對象是把構成問題事務分解成各個對象,建立對象的目的不是為了完成一個步驟,而是為了描敘某個事物在整個解決問題的步驟中的行為。
- 面向對象是一種思維方法
- 面向對象是一種編程方法
- 面向對象並不只針對某一種編程語言
1.3 面向對象和麵向過程的區別和聯繫
面向過程,做什麼
面向過程過程側重整個問題的解決步驟,著眼局部或者具體,核心:過程的實現
【面向過程】:
為了把大象裝進冰箱,需要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先有對象還是先有類
看情況:
- 在做面向對象分析的時候, 一般是先有對象, 然後把對象的共性抽象出來, 然後就形成了類.
- 編寫代碼的時候, 一般是先完成類的編寫, 然後在需要的時候創建對象就可以了.
2.2 定義類
我們需要關鍵字class
來定義類
class 類名:
# 類中定義的內容
比如我們定義一個表示學生的Student
類:
class Student:
pass
說明:
class
後面緊跟類名. 類名的命名使用駝峰命名法.- 類名的後面跟一個冒號
- 然後是帶有正確縮進的類的主體.(上面的例子中使用
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 種:
- 實例變數(Instance Variables):
他存儲的值是屬於每一個對象的, 或者對不同的對象來說他們是獨立. 就像人的私有財產 - 類變數(Class Variables):
他存儲的值是屬於類本身的, 而不是屬於某一個具體的對象, 或者說他是被所有的對象所共有的. 就像公共財產
2. 方法
方法就是我們以前學習的函數, 只是同類扯上關係了而已!
在 python 中的方法分 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)
上面這種寫法, 會導致使用這個類創建的對象的name
和age
這個兩個實例變數的值都會初始化為同樣的值, 這其實並不好.
我們想每個變數初始化的值都不一樣, 可以在創建的對象的是把具體的值溝通過實參傳遞過去, 則實參會自動傳到__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. 定義實例方法
實例方法和普通方法的區別:
- 實例方法必須定義在類中
- 實例方法的第一個參數永遠是那個具體的對象, 而且不需要調用者手動傳入, python 會自動傳入那個對象. 而且這個形參一般命名為
self
- 實例方法調用方式:
對象.實例方法(實參)
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. 定義類方法
類方法和實例方法的區別:
- 一個方法想成為類方法, 需要在方法的上面要添加一個內置裝飾器:
@classmethod
- 實例方法的第一個參數是具體的對象, 而類方法的第一個參數是類對象.(python 中一切皆對象, 那麼類本身也是個對象, 我們一般稱之為類對象). 類對象也是 python 自動傳遞. (類對象一般起名為
cls
) - 調用實例方法使用
對象.實例方法(實參)
, 而調用類方法使用類名.類方法(實參)
- 類方法中可以訪問類變數, 但是不能訪問實例變數, 因為沒有
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. 定義靜態方法
靜態方法其實是一種普通的函數, 他僅僅是處於類的命名空間中.(把當前類作為了他的命名空間, 其他情況與普通函數沒有任何區別).
- 定義靜態方法只需要在函數的上面添加一個裝飾器:
@staticmethod
- 調動靜態方法:
類名.靜態方法(實參)
. 只需要把類名作為他的首碼來執行, 與普通函數相比, 沒有其他任何多餘的操作!
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__()
的第一個形參總是新創建的那個實例對象.