程式設計是一門藝術。和藝術一樣,選擇合適的畫筆和顏料對於創作出最好的作品至關重要。 Python物件導向程式設計就是這樣一種技能。
選擇正確的程式語言是任何專案的關鍵部分,它可以導致流暢和愉快的開發或徹底的噩夢。因此,最好為您的用例使用最適合的語言。
這是在Python中學習物件導向程式設計的主要原因,Python也是最流行的程式語言之一。讓我們來學習!
- 一個示例Python程式
- 學習Python OOP的要求
- 什麼是Python中的物件導向程式設計?
- 為什麼我們在Python中使用物件導向程式設計?
- Python中的一切都是物件
- 你在Python中的第一個物件
- Python中OOP的4個支柱
- 構建區域形狀解析器計算器
一個示例Python程式
在深入探討這個問題之前,讓我們提出一個問題:你有沒有寫過像下面這樣的Python程式?
secret_number = 20 while True: number = input('Guess the number: ') try: number = int(number) except: print('Sorry that is not a number') continue if number != secret_number: if number > secret_number: print(number, 'is greater than the secret number') elif number < secret_number: print(number, 'is less than the secret number') else: print('You guessed the number:', secret_number) break
此程式碼是一個簡單的數字猜測器。嘗試將其複製到 Python 檔案中並在您的系統中執行它。它完美地實現了它的目的。
但是這裡出現了一個大問題:如果我們要求您實現一個新功能怎麼辦?它可能很簡單——例如:
“如果輸入是密碼的倍數,給使用者一個提示。”
隨著特徵數量的增加以及巢狀條件的總數的增加,程式會迅速變得複雜和繁重。
這正是物件導向程式設計試圖解決的問題。
學習Python OOP的要求
在開始物件導向程式設計之前,我們強烈建議您牢牢掌握Python基礎知識。
對被視為“基本”的主題進行分類可能很困難。因此,我們設計了一份備忘單,其中包含在Python中學習物件導向程式設計所需的所有主要概念。
- 變數:指向特定物件的符號名稱(我們將通過文章瞭解物件的含義)。
- 算術運算子:加法 (+)、減法 (-)、乘法 (*)、除法 (/)、整數除法 (//)、模 (%)。
- 內建資料型別:數字(整數、浮點數、複數)、序列(字串、列表、元組)、布林值(真、假)、字典和集合。
- 布林表示式:結果為真或假的表示式。
- 條件:評估布林表示式並根據結果進行一些處理。由if/else語句處理。
- 迴圈:重複執行程式碼塊。它可以是for或while迴圈。
- 功能:有組織和可重用的程式碼塊。您可以使用關鍵字def建立它們。
- 引數:傳遞給函式的物件。例如:
sum([1, 2, 4])
- 執行Python指令碼:開啟終端或命令列並輸入“python <檔名>”。
- 開啟Python指令碼:開啟終端並鍵入
python
或python3
(取決於您的系統)。
現在您已經清楚地瞭解了這些概念,您可以繼續理解物件導向程式設計。
什麼是Python中的物件導向程式設計?
物件導向程式設計 (OOP) 是一種程式設計正規化,我們可以在其中將複雜問題視為物件。
正規化是為解決問題提供基礎的理論。
因此,當我們談論OOP時,我們指的是一組用於解決物件問題的概念和模式。
Python中的物件是資料(屬性)和行為(方法)的單一集合。您可以將物體視為您周圍真實的事物。例如,考慮計算器:
計算器可以是一個物件
您可能會注意到,資料(屬性)始終是名詞,而行為(方法)始終是動詞。
這種劃分是物件導向程式設計的核心概念。您構建儲存資料幷包含特定型別功能的物件。
為什麼我們在Python中使用物件導向程式設計?
OOP允許您建立安全可靠的軟體。許多Python框架和庫使用這種正規化來構建它們的程式碼庫。一些示例是Django、Kivy、pandas、NumPy和TensorFlow。
讓我們看看在Python中使用OOP的主要優勢。
Python OOP的優點
以下原因將使您選擇在Python中使用物件導向程式設計。
所有現代程式語言都使用OOP
這種正規化與語言無關。如果您在Python中學習了OOP,您將能夠在以下方面使用它:
- Java
- PHP
- Ruby
- Javascript
- C#
- Kotlin
所有這些語言要麼是本機物件導向的,要麼包含物件導向功能的選項。如果你想在Python之後學習它們中的任何一個,它會更容易——你會發現處理物件的語言之間有許多相似之處。
OOP使您可以更快地編碼
更快的編碼並不意味著編寫更少的程式碼行。這意味著您可以在更短的時間內實現更多功能,而不會影響專案的穩定性。
物件導向程式設計允許您通過實現抽象來重用程式碼。這一原則使您的程式碼更加簡潔易讀。
您可能知道,程式設計師花在閱讀程式碼上的時間比編寫程式碼的時間多得多。這就是易讀性始終比儘快推出功能更重要的原因。
程式碼不清晰導致生產力下降
稍後您將看到有關抽象原則的更多資訊。
OOP幫助您避免義大利麵條式程式碼
還記得本文開頭的猜數程式嗎?
如果您繼續新增功能,將來您將擁有許多巢狀的if語句。這種無休止的程式碼行纏結稱為義大利麵條式程式碼,您應該儘可能避免使用它。
OOP為我們提供了壓縮物件中所有邏輯的可能性,因此避免了巢狀的長段if 。
OOP改善您對任何情況的分析
一旦您獲得了一些OOP的經驗,您就能夠將問題視為小而具體的物件。
這種理解導致快速的專案初始化。
結構化程式設計與物件導向程式設計
結構化程式設計是初學者最常用的正規化,因為它是構建小程式的最簡單方法。
它涉及按順序執行Python程式。這意味著你給計算機一個任務列表,然後從上到下執行它們。
讓我們看一個帶有咖啡店程式的結構化程式設計示例。
small = 2 regular = 5 big = 6 user_budget = input('What is your budget? ') try: user_budget = int(user_budget) except: print('Please enter a number') exit() if user_budget > 0: if user_budget >= big: print('You can afford the big coffee') if user_budget == big: print('It\'s complete') else: print('Your change is', user_budget - big) elif user_budget == regular: print('You can afford the regular coffee') print('It\'s complete') elif user_budget >= small: print('You can buy the small coffee') if user_budget == small: print('It\'s complete') else: print('Your change is', user_budget - small)
上面的程式碼充當咖啡店供應商。它會詢問你的預算,然後“賣”給你你能買到的最大的咖啡。
嘗試在終端中執行它。它將根據您的輸入逐步執行。
這段程式碼執行良好,但我們有三個問題:
- 它有很多重複的邏輯。
- 它使用許多巢狀的if條件。
- 閱讀和修改會很困難。
OOP的發明是為了解決所有這些問題。
讓我們看看上面用OOP實現的程式。如果您還不明白,請不要擔心。它僅用於比較結構化程式設計和麵向物件程式設計。
class Coffee: # Constructor def __init__(self, name, price): self.name = name self.price = float(price) def check_budget(self, budget): # Check if the budget is valid if not isinstance(budget, (int, float)): print('Enter float or int') exit() if budget < 0: print('Sorry you don\'t have money') exit() def get_change(self, budget): return budget - self.price def sell(self, budget): self.check_budget(budget) if budget >= self.price: print(f'You can buy the {self.name} coffee') if budget == self.price: print('It\'s complete') else: print(f'Here is your change {self.get_change(budget)}$') exit('Thanks for your transaction')
注意:本文將更深入地解釋以下所有概念。
上面的程式碼代表一個名為“Coffee”的類。它有兩個屬性——“name”和“price”——它們都在方法中使用。主要方法是“銷售”,它處理完成銷售過程所需的所有邏輯。
如果您嘗試執行該類,您將不會得到任何輸出。這主要是因為我們只是為咖啡宣告瞭“模板”,而不是咖啡本身。
讓我們使用以下程式碼實現該類:
small = Coffee('Small', 2) regular = Coffee('Regular', 5) big = Coffee('Big', 6) try: user_budget = float(input('What is your budget? ')) except ValueError: exit('Please enter a number') for coffee in [big, regular, small]: coffee.sell(user_budget)
在這裡,我們正在製作“Coffee”類的例項或咖啡物件,然後呼叫每種咖啡的“sell”方法,直到使用者負擔得起任何選項。
我們將使用兩種方法獲得相同的輸出,但我們可以使用OOP更好地擴充套件程式功能。
下表比較了物件導向程式設計和結構化程式設計:
物件導向程式設計 | 結構化程式設計 |
更易於維護 | 難以維護 |
不要重複自己 (DRY) 方法 | 多處重複程式碼 |
多處複用的小段程式碼 | 幾個地方的大量程式碼 |
物件方法 | 塊碼方法 |
更容易除錯 | 更難除錯 |
大學習曲線 | 更簡單的學習曲線 |
用於大型專案 | 優化為簡單程式 |
總結正規化比較:
- 兩種正規化都不是完美的(在簡單的專案中使用OOP可能會讓人不知所措)。
- 這只是解決問題的兩種方法;還有其他人在那裡。
- OOP用於大型程式碼庫,而結構化程式設計主要用於簡單的專案。
讓我們繼續討論Python中的內建物件。
Python中的一切都是物件
我們會告訴你一個祕密:你一直在使用OOP而沒有注意到它。
即使在Python中使用其他正規化時,您仍然使用物件來完成幾乎所有事情。
那是因為,在Python中,一切都是物件。
記住物件的定義:Python中的物件是資料(屬性)和行為(方法)的單一集合。
這匹配Python中的任何資料型別。
字串是資料(字元)和行為(upper()、lower()等)的集合。這同樣適用於整數、浮點數、布林值、列表和字典。
在繼續之前,讓我們回顧一下屬性和方法的含義。
屬性和方法
屬性是物件內部的內部變數,而方法是產生某些行為的函式。
讓我們在Python終端中做一個簡單的練習。您可以通過在終端中python
或python3
Python終端
現在,讓我們使用Python終端來發現方法和型別。
>>> wbolt = 'WBOLT, Premium WordPress Themes and Plugins' >>> wbolt.upper() 'WBOLT, PREMIUM WORDPRESS THEMES AND PLUGINS'
在第二行中,我們呼叫了一個字串方法upper() 。它以大寫形式返回字串的內容。但是,它不會更改原始變數。
>>> wbolt 'Wbolt, Premium WordPress Themes and Plugins'
在處理物件時,讓我們深入研究有價值的功能。 type()函式允許您獲取物件的型別。 “型別”是物件所屬的類。
>>> type(wbolt) # class 'str'
dir()函式返回物件的所有屬性和方法。讓我們用wbolt變數來測試一下。
>>> dir(wbolt) ['__add__', '__class__', ........... 'upper', 'zfill']
現在,嘗試列印此物件的一些隱藏屬性。
>>> wbolt.__class__ # class ‘str’ e>
這將輸出物件wbolt所屬的類。所以我們可以說type函式唯一返回的是物件的__class__屬性。
您可以試驗所有資料型別,直接在終端上發現它們的所有屬性和方法。您可以在官方文件中瞭解有關內建資料型別的更多資訊。
你在Python中的第一個物件
一個類就像一個模板。它允許您根據您定義的屬性和方法建立自定義物件。
你可以把它想象成一個餅乾切割器,你可以修改它來烘焙完美的餅乾(物件,而不是跟蹤餅乾),具有定義的特徵:形狀、大小等。
另一方面,我們有例項。例項是類的單個物件,它具有唯一的記憶體地址。
Python中的例項
現在您知道什麼是類和例項,讓我們定義一些!
要在Python中定義類,請使用class關鍵字,後跟其名稱。在本例中,您將建立一個名為Cookie的類。
注意:在Python中,我們使用駝峰命名約定來命名類。
class Cookie: pass
開啟你的Python終端並輸入上面的程式碼。要建立類的例項,只需在其後鍵入其名稱和括號。這與呼叫函式的過程相同。
cookie1 = Cookie()
恭喜——你剛剛用Python建立了你的第一個物件!您可以使用以下程式碼檢查其 id 和型別:
id(cookie1) 140130610977040 # Unique identifier of the object type(cookie1) <class '__main__.Cookie'>
可以看到,這個cookie在記憶體中有一個唯一的識別符號,它的型別是Cookie。
您還可以使用isinstance()函式檢查物件是否是類的例項。
isinstance(cookie1, Cookie) # True isinstance(cookie1, int) # False isinstance('a string', Cookie) # False
構造方法
__init__()方法也稱為“建構函式”。每次我們例項化一個物件時,它都被稱為Python。
建構函式使用它需要存在的最小引數集建立物件的初始狀態。讓我們修改Cookie類,使其在其建構函式中接受引數。
class Cookie: # Constructor def __init__(self, name, shape, chips='Chocolate'): # Instance attributes self.name = name self.shape = shape self.chips = chips
在Cookie類中,每個cookie都必須有一個name、shape和chips。我們將最後一種定義為“Chocolate”。
另一方面, self指的是類的例項(物件本身)。
嘗試將類貼上到終端中並像往常一樣建立cookie的例項。
cookie2 = Cookie() # TypeError
你會得到一個錯誤。那是因為您必須提供物件生存所需的最少資料集——在本例中,name和shape,因為我們已經將chips設定為“Chocolate”。
cookie2 = Cookie('Awesome cookie', 'Star')
要訪問例項的屬性,您必須使用點表示法。
cookie2.name # 'Awesome cookie' cookie2.shape # 'Star' cookie2.chips # 'Chocolate'
目前, Cookie類沒有任何內容。讓我們新增一個示例方法bake()以使事情變得更有趣。
class Cookie: # Constructor def __init__(self, name, shape, chips='Chocolate'): # Instance attributes self.name = name self.shape = shape self.chips = chips # The object is passing itself as a parameter def bake(self): print(f'This {self.name}, is being baked with the shape {self.shape} and chips of {self.chips}') print('Enjoy your cookie!')
要呼叫方法,請使用點表示法並將其作為函式呼叫。
cookie3 = Cookie('Baked cookie', 'Tree') cookie3.bake() # This Baked cookie, is being baked with the shape Tree and chips of Chocolate Enjoy your cookie!
Python中OOP的4個支柱
物件導向程式設計包括四個主要支柱:
1. 抽象
抽象對使用者隱藏了應用程式的內部功能。使用者可以是最終客戶或其他開發人員。
我們可以在日常生活中找到抽象。例如,您知道如何使用手機,但每次開啟應用程式時,您可能並不確切知道手機內部發生了什麼。
另一個例子是Python本身。您知道如何使用它來構建功能性軟體,即使您不瞭解Python的內部工作原理,您也可以做到。
將相同的應用於程式碼允許您收集問題中的所有物件並將 標準功能抽象到類中。
2.繼承
繼承允許我們從一個已經定義的類中定義多個子類。
它的主要目的是遵循DRY原則。通過將所有共享元件實現到超類中,您將能夠重用大量程式碼。
您可以將其視為現實生活中的基因遺傳概念。Children(子類)是兩個parents(父)之間的繼承的結果。它們繼承了所有的物理特性(屬性)和一些常見的行為(方法)。
3. 多型性
多型允許我們稍微修改之前在超類中定義的子類的方法和屬性。
字面意思是“多種形式” 。那是因為我們構建了名稱相同但功能不同的方法。
回到之前的想法,孩子也是多型的一個完美例子。它們可以繼承定義的行為get_hungry()但方式略有不同,例如,每4小時而不是每6小時餓一次。
4.封裝
封裝是我們保護類中資料內部完整性的過程。
儘管Python中沒有private語句,但您可以通過在Python中使用mangling來應用封裝。有一些名為getter和setter的特殊方法允許我們訪問獨特的屬性和方法。
讓我們想象一個Human類,它有一個名為_height的唯一屬性。您只能在某些約束內修改此屬性(幾乎不可能高於3米)。
構建區域形狀解析器計算器
Python最好的事情之一是它讓我們可以建立各種各樣的軟體,從CLI(命令列介面)程式到複雜的Web應用程式。
現在您已經瞭解了OOP的支柱概念,是時候將它們應用到實際專案中了。
注意:以下所有程式碼都將在此GitHub儲存庫中可用。一種程式碼修訂工具,可幫助我們使用Git管理程式碼版本。
您的任務是建立以下形狀的面積計算器:
- 正方形
- 長方形
- 三角形
- 圓圈
- 六邊形
Shape Base類
首先,建立一個檔案calculator.py並開啟它。因為我們已經有了要使用的物件,所以很容易在一個類中抽象它們。
您可以分析它們的共同特徵並發現所有這些都是二維形狀。因此,最好的選擇是使用get_area()方法建立一個類Shape ,每個形狀都將從該方法繼承。
注意:所有的方法都應該是動詞。這是因為此方法名為get_area()而不是area() 。
class Shape: def __init__(self): pass def get_area(self): pass
上面的程式碼定義了類;然而,其中還沒有任何有趣的東西。
讓我們實現大多數這些形狀的標準功能。
class Shape: def __init__(self, side1, side2): self.side1 = side1 self.side2 = side2 def get_area(self): return self.side1 * self.side2 def __str__(self): return f'The area of this {self.__class__.__name__} is: {self.get_area()}'
讓我們分解一下我們正在用這段程式碼做什麼:
- 在__init__方法中,我們要求兩個引數,SIDE1和side2。這些將保留為例項屬性。
- get_area()函式返回形狀的面積。在這種情況下,它使用矩形的面積公式,因為使用其他形狀更容易實現。
- __str__() 和__init__()一樣是一種“神奇的方法”。它允許您修改例項的列印方式。
- self.__class__.__name__隱藏屬性指的是類的名稱。如果您使用的是Triangle類,則該屬性將為“Triangle”。
Rectangle類
由於我們實現了 Rectangle 的面積公式,我們可以建立一個簡單的Rectangle類,它只繼承Shape類。
要在Python中應用繼承,您將像往常一樣建立一個類,並用括號將要繼承的超類括起來。
# Folded base class class Shape: ... class Rectangle(Shape): # Superclass in Parenthesis pass
Square類
我們可以對Square類採取一種很好的多型方法。
請記住,正方形只是四個邊都相等的矩形。這意味著我們可以使用相同的公式來獲得面積。
我們可以通過修改init方法來做到這一點,只接受一個邊作為引數,並將該邊的值傳遞給Rectangle類的建構函式。
# Folded classes class Shape: ... class Rectangle(Shape): ... class Square(Rectangle): def __init__(self, side): super().__init__(side, side)
如您所見, super 函式將side引數傳遞給 superclass兩次。換句話說,這既是傳遞側SIDE1和側面2先前定義的建構函式。
Triangle類
三角形的大小是它周圍的矩形的一半。
三角形和矩形之間的關係(圖片來源:Varsity Tutors)
因此,我們可以繼承Rectangle類,修改get_area方法來匹配三角形面積公式,即底乘以高的二分之一。
# Folded classes class Shape: ... class Rectangle(Shape): ... class Square(Rectangle): ... class Triangle(Rectangle): def __init__(self, base, height): super().__init__(base, height) def get_area(self): area = super().get_area() return area / 2
super()函式的另一個用例是呼叫超類中定義的方法並將結果儲存為變數。這就是 get_area()方法內部發生的事情。
Circle類
您可以使用公式πr²找到圓面積,其中r是圓的半徑。這意味著我們必須修改get_area()方法來實現該公式。
注意:我們可以從math模組中匯入π的近似值
# Folded classes class Shape: ... class Rectangle(Shape): ... class Square(Rectangle): ... class Triangle(Rectangle): … # At the start of the file from math import pi class Circle(Shape): def __init__(self, radius): self.radius = radius def get_area(self): return pi * (self.radius ** 2)
上面的程式碼定義了Circle類,它使用不同的建構函式和get_area()方法。儘管Circle繼承自Shape類,但您可以重新定義每個方法並根據自己的喜好將其歸因。
Regular Hexagon類
我們只需要正六邊形的邊長來計算它的面積。它類似於Square類,我們只向建構函式傳遞一個引數。
六邊形面積公式(圖片來源:BYJU’S)
但是,公式完全不同,它意味著使用平方根。這就是您將使用math模組中的sqrt()函式的原因。
# Folded classes class Shape: ... class Rectangle(Shape): ... class Square(Rectangle): ... class Triangle(Rectangle): … class Circle(Shape): … # Import square root from math import sqrt class Hexagon(Rectangle): def get_area(self): return (3 * sqrt(3) * self.side1 ** 2) / 2
測試我們的類
您可以在使用偵錯程式執行Python檔案時進入互動模式。最簡單的方法是使用內建的斷點函式。
注意:此函式僅在Python 3.7或更高版本中可用。
from math import pi, sqrt # Folded classes class Shape: ... class Rectangle(Shape): ... class Square(Rectangle): ... class Triangle(Rectangle): … class Circle(Shape): … class Hexagon(Rectangle): … breakpoint()
現在,執行Python檔案並使用您建立的類。
$ python calculator.py (Pdb) rec = Rectangle(1, 2)(Pdb) print(rec) The area of this Rectangle is: 2 (Pdb) sqr = Square(4) (Pdb) print(sqr) The area of this Square is: 16 (Pdb) tri = Triangle(2, 3) (Pdb) print(tri) The area of this Triangle is: 3.0 (Pdb) cir = Circle(4) (Pdb) print(cir) The area of this Circle is: 50.26548245743669 (Pdb) hex = Hexagon(3) (Pdb) print(hex) The area of this Hexagon is: 23.382685902179844
挑戰
使用run方法建立一個類,使用者可以在其中選擇形狀並計算其面積。完成挑戰後,您可以向GitHub儲存庫傳送拉取請求或在評論部分發布您的解決方案。
小結
物件導向程式設計是一種正規化,我們通過將問題視為物件來解決問題。如果您瞭解Python OOP,您還可以輕鬆地將其應用於Java、PHP、Javascript和C#等語言。
在本文中,您已瞭解:
- Python中物件導向的概念
- 物件導向相對於結構化程式設計的優勢
- Python物件導向程式設計的基礎知識
- 類的概念以及如何在Python中使用它們
- Python中類的建構函式
- Python中的方法和屬性
- OOP的四大支柱
- 在專案中實現抽象、繼承和多型
評論留言