面向對象與面向過程?
面向過程編程的基本思想是:分析解決問題的步驟,使用函數實現每步對應的功能,按照步驟的先后順序依次調用函數。面向過程只考慮如何解決當前問題,它著眼于問題本身。
(資料圖片僅供參考)
面向對象編程的基本思想是:著眼于角色以及角色之間的聯系。使用面向對象編程思想解決問題時,開發人員首先會從問題之中提煉出問題涉及的角色,將不同角色各自的特征和關系進行封裝,以角色為主體,為不同角度定義不同的屬性和方法,以描述角色各自的屬性與行為。
1.對象
對象是一個程序模塊,從用戶來看,對象為他們提供所希望的行為。對象既可以是具體的物理實體的事物,也可以是人為的概念,如一名員工、一家公司、一輛汽車、一個故事等。
2.類
在面向對象的方法中,類是具有相同屬性和行為的一組對象的集合,它提供了一個抽象的描述,其內部包括屬性和方法兩個主要部分。
3.抽象
抽象是抽取特定實例的共同特征,形成概念的過程。抽象主要是為了使復雜度降低,它強調主要特征,忽略次要特征,以得到較簡單的概念,從而讓人們能控制其過程或以綜合的角度來了解許多特定的事態。
4.封裝
封裝是面向對象的核心思想,將對象的屬性和行為封裝起來,不需要讓外界知道具體實現細節,避免了外界直接訪問對象屬性而造成耦合度過高及過度依賴,同時也阻止了外界對對象內部數據的修改而可能引發的不可預知錯誤。
5.繼承
繼承不僅增強了代碼復用性,提高了開發效率,也為程序的擴充提供了便利。在軟件開發中,類的繼承性使所建立的軟件具有開放性、可擴充性,這是數據組織和分類行之有效的方法,它降低了創建對象、類的工作量。
6.多態
多態指同一屬性或行為在父類及其各派生類中具有不同的語義,面向對象的多態特性使開發更科學、更符合人類的思維習慣,能有效地提高軟件開發效率,縮短開發周期,提高軟件的可靠性。
類是對多個對象共同特征的抽象描述,是對象的模板;對象用于描述現實中的個體,它是類的實例。
類的定義格式如下:
class 類名: # 使用class定義類 類名首字母一般為大寫屬性名 = 屬性值 # 定義屬性def 方法名(self,形參1,形參2,...,形參N): # 定義方法 方法中有一個指向對象的默認參數self方法體
可以看到,在方法定義的參數中,有一個:self關鍵字。self關鍵字是成員方法定義的時候,必須填寫的。
類定義完成后不能直接使用,程序中的類需要實例化為對象才能實現其意義。
對象的創建格式
對象名=類名()
訪問對象成員
對象名.屬性 # 訪問對象屬性對象名.方法() # 訪問對象方法
類中定義的屬性和方法默認為公有屬性和方法,該類的對象可以任意訪問類的公有成員。為了契合封裝原則,python支持將類中的成員設置為私有成員,在一定程度上限制對象對類成員的訪問。
定義私有成員
python通過在類成員名之前添加雙下畫線(__)來限制成員的訪問權限,語法格式如下:
__屬性名__方法名
class PersonInfo:__weight = 55 # 私有屬性def __info(self): # 私有方法print(f"我的體重是:{__weight}")
私有成員的訪問
對象無法直接訪問類的私有成員。下面演示如何在類內部訪問私有屬性和私有方法。
訪問私有屬性。私有屬性可在公有方法中通過指代類本身的默認參數self訪問,類外部可通過公有方法間接獲取類的私有屬性。
class PersonInfo: __weight = 65 # 私有屬性 def get_weight(self): print(f"體重:{self.__weight}kg") # 體重:65kgpersonInfo = PersonInfo()personInfo.get_weight()
訪問私有方法。私有方法同樣在公有方法中通過參數self訪問。
class PersonInfo: __weight = 65 # 私有屬性 def __info(self): # 私有方法 print(f"我的體重是:{self.__weight}") def get_weight(self): print(f"體重:{self.__weight}kg") self.__info()# 創建PersonInfo類的對象person,訪問公有方法get_weight()personInfo = PersonInfo() # 體重:65kgpersonInfo.get_weight() # 我的體重是:65
類中有兩個特殊的方法:構造方法_init_()和析構方法__del()__。這兩個方法分別在類創建和銷毀的時候自動調用。
每個類都有一個默認的_init_()方法,如果在定義類時顯示地定義了_init()方法,則創建對象時python解釋器會調用顯式定義的_init_()方法;如果定義類時沒有顯式定義_init()方法,那么Python解釋器會調用默認的_init()方法。
class Information(object): def __init__(self, name, sex): # 有參構造方法 self.name = name # 添加屬性name self.sex = sex # 添加屬性sex def info(self): print(f"姓名:{self.name}") # 姓名:齊納 print(f"性別:{self.sex}") # 性別:男information = Information("齊納", "男")information.info()
注意:前面在類中定義的屬性是類屬性,可以通過對象或類進行訪問;在構造方法中定義的屬性是實例屬性,只能通過對象進行訪問。
在創建對象時,系統會自動調用_init_()方法,在對象被清理時,系統也會自動調用一個_del_()方法,這個方法就是類的析構方法。
擴展:python的垃圾回收機制。Pyhton中的垃圾回收主要采用的是引用計數。引用計數是一種內存管理技術,他通過引用計數器記錄所有對象的引用數量,當對象的引用計數器數值為0時,就會將該對象視為垃圾進行回收。
import sys# getrefcount()函數是sys模塊中用于統計對象引用數量的函數,其返回結果通常比預期結果大1,這是因為getrefcount()函數也會統計臨時對象的引用class Destruction: def __init__(self): print("對象被創建") def __del__(self): print("對象被銷毀")# 調用getrefcount()函數返回Destruction類的對象的引用計數器的值。destruction = Destruction()print(sys.getrefcount(destruction)) # 2
_str_()
# __str__字符串方法class Student: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f"Student類對象,name={self.name},age={self.age}"student = Student("趙無極", 10)# print(student) # <__main__.Student object at 0x0000017F57579390># print(str(student)) # <__main__.Student object at 0x0000017F57579390>print(student) # Student類對象,name=趙無極,age=10print(str(student)) # Student類對象,name=趙無極,age=10# 當類對象需要被轉換為字符串之時,會輸出如上結果(內存地址)# 我們可以通過__str__方法,控制類轉換為字符串的行為。
_lt_ 小于符號比較方法
class Student: def __init__(self, name, age): self.name = name self.age = age def __lt__(self, other): return self.age < other.agestu1 = Student("周杰倫", 41)stu2 = Student("林俊杰", 42)# 直接對2個對象進行比較是不可以的,但是在類中實現__lt__方法,即可同時完成:小于符號 和 大于符號 2種比較print(stu1 < stu2) # True print(stu1 > stu2) # False
_le_ 小于等于比較符號方法
__le__可用于:<=、>=兩種比較運算符上。
class Student: def __init__(self, name, age): self.name = name self.age = age def __le__(self, other): return self.age < other.agestu1 = Student("周杰倫", 41)stu2 = Student("林俊杰", 41)print(stu1 <= stu2) # Trueprint(stu1 >= stu2) # False
_eq_,比較運算符實現方法
不實現__eq__方法,對象之間可以比較,但是是比較內存地址,也即是:不同對象==比較一定是False結果。實現了__eq__方法,就可以按照自己的想法來決定2個對象是否相等了。
class Student: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): return self.age == other.agestudent1 = Student("周杰庫", 41)student2 = Student("張學友", 41)print(student2 == student1) # True
擴展:實例方法:直接定義、只比普通函數多一個self參數的方法是類最基本的方法,這種方法稱為實例方法,它只能通過類實例化的對象調用。
類方法與實例方法有以下不同:
定義類方法
語法格式如下:
類名.類方法對象名.類方法
class Test: @classmethod def use_class_method(cls): print(f"我是類方法")test = Test()Test.use_class_method() # 類名調用方法test.use_class_method() # 對象名調用方法
從輸出結果可以看出,使用類名或對象名均可調用類方法。
在實例方法中無法修改類屬性的值,但在類方法中可以修改類屬性的值。
class Apple(object): # 定義Apple類 count = 0 # 定義類屬性 def add_one(self): self.count = 1 # 對象方法 @classmethod def add_two(cls): cls.count = 2 # 類方法apple = Apple()apple.add_one()print(Apple.count) # 0print(apple.count) # 1Apple.add_two()print(Apple.count) # 2
從輸出結果可以看出,調用實例方法add_one()后訪問count的值為0,說明count的值沒有被修改;調用類方法add_two()后再次訪問count的值為2,說明類屬性count的值被修改成功。
思考
:通過"self.count=1"只是創建了一個與類屬性同名的實例屬性count并將其賦值為1,而非對類屬性進行重新賦值。
靜態方法與實例方法有以下不同:
class Example: num = 10 # 類屬性 @staticmethod # 定義靜態方法 def static_method(): print(f"類屬性的值為:{Example.num}") print("--靜態方法--")example = Example() # 創建對象example.static_method() # 對象調用Example.static_method() # 類調用
總結:類方法和靜態方法的區別
類方法和靜態方法最主要的區別在于類方法有一個cls參數,使用該參數可以在類方法中訪問類的成員;靜態方法沒有任何默認參數,它無法使用默認參數訪問類的成員。因此,靜態方法更適合與類無關的操作。
單繼承指的是子類只繼承一個父類,其語法格式如下:
class 子類(父類):
class Amphibian: name = "兩棲動物" def features(self): print("幼年用腮呼吸") print("成年用肺兼皮膚呼吸")class Frog(Amphibian): # Frog類繼承自Amphibian類 def attr(self): print(f"青蛙是{self.name}") print("我會呱呱叫")frog = Frog() # 創建類的實例化對象print(frog.name) # 訪問父類的屬性 兩棲動物frog.features() # 使用父類的方法 幼年用腮呼吸 成年用肺兼皮膚呼吸frog.attr() # 使用自身的方法 青蛙是兩棲動物 我會呱呱叫
從輸出結果可以看出,子類繼承父類之后,就擁有了父類繼承的屬性和方法,它既可以調用自己的方法,也可以調用從分類繼承的方法。
擴展:isinstance()函數與issubclass()函數
isinstance(o,t)函數用于檢查對象的類型,它有兩個參數,第一個參數是要判斷類型的對象(o),第二個參數是類型(t),如果o是t類型的對象,則函數返回True,否則返回False
print(isinstance(frog, Frog)) # True
函數issubclass(cls,classinfo)用于檢查類的繼承關系,它也有2個參數:第一個參數是要判斷的子類型(cls);第二個參數是要判斷的父類類型(classinfo)。如果cls類型是classinfo類型的子類,則函數返回True,否則返回False。
print(issubclass(Frog, Amphibian)) # True
多基礎指的是一個子類繼承多個父類,其語法格式如下:
class 子類(父類A, 父類B):
class English: def eng_konw(self): print("具備英語知識")class Math: def math_koow(self): print("具備數學知識")class Student(English, Math): def study(self): print("學生的任務是學習")s = Student()s.eng_konw() # 具備英語知識s.math_koow() # 具備數學知識s.study() # 學生的任務是學習
注意事項
:多個父類中,如果有同名的成員,那么默認以繼承順序(從左到右)為優先級。即:先繼承的保留,后繼承的被覆蓋
子類可以繼承父類的屬性和方法,若父類的方法不能滿足子類的要求,子類可以重寫父類的方法,以實現輔助的功能。
class Felines: def speciality(self): print("貓科動物特長是爬樹")class Cat(Felines): name = "貓" def speciality(self): print(f"{self.name}會抓老鼠") print(f"{self.name}會爬樹")cat = Cat()cat.speciality()
程序運行結果:
貓會抓老鼠貓會爬樹
如果子類重寫了父類的方法,但仍希望調用父類中的方法,可以通過super()函數調用父類的方法。
super()函數使用方法如下:
super().方法名()
class Felines: def speciality(self): print("貓科動物特長是爬樹")class Cat(Felines): name = "貓" def speciality(self): print(f"{self.name}會抓老鼠") print(f"{self.name}會爬樹") print("*" * 20) super().speciality()cat = Cat()cat.speciality()
程序運行結果如下:
貓會抓老鼠貓會爬樹********************貓科動物特長是爬樹
從輸出結果可以看出,通過super()函數可以訪問被重寫的父類方法。
在Python中,多態值在不考慮對象類型的情況下使用對象。Python中并不需要顯式指定對象的類型,只要對象具有預期的方法和表達式操作符,就可以使用對象。
pass關鍵字的作用是什么?
pass是占位語句,用來保證函數(方法)或類定義的完整性,表示無內容,空的意思。
class Animal(object): # 定義父類Animal def move(self): passclass Rabbit(Animal): # 定義子類Rabbit def move(self): print("兔子蹦蹦跳跳")class Snail(Animal): # 定義子類Snail def move(self): print("蝸牛緩慢爬行")def test(obj): obj.move()rabbit = Rabbit()test(rabbit) # 接受Rabbit對象snail = Snail()test(snail) # 接受Snail對象
程序運行結果
兔子蹦蹦跳跳蝸牛緩慢爬行
從運行結果看出,同一個函數會根據參數的類型去調用不同的方法,從而產生不同的結果。
Pyhton在3.5版本的時候引入了類型注解,以方便靜態類型檢查工具,IDE等第三方工具。
類型注解:在代碼中涉及數據交互的地方,提供數據類型的注解(顯示的說明)。
主要功能:
支持:
類型注解的語法
基礎語法:變量:類型
# 基礎數據類型注解var1: int = 10var2: float = 3.1515926var3: bool = Truevar_4: str = "boost"# 類對象類型注解class Student: passstu: Student = Student()# 基礎容器類型注解my_list: list = [1, 2, 4]my_tuple: tuple = (1, 2, 3)my_set: set = {1, 2, 4}my_dict: dict = {"name": "張三"}my_str: str = "李四"# 容器類型詳細注解""" 1. 元組類型設置類型詳細注解,需要將每一個元素都標記出來 2. 字典類型設置類型詳細注解,需要2個類型,第一個是key,第二個是value"""my_list1: list[int] = {1, 2, 4}my_tuple1: tuple[str, int, bool] = ("張三", 34, True)my_set1: set[int] = {1, 2, 4}my_dict1: dict[str, int] = {"name": "張三"}
除了使用 變量: 類型, 這種語法做注解外,也可以在注釋中進行類型注解。語法:#type: 類型
# 在注釋中進行類型注解class Student:passvar_1 = random.randint(1, 10) # type:intvar_2 = json.loads(data) # type:dict[str,int]var_3 = func() # type:Student
tips:一般,無法直接看出變量類型之時會添加變量的類型注解。
類型注解的限制
類型注解主要功能在于:
并不會真正的對類型做驗證和判斷,也就是說,類型注解僅僅是提示性的,不是決定性的。
var_1: int = "張三"var_2: str = 123# 該代碼是不會報錯的
總結
函數和方法的形參類型注解語法:
def 函數方法名(形參名:類型,形參名:類型,.......):pass
def add(x: int, y: int): return x + yadd(1, 3)def func(data: list): data.append(1)
函數(方法)的返回值也是可以添加類型注解的。語法如下:
def 函數方法名(形參:類型,形參:類型, .....) -> 返回值類型:pass
def add(x: int, y: int) -> int: return x + ydef func(data: list[int]) -> list[int]: pass
使用Union[類型,...,類型]可以定義聯合類型注解
mylist: list[int] = [1, 2, 3]my_dict: dict[str, int] = {"age": 31, "num": 1}mylist = [1, 2, "name"]my_dict = {"name": "周杰倫", "age": 43}from typing import Unionmylist: list[Union[str, int]] = [1, 2, "name"]my_dict: dict[str, Union[str, int]] = {"name": "周杰倫", "age": 43}
# Union聯合類型注解,在變量注解、函數(方法)形參和返回值注解中,均可使用。my_list: list[Union[int, str]] = [1, 2, "張三", "李四"]my_dict: dict[Union[int, str]] = [1, 2, "張三", "李四"]def func(data: Union[int, str]) -> Union[int, str]: pass
總結:
基本需求
實現代碼:
class Student: def __init__(self, name, age, address, count): self.name = name self.age = age self.address = address self.count = count print(f"學生{count}信息錄入完成,信息為:【學生姓名:{self.name},年齡:{self.age},地址:{self.address}】") count += 1for i in range(1, 11): print(f"當前錄入第{i}位學生信息,總共需錄入10位學生信息") name = input("請輸入學生姓名:") age = input("請輸入學生年齡:") address = input("請輸入學生地址:") student = Student(name, age, address, i)
某公司,有2份數據文件,現需要對其進行分析處理,計算每日的銷售額并以柱狀圖表的形式進行展示。
數據內容
1月份數據是普通文本,使用逗號分割數據記錄,從前到后分別是(日期,訂單id,銷售額,銷售省份)
2月份數據是JSON數據,同樣包含(日期,訂單id,銷售額,銷售省份)
需求分析
實現代碼如下:
數據定義:data_define.py
"""數據定義的類"""class Record: def __init__(self, date, order_id, money, province): self.date = date # 訂單日期 self.order_id = order_id # 訂單ID self.money = money # 訂單金額 self.province = province # 銷售省份 def __str__(self): return f"訂單日期={self.date},訂單ID={self.order_id},訂單金額={self.money},銷售省份={self.province}"
操作文件:file_define.py
"""和文件相關的類定義"""import json# 導包from data_define import Record# 先定義一個抽象類用來做頂層設計,確定有哪些功能需要實現class FileReader: # 抽象方法 def reader_data(self) -> list[Record]: # 讀取文件數據,讀到的每一條數據都轉換為Record對象,將他們都封裝到list內返回即可 passclass TextFileReader(FileReader): def __init__(self, path): self.path = path # 定義成員變量記錄文件路徑 # 復寫(實現抽象方法)父類的方法 def reader_data(self) -> list[Record]: f = open(self.path, "r", encoding="UTF-8") record_list: list[Record] = [] for line in f.readlines(): line = line.strip() # 消除讀取到的每一行數據中的\n data_list = line.split(",") record = Record(data_list[0], data_list[1], int(data_list[2]), data_list[3]) record_list.append(record) f.close() return record_listclass JsonFileReader(FileReader): def __init__(self, path): self.path = path # 定義成員變量記錄文件路徑 # 復寫(實現抽象方法)父類的方法 def reader_data(self) -> list[Record]: f = open(self.path, "r", encoding="UTF-8") record_list: list[Record] = [] for line in f.readlines(): data_dict = json.loads(line) record = Record(data_dict["date"], data_dict["order_id"], int(data_dict["money"]), data_dict["province"]) record_list.append(record) f.close() return record_list# 測試使用if __name__ == "__main__": text_file_reader = TextFileReader("2011年1月銷售數據.txt") json_filer_reader = JsonFileReader("2011年2月銷售數據JSON.txt") list1 = text_file_reader.reader_data() list2 = json_filer_reader.reader_data() for l in list1: print(l) for l in list2: print(l)
主文件:main.py
"""面向對象,數據分析案例,主業務邏輯代碼實現步驟:1.設計一個類,可以完成數據的封裝2.設計一個抽象類,定義文件讀取的相關功能,并使用子類實現具體功能3.讀取文件,生產數據對象4.進行數據需求的邏輯計算(計算每一天的銷售額)5.通過PyEcharts進行圖形繪制"""from file_define import FileReader, TextFileReader, JsonFileReaderfrom data_define import Recordfrom pyecharts.charts import Barfrom pyecharts.options import *from pyecharts.globals import ThemeTypetext_file_reader = TextFileReader("2011年1月銷售數據.txt")json_file_reader = JsonFileReader("2011年2月銷售數據JSON.txt")jan_data: list[Record] = text_file_reader.reader_data()feb_data: list[Record] = json_file_reader.reader_data()# 將2個月份的數據合并為一個list來存儲all_data: list[Record] = jan_data + feb_data# 開始進行數據計算# {"2011-1-1": 1234, "2011-01-02": 100}data_dict = {}for record in all_data: if record.date in data_dict.keys(): # 當前日期已經有記錄了,所以和老記錄做累加即可 data_dict[record.date] += record.money else: data_dict[record.date] = record.money# 可視化圖表開發bar = Bar(init_opts=InitOpts(theme=ThemeType.LIGHT))bar.add_xaxis(list(data_dict.keys())) # 添加x軸的數據bar.add_yaxis("銷售額", list(data_dict.values()), label_opts=LabelOpts(is_show=False)) # 添加了y軸的數據bar.set_global_opts( title_opts=TitleOpts(title="每日銷售額"))bar.render("每日銷售額柱狀圖.html")
程序運行結果如下:
免責聲明:本文不構成任何商業建議,投資有風險,選擇需謹慎!本站發布的圖文一切為分享交流,傳播正能量,此文不保證數據的準確性,內容僅供參考
關鍵詞: