Python物件導向程式設計 (OOP) 初學者指南

Python物件導向程式設計 (OOP) 初學者指南

程式設計是一門藝術。和藝術一樣,選擇合適的畫筆和顏料對於創作出最好的作品至關重要。 Python物件導向程式設計就是這樣一種技能。

選擇正確的程式語言是任何專案的關鍵部分,它可以導致流暢和愉快的開發或徹底的噩夢。因此,最好為您的用例使用最適合的語言。

這是在Python中學習物件導向程式設計的主要原因,Python也是最流行的程式語言之一。讓我們來學習!

  1. 一個示例Python程式
  2. 學習Python OOP的要求
  3. 什麼是Python中的物件導向程式設計?
  4. 為什麼我們在Python中使用物件導向程式設計?
  5. Python中的一切都是物件
  6. 你在Python中的第一個物件
  7. Python中OOP的4個支柱
  8. 構建區域形狀解析器計算器

一個示例Python程式

在深入探討這個問題之前,讓我們提出一個問題:你有沒有寫過像下面這樣的Python程式?

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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語句處理。
  • 迴圈:重複執行程式碼塊。它可以是forwhile迴圈。
  • 功能:有組織和可重用的程式碼塊。您可以使用關鍵字def建立它們。
  • 引數:傳遞給函式的物件。例如: sum([1, 2, 4])
  • 執行Python指令碼:開啟終端或命令列並輸入“python <檔名>”。
  • 開啟Python指令碼:開啟終端並鍵入pythonpython3(取決於您的系統)。

現在您已經清楚地瞭解了這些概念,您可以繼續理解物件導向程式設計。

什麼是Python中的物件導向程式設計?

物件導向程式設計 (OOP) 是一種程式設計正規化,我們可以在其中將複雜問題視為物件。

正規化是為解決問題提供基礎的理論。

因此,當我們談論OOP時,我們指的是一組用於解決物件問題的概念和模式。

Python中的物件是資料(屬性)和行為(方法)的單一集合。您可以將物體視為您周圍真實的事物。例如,考慮計算器:

Python物件導向程式設計 (OOP) 初學者指南-2

計算器可以是一個物件

您可能會注意到,資料(屬性)始終是名詞,而行為(方法)始終是動詞。

這種劃分是物件導向程式設計的核心概念。您構建儲存資料幷包含特定型別功能的物件。

為什麼我們在Python中使用物件導向程式設計?

OOP允許您建立安全可靠的軟體。許多Python框架和庫使用這種正規化來構建它們的程式碼庫。一些示例是Django、Kivy、pandas、NumPy和TensorFlow。

讓我們看看在Python中使用OOP的主要優勢。

Python OOP的優點

以下原因將使您選擇在Python中使用物件導向程式設計。

所有現代程式語言都使用OOP

這種正規化與語言無關。如果您在Python中學習了OOP,您將能夠在以下方面使用它:

所有這些語言要麼是本機物件導向的,要麼包含物件導向功能的選項。如果你想在Python之後學習它們中的任何一個,它會更容易——你會發現處理物件的語言之間有許多相似之處。

OOP使您可以更快地編碼

更快的編碼並不意味著編寫更少的程式碼行。這意味著您可以在更短的時間內實現更多功能,而不會影響專案的穩定性。

物件導向程式設計允許您通過實現抽象來重用程式碼。這一原則使您的程式碼更加簡潔易讀。

您可能知道,程式設計師花在閱讀程式碼上的時間比編寫程式碼的時間多得多。這就是易讀性始終比儘快推出功能更重要的原因。

程式碼不清晰導致生產力下降

程式碼不清晰導致生產力下降

稍後您將看到有關抽象原則的更多資訊。

OOP幫助您避免義大利麵條式程式碼

還記得本文開頭的猜數程式嗎?

如果您繼續新增功能,將來您將擁有許多巢狀的if語句。這種無休止的程式碼行纏結稱為義大利麵條式程式碼,您應該儘可能避免使用它。

OOP為我們提供了壓縮物件中所有邏輯的可能性,因此避免了巢狀的長段if

OOP改善您對任何情況的分析

一旦您獲得了一些OOP的經驗,您就能夠將問題視為小而具體的物件。

這種理解導致快速的專案初始化。

結構化程式設計與物件導向程式設計

結構化程式設計是初學者最常用的正規化,因為它是構建小程式的最簡單方法。

它涉及按順序執行Python程式。這意味著你給計算機一個任務列表,然後從上到下執行它們。

讓我們看一個帶有咖啡店程式的結構化程式設計示例。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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)
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)

上面的程式碼充當咖啡店供應商。它會詢問你的預算,然後“賣”給你你能買到的最大的咖啡。

嘗試在終端中執行它。它將根據您的輸入逐步執行。

這段程式碼執行良好,但我們有三個問題:

  1. 它有很多重複的邏輯。
  2. 它使用許多巢狀的if條件。
  3. 閱讀和修改會很困難。

OOP的發明是為了解決所有這些問題。

讓我們看看上面用OOP實現的程式。如果您還不明白,請不要擔心。它僅用於比較結構化程式設計和麵向物件程式設計。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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')
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')
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”——它們都在方法中使用。主要方法是“銷售”,它處理完成銷售過程所需的所有邏輯。

如果您嘗試執行該類,您將不會得到任何輸出。這主要是因為我們只是為咖啡宣告瞭“模板”,而不是咖啡本身。

讓我們使用以下程式碼實現該類:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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)
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終端中做一個簡單的練習。您可以通過在終端中pythonpython3

Python終端

Python終端

現在,讓我們使用Python終端來發現方法和型別。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
>>> wbolt = 'WBOLT, Premium WordPress Themes and Plugins'
>>> wbolt.upper()
'WBOLT, PREMIUM WORDPRESS THEMES AND PLUGINS'
>>> wbolt = 'WBOLT, Premium WordPress Themes and Plugins' >>> wbolt.upper() 'WBOLT, PREMIUM WORDPRESS THEMES AND PLUGINS'
>>> wbolt = 'WBOLT, Premium WordPress Themes and Plugins'
>>> wbolt.upper()
'WBOLT, PREMIUM WORDPRESS THEMES AND PLUGINS'

在第二行中,我們呼叫了一個字串方法upper() 。它以大寫形式返回字串的內容。但是,它不會更改原始變數。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
>>> wbolt
'Wbolt, Premium WordPress Themes and Plugins'
>>> wbolt 'Wbolt, Premium WordPress Themes and Plugins'
>>> wbolt
'Wbolt, Premium WordPress Themes and Plugins'

在處理物件時,讓我們深入研究有價值的功能。 type()函式允許您獲取物件的型別。 “型別”是物件所屬的類。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
>>> type(wbolt)
# class 'str'
>>> type(wbolt) # class 'str'
>>> type(wbolt)
# class 'str'

dir()函式返回物件的所有屬性和方法。讓我們用wbolt變數來測試一下。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
>>> dir(wbolt)
['__add__', '__class__', ........... 'upper', 'zfill']
>>> dir(wbolt) ['__add__', '__class__', ........... 'upper', 'zfill']
>>> dir(wbolt)
['__add__', '__class__',  ........... 'upper', 'zfill']

現在,嘗試列印此物件的一些隱藏屬性。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
>>> wbolt.__class__ # class ‘str’ e>
>>> wbolt.__class__ # class ‘str’ e>
 >>> wbolt.__class__ # class ‘str’ e>

這將輸出物件wbolt所屬的類。所以我們可以說type函式唯一返回的是物件的__class__屬性。

您可以試驗所有資料型別,直接在終端上發現它們的所有屬性和方法。您可以在官方文件中瞭解有關內建資料型別的更多資訊。

你在Python中的第一個物件

一個就像一個模板它允許您根據您定義的屬性和方法建立自定義物件。

你可以把它想象成一個餅乾切割器,你可以修改它來烘焙完美的餅乾(物件,而不是跟蹤餅乾),具有定義的特徵:形狀、大小等。

另一方面,我們有例項。例項是類的單個物件,它具有唯一的記憶體地址。

Python中的例項

Python中的例項

現在您知道什麼是類和例項,讓我們定義一些!

要在Python中定義類,請使用class關鍵字,後跟其名稱。在本例中,您將建立一個名為Cookie的類。

注意:在Python中,我們使用駝峰命名約定來命名類。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Cookie:
pass
class Cookie: pass
class Cookie:
	pass

開啟你的Python終端並輸入上面的程式碼。要建立類的例項,只需在其後鍵入其名稱和括號。這與呼叫函式的過程相同。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cookie1 = Cookie()
cookie1 = Cookie()
cookie1 = Cookie()

恭喜——你剛剛用Python建立了你的第一個物件!您可以使用以下程式碼檢查其 id 和型別:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
id(cookie1)
140130610977040 # Unique identifier of the object
type(cookie1)
<class '__main__.Cookie'>
id(cookie1) 140130610977040 # Unique identifier of the object type(cookie1) <class '__main__.Cookie'>
id(cookie1)
140130610977040 # Unique identifier of the object

type(cookie1)
<class '__main__.Cookie'>

可以看到,這個cookie在記憶體中有一個唯一的識別符號,它的型別是Cookie

您還可以使用isinstance()函式檢查物件是否是類的例項。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
isinstance(cookie1, Cookie)
# True
isinstance(cookie1, int)
# False
isinstance('a string', Cookie)
# False
isinstance(cookie1, Cookie) # True isinstance(cookie1, int) # False isinstance('a string', Cookie) # False
isinstance(cookie1, Cookie)
# True
isinstance(cookie1, int)
# False
isinstance('a string', Cookie)
# False

構造方法

__init__()方法也稱為“建構函式”。每次我們例項化一個物件時,它都被稱為Python。

建構函式使用它需要存在的最小引數集建立物件的初始狀態。讓我們修改Cookie類,使其在其建構函式中接受引數。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Cookie:
# Constructor
def __init__(self, name, shape, chips='Chocolate'):
# Instance attributes
self.name = name
self.shape = shape
self.chips = chips
class Cookie: # Constructor def __init__(self, name, shape, chips='Chocolate'): # Instance attributes self.name = name self.shape = shape self.chips = chips
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的例項。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cookie2 = Cookie()
# TypeError
cookie2 = Cookie() # TypeError
cookie2 = Cookie()
# TypeError

你會得到一個錯誤。那是因為您必須提供物件生存所需的最少資料集——在本例中,nameshape因為我們已經將chips設定為“Chocolate”。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cookie2 = Cookie('Awesome cookie', 'Star')
cookie2 = Cookie('Awesome cookie', 'Star')
cookie2 = Cookie('Awesome cookie', 'Star')

要訪問例項的屬性,您必須使用點表示法。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cookie2.name
# 'Awesome cookie'
cookie2.shape
# 'Star'
cookie2.chips
# 'Chocolate'
cookie2.name # 'Awesome cookie' cookie2.shape # 'Star' cookie2.chips # 'Chocolate'
cookie2.name
# 'Awesome cookie'
cookie2.shape
# 'Star'
cookie2.chips
# 'Chocolate'

目前, Cookie類沒有任何內容。讓我們新增一個示例方法bake()以使事情變得更有趣。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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!')
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!')
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!')

要呼叫方法,請使用點表示法並將其作為函式呼叫。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
cookie3 = Cookie('Baked cookie', 'Tree')
cookie3.bake()
# This Baked cookie, is being baked with the shape Tree and chips of Chocolate
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!
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來應用封裝。有一些名為gettersetter的特殊方法允許我們訪問獨特的屬性和方法。

讓我們想象一個Human類,它有一個名為_height的唯一屬性。您只能在某些約束內修改此屬性(幾乎不可能高於3米)。

構建區域形狀解析器計算器

Python最好的事情之一是它讓我們可以建立各種各樣的軟體,從CLI(命令列介面)程式到複雜的Web應用程式。

現在您已經瞭解了OOP的支柱概念,是時候將它們應用到實際專案中了。

注意:以下所有程式碼都將在此GitHub儲存庫中可用。一種程式碼修訂工具,可幫助我們使用Git管理程式碼版本。

您的任務是建立以下形狀的面積計算器:

  • 正方形
  • 長方形
  • 三角形
  • 圓圈
  • 六邊形

Shape Base類

首先,建立一個檔案calculator.py並開啟它。因為我們已經有了要使用的物件,所以很容易在一個類中抽象它們

您可以分析它們的共同特徵並發現所有這些都是二維形狀。因此,最好的選擇是使用get_area()方法建立一個類Shape ,每個形狀都將從該方法繼承。

注意:所有的方法都應該是動詞。這是因為此方法名為get_area()而不是area()

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Shape:
def __init__(self):
pass
def get_area(self):
pass
class Shape: def __init__(self): pass def get_area(self): pass
class Shape:
	def __init__(self):
		pass

	def get_area(self):
		pass

上面的程式碼定義了類;然而,其中還沒有任何有趣的東西。

讓我們實現大多數這些形狀的標準功能。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()}'
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()}'
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__方法中,我們要求兩個引數,SIDE1side2這些將保留為例項屬性
  • get_area()函式返回形狀的面積。在這種情況下,它使用矩形的面積公式,因為使用其他形狀更容易實現。
  • __str__() 和__init__()一樣是一種“神奇的方法”。它允許您修改例項的列印方式。
  • self.__class__.__name__隱藏屬性指的是類的名稱。如果您使用的是Triangle類,則該屬性將為“Triangle”。

Rectangle類

由於我們實現了 Rectangle 的面積公式,我們可以建立一個簡單的Rectangle類,它只繼承Shape類。

要在Python中應用繼承,您將像往常一樣建立一個類,並用括號將要繼承的超類括起來。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Folded base class
class Shape: ...
class Rectangle(Shape): # Superclass in Parenthesis
pass
# Folded base class class Shape: ... class Rectangle(Shape): # Superclass in Parenthesis pass
# Folded base class
class Shape: ...
 
class Rectangle(Shape): # Superclass in Parenthesis
	pass

Square類

我們可以對Square類採取一種很好的多型方法。

請記住,正方形只是四個邊都相等的矩形。這意味著我們可以使用相同的公式來獲得面積。

我們可以通過修改init方法來做到這一點,只接受一個作為引數,並將該邊的值傳遞給Rectangle類的建構函式。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Folded classes
class Shape: ...
class Rectangle(Shape): ...
class Square(Rectangle):
def __init__(self, side):
super().__init__(side, side)
# Folded classes class Shape: ... class Rectangle(Shape): ... class Square(Rectangle): def __init__(self, side): super().__init__(side, side)
# 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)

三角形和矩形之間的關係(圖片來源:Varsity Tutors)

因此,我們可以繼承Rectangle類,修改get_area方法來匹配三角形面積公式,即底乘以高的二分之一。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# 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
# 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
# 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模組中匯入π的近似值

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# 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)
# 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)
# 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)

六邊形面積公式(圖片來源:BYJU’S)

但是,公式完全不同,它意味著使用平方根。這就是您將使用math模組中的sqrt()函式的原因

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# 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
# 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
# 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或更高版本中可用。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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()
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檔案並使用您建立的類。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$ 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
$ 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
$ 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的四大支柱
  • 在專案中實現抽象繼承多型

評論留言