在學習面向對象編程時,我決定深入研究它的歷史,結果發現它很迷人。術語「面向對象編程」(OOP)是Alan Kay 在1966年研究生院時提出的。
Simula語言是第一種具有面向對象編程特點的程式語言。它是在1967年開發的,用於製作模擬程序,其中最重要的信息稱為對象。
儘管OOPS自20世紀60年代初就在市場上,但在20世紀90年代,OOPS開始因為C++而增長。
除此之外,這種編程技術已經被包括Python在內的各種程式語言所採用,它的應用幾乎遍及所有領域,如實時系統、人工智慧、專家系統、CS系統、面向對象資料庫等等。
因此,在本文中,我將解釋Python中面向對象編程的基本概念。
目錄
什麼是面向對象編程?
面向對象編程(OOP)就是創建「對象」。對象是一組相互關聯的變量和函數。這些變量通常稱為對象的屬性,函數稱為對象的行為。這些對象為程序提供了一個更好、更清晰的結構。
例如,汽車可以是一個對象。如果我們把汽車當作一個對象,那麼它的屬性就是——顏色、型號、價格、品牌等等,它的行為/函數是加速、減速、換擋。
另一個例子——如果我們把狗當作一個物體,那麼它的屬性就是——它的顏色、品種、名字、體重等,而它的行為/函數是行走、吠叫、玩耍等。
面向對象編程因其在編程中實現了對象、隱藏、繼承等現實實體而聞名。它使可視化更容易,因為它接近真實世界的場景。
面向對象編程(OOP)與面向過程編程(POP)
OOP和過程式編程的基本區別是
OOPs的主要概念
在本節中,我們將深入探討OOP的基本概念。我們將討論以下主題
1.什麼是類?
這個問題的直接答案是:類是對象的集合。與原始數據結構不同,類是用戶定義的數據結構。它們使代碼更易於管理。
讓我們看看如何定義下面的類
class class_name:class body
我們用關鍵字「class」在類名和分號後面定義一個類。在使用縮進之後,我們會考慮有縮進的所有東西。為了讓這更容易理解,讓我們看一個例子。
以汽車展廳為例。你要存儲每輛車的詳細信息。讓我們首先定義一個類
class Car:pass
就這樣!
注意:我用pass語句代替了它的主體,因為主要目的是展示如何定義一個類,而不是它應該包含什麼。
在詳細介紹之前,首先要了解對象和實例化。
2.對象和對象實例化
當我們定義一個類時,只會創建對象的描述或藍圖,在這個過程中沒有內存分配。而對象實例具有內存分配,它包含真實的數據或信息。
實例化是創建一個類的實例。讓我們創建上面定義的類的對象-
obj1 = Car()
這樣就創建了一個對象實例。請注意,你可以根據你的選擇更改對象名稱。
嘗試列印此對象
print(obj1)
因為我們的類是空的,所以它返回存儲對象的地址,即0x7fc5e677b6d8
在繼續學習之前,你還需要了解類的構造函數。
類構造函數
到目前為止,我們有一輛空的車,是時候把車的特徵填滿我們的類了。類構造函數的工作是在創建類的對象時將值分配給類的數據成員。
一輛車可以有不同的屬性,如名稱、顏色、型號、品牌、發動機功率、重量、價格等。我們將僅選擇其中幾項以便於理解。
class Car:def __init__(self, name, color):
self.name = name
self.color = color
因此,car或任何其他對象的屬性必須位於一個我們稱為 **__init__( )**的方法中。此方法也稱為構造函數方法。每當類的對象被構造時,就調用構造函數方法。
現在,讓我們來討論一下 **__init__( )**方法的參數。所以,這個方法的第一個參數必須是self。然後才是剩下的參數
構造函數方法中的兩個語句是
這將創建新的屬性,即name和color,然後將參數的值分配給它們。「self」關鍵字表示類的實例。通過使用「self」關鍵字,我們可以訪問類的屬性和方法。它在方法定義和變量初始化中很有用。「self」在每次定義方法時都顯式使用。
注意:你也可以在此方法之外創建屬性。但是這些屬性對整個類都是通用的,必須給它們賦值。
假設你的展廳中的所有汽車都是Sedan,而不是一次又一次地指定它,你可以通過創建一個屬性來將car_type的值固定為Sedan。
class Car:
car_type = "Sedan" #類屬性def __init__(self, name, color):
self.name = name #實例屬性self.color = color #實例屬性
這裡,實例屬性是指構造函數方法中的屬性,在這個例子中是self.name 和 self.color。類屬性是指構造函數方法之外的屬性,在這個例子中是car_type。
3.類方法
到目前為止,我們已經添加了汽車的屬性。現在是時候添加一些行為了。方法是我們用來描述對象行為的函數。它們也在類中定義。請看下面的代碼
class Car:
car_type = "Sedan"
def __init__(self, name, mileage):
self.name = name
self.mileage = mileage
def description(self):
return f"The {self.name} car gives the mileage of {self.mileage}km/l"
def max_speed(self, speed):
return f"The {self.name} runs at the maximum speed of {speed}km/hr"
在類中定義的方法(而不是構造函數方法)稱為實例方法。此外,這裡有兩個實例方法,description()和max_speed()。
請注意,附加參數speed沒有使用「self」關鍵字。因為speed不是一個實例變量,所以我們不使用self關鍵字作為前綴。讓我們為上面描述的類創建一個對象。
obj2 = Car("Honda City",24.1)
print(obj2.description())
print(obj2.max_speed(150))
我們所做的是創建一個類car的對象並傳遞所需的參數。為了使用object_name.method_name().
description()方法沒有任何附加參數,因此在調用它時沒有傳遞任何參數。
max_speed()方法有一個附加參數,因此我們在調用它時傳遞了一個參數。
注意:三件重要的事要記住
讓我們一個個來看看
class Car:def __init__(self, name, mileage):
self.name = name
self.mileage = mileage
def max_speed(self, speed):
return f"The {self.name} runs at the maximum speed of {speed}km/hr"
Honda = Car("Honda City",21.4)
print(Honda.max_speed(150))
Skoda = Car("Skoda Octavia",13)
print(Skoda.max_speed(210))
class Car:
def __init__(self, name, mileage):
self.name = name
self.mileage = mileage
Honda = Car("Honda City")
print(Honda)
因為我們沒有提供第二個參數,所以我們得到了這個錯誤。
class Car:
def __init__(self, name, mileage):
self.name = name
self.mileage = mileage
def description(self):
return f"The {self.name} car gives the mileage of {self.mileage}km/l"
Honda = Car(24.1,"Honda City")
print(Honda.description())
搞混了!因為我們改變了論點的順序。
現在,面向對象編程有四個基本概念:繼承、封裝、多態和數據抽象。為了理解OOPs,了解所有這些非常重要。到目前為止,我們已經介紹了面向對象編程的基本知識,讓我們進一步深入。
4. Python類中的繼承
繼承是一個類繼承另一個類的屬性和方法的過程。被繼承的類稱為父類。從父類繼承屬性的類是子類。
有趣的是,除了繼承的屬性和方法外,子類可以有自己的屬性和方法。
如何繼承父類?使用以下語法:
class parent_class:
body of parent class
class child_class( parent_class):
body of child class
讓我們看看實現
class Car: #父類
def __init__(self, name, mileage):
self.name = name
self.mileage = mileage
def description(self):
return f"The {self.name} car gives the mileage of {self.mileage}km/l"
class BMW(Car): #子類pass
class Audi(Car): #子類def audi_desc(self):return "This is the description method of class Audi."
obj1 = BMW("BMW 7-series",39.53)
print(obj1.description())
obj2 = Audi("Audi A8 L",14)
print(obj2.description())
print(obj2.audi_desc())
我們創建了兩個子類,即「BMW」和「Audi」,它們繼承了父類「Car」的方法和屬性。在BMW類我們沒有提供額外的特徵和函數。而Audi內部的一個附加方法。
注意在obj1.description()和obj2.description()的幫助下,父類的實例方法description()是如何被子類的對象訪問的。此外,還可以使用obj2.Audi_desc()訪問Audi類的單獨方法。
5.封裝
正如我在文章開頭部分提到的,封裝是確保安全性的一種方法。基本上,它將數據隱藏起來,不讓外人訪問。例如,如果我們想讓某些值不被客戶或任何未經授權的人訪問,那麼封裝就是確保這一點的方法。
你可以在方法或屬性的名稱前使用一個下劃線( _ ) 來聲明受保護的方法或屬性。例如 self._name或def _method();這兩行都說明屬性和方法是受保護的,不應在類和子類的訪問之外使用,而是可以由類方法和對象訪問。
Python只是將『_『 用作編碼約定,告訴你應該在類的範圍內使用這些屬性/方法。你仍然可以像往常一樣訪問定義為受保護的變量和方法。
現在,為了實際防止從類範圍之外訪問屬性/方法,可以使用「私有成員」。若要將屬性/方法聲明為私有成員,請在前綴中使用雙下劃線( __ ) 。例如– self.__name或def __method();這兩行都說明屬性和方法是私有的,不能從類外部進行訪問。
class car:
def __init__(self, name, mileage):
self._name = name #受保護屬性self.mileage = mileage
def description(self):
return f"The {self._name} car gives the mileage of {self.mileage}km/l"
obj = car("BMW 7-series",39.53)
#通過類方法訪問受保護變量
print(obj.description())
#直接從外部訪問受保護變量
print(obj._name)
print(obj.mileage)
請注意我們如何訪問受保護的變量而沒有任何錯誤。很明顯,對變量的訪問仍然是公開的。讓我們看看封裝是如何工作的
class Car:
def __init__(self, name, mileage):
self.__name = name #私有變量self.mileage = mileage
def description(self):
return f"The {self.__name} car gives the mileage of {self.mileage}km/l"
obj = Car("BMW 7-series",39.53)
#通過類方法訪問私有變量
print(obj.description())
#直接從外部訪問私有變量
print(obj.mileage)
print(obj.__name)
當我們嘗試使用description()方法訪問私有變量時,沒有遇到任何錯誤。但是當我們嘗試直接在類外部訪問私有變量時,Python給出了一個錯誤,聲明:car對象沒有屬性''__name'。
你仍然可以使用該屬性的修飾名稱直接訪問它。Name mangling(命名修飾)是一種從外部訪問類成員的機制。Python解釋器將任何帶有「__var」的標識符重寫為「_ClassName__var」。使用這個你可以從外部訪問類成員。
class Car:
def __init__(self, name, mileage):
self.__name = name #私有變量 self.mileage = mileage
def description(self):
return f"The {self.__name} car gives the mileage of {self.mileage}km/l"
obj = Car("BMW 7-series",39.53)
#通過類方法訪問私有變量
print(obj.description())
#直接從外部訪問私有變量
print(obj.mileage)
print(obj._car__name) #名稱修飾
請注意,這個規則的設計是想處理某些情況,例如在調試器中。但是正常編程中不推薦使用
6.多態
多態(Polymorphism)一個希臘單詞。如果我們分解Polymorphism這一術語,我們會得到「poly」(多)和「morph」(變形)形式。所以多態性意味著有多種形式。在OOP中,它指的是具有相同名稱但具有不同功能的函數。
class Audi:def description(self):
print("This the description function of class AUDI.")
class BMW:def description(self):
print("This the description function of class BMW.")
audi = Audi()
bmw = BMW()
for car in (audi,bmw):
car.description()
使用對象audi調用函數時,將調用audi類的函數;使用對象bmw調用函數時,將調用bmw類的函數。
7.數據抽象
我們使用抽象來隱藏函數的內部細節或實現,只顯示其功能。這與你在不知道汽車原理的情況下知道如何駕駛汽車,或者你知道如何用開關打開或關閉燈,但你不知道插座後面發生了什麼的情況類似。
任何具有至少一個抽象函數的類都是抽象類。為了首先創建一個抽象類,你需要從ABC模塊導入ABC類。這使你可以在其中創建抽象方法。ABC代表抽象基類。
from abc import ABC
class abs_class(ABC):
Body of the class
重要的是,不能為抽象類創建實例對象。例如-
from abc import ABC, abstractmethod
class Car(ABC):def __init__(self,name):
self.name = name
@abstractmethoddef price(self,x):pass
obj = Car("Honda City")
現在的問題是我們如何準確地使用這個抽象。答案是使用繼承。
from abc import ABC, abstractmethod
class Car(ABC):def __init__(self,name):
self.name = name
def description(self):
print("This the description function of class car.")
@abstractmethoddef price(self,x):pass
class new(Car):def price(self,x):
print(f"The {self.name}'s price is {x} lakhs.")
obj = new("Honda City")
obj.description()
obj.price(25)
Car是從ABC模塊的ABC類繼承的抽象類。注意我在抽象類中有一個抽象方法(price())和一個具體方法(description())。這是因為抽象類可以同時包含這兩種類型的函數,而普通類則不能。從這個抽象類繼承的另一個類是new。new的price()方法是我們重寫了抽象類Car的price方法。
在用戶從new()類創建對象並調用price()方法之後,new()類中price方法的定義就開始發揮作用了。這些定義對用戶是隱藏的。抽象方法只是提供一個聲明。需要提供子類的定義。
但是,當為new()類(即obj)的對象調用description()方法時,將調用Car的description()方法,因為它不是抽象方法。