2022 Python全攻略:OOP - HackMD
文章推薦指數: 80 %
OOP is a method of structring a program by bounding related properties and behaviors into individual objects. There are many built-in classes in Python, ...
Published
LinkedwithGitHub
Like
Bookmark
Subscribe
Edit
---
tags:Python
---
#2022Python全攻略:OOP
:::info
:earth_asia:**前言**
本筆記是udemy"2022Python全攻略"課程的學習紀錄。
參考連結:https://www.udemy.com/course/python-master/
:::
##CH6.Object-OrientedPrograming
###91.IntrotoOOP
*OOPisamethodofstructringaprogrambyboundingrelatedpropertiesandbehaviorsintoindividualobjects.
*Therearemanybuilt-inclassesinPython,andwecancreateourowntypesaswell.
***Aclassisacodetemplateforcreatingobjects.**Objectshavepropertiesandbehaviorassociatedwiththem.
*Basically,wecanconsider"class"asanewdatatypethatcancreateonourown,justlikethebuilt-inPythondatatypes.
*Thenamingconventionofclassnameisalwayscapitalizedthefirstletter!
```python=
classRobot:
pass
myRobot1=Robot()
print(type(myRobot1))#
但classmethod的好處在於,classmethod屬於這個class,所以我們透過class來instantiateobjects時,每個object佔用的記憶體會更少。
假定某個class有10個method以及3個classmethod,那麼我們instantiate100個objects時,會用到的記憶體量是100x10=1000單位。
但如果我們不用classmethod,全寫成一般的method,那麼我們instantiate100個objects時,會用到的記憶體量是100x13=1300單位。
因此,如果我們知道某個method可以寫成classmethod,就應該要這麼做,節省記憶體用量。
另外,對程式節省記憶體是很重要的。
例如,GoogleChrome瀏覽器因為佔用太多記憶體而為人詬病,edge,firefox,safari等等瀏覽器的用戶比例近年不斷攀升,就是因為這個緣故。
###94.Inheritance(繼承)
*Inheritanceallowsustodefineaclassthatinheritsallthemethodsandpropertiesfromanotherclass.
*The**parentclass**istheclassbeinginheritedfrom,alsocalledthebaseclass.
*The**childclass**istheclassthatinheritsfromanotherclass,alsocalledthederivedclass.
```python=
classPeople:
def__init__(self,name,age):
self.name=name
self.age=age
defsleep(self):
print(f"{self.name}issleeping...")
defwalk(self):
print(f"{self.name}iswalking...")
classStudent(People):
def__init__(self,name,age,student_id):
People.__init__(self,name,age)#呼叫父類的方法
self.student_id=student_id
defwalk(self):#會複寫父類的函式
print(f"Student{self.name}iswalking...")
student1=Student("Wilson",25,123456789)
print(student1.student_id)
student1.sleep()
student1.walk()
```
*The`super()`(命名源自離散數學的superset)methodreturnsaproxyobject(temporaryobjectofthesuperclass)thatallowsustoaccessmethodsofthebaseclass.
```python=
classPeople:
def__init__(self,name,age):
self.name=name
self.age=age
defsleep(self):
print(f"{self.name}issleeping...")
defwalk(self):
print(f"{self.name}iswalking...")
classStudent(People):
def__init__(self,name,age,student_id):
super().__init__(name,age)#差在這行,呼叫父類的方法
self.student_id=student_id
defwalk(self):#會複寫父類的函式
print(f"Student{self.name}iswalking...")
student1=Student("Wilson",25,123456789)
print(student1.student_id)
student1.sleep()
student1.walk()
```
###95.MultipleInheritance
*InJavaandJavascript,multipleinheritancesarenotallowed.InC++,multipleinheritancesarepermitted,butit'sincrediblycomplicated;peopleavoidusingitasmuchaspossible.
*Pythonsupportsmultipleinheritances!Thisisnotspecial.Thatjustmeanswecaninheritfrommorethanoneparentclass.
```python=
classC:
defdo_stuff(self):
print("hellofromclassC")
classD:
defdo_another_stuff(self):
print("hellofromclassD")
classA(C,D):
pass
a=A()
a.do_stuff()
a.do_another_stuff()
```
*Thingsbecomecomplicatediftheinheritancegraphishuge.Forexample,ifwehave(每個綠圈圈代表一個class):
![](https://i.imgur.com/1jWk3LB.png)
```python=
classE:
defdo_stuff_XXX(self):
print("doingstufffromclassE")
classF:
defdo_stuff_XXX(self):
print("doingstufffromclassF")
classB(E,F):
defdo_stuff_B(self):
print("doingstufffromclassB")
classC:
defdo_stuff_C(self):
print("doingstufffromclassC")
classG:
defdo_stuff_XXX(self):
print("doingstufffromclassG")
classD(G):
defdo_stuff_D(self):
print("doingstufffromclassD")
classA(B,C,D):
defdo_stuff_A(self):
print("doingstufffromclassA")
a=A()
a.do_stuff_C()#doingstufffromclassC
a.do_stuff_XXX()#doingstufffromclassE(why???)
print(A.mro())#用classname,而非objectname
print(A.__mro__)
```
*Thebasicprincipleof**methodresolutionorder(MRO)**usesa**depth-firstgraphtraversalalgorithm**.
*Byapplyingthealgorithm,wewouldget:A,B,E,F,C,D,G.Thisistheorderofwhereamethodshouldbelookedup.
*IfyoucannotdeterminedtheMRO,wecanusethePythonbuilt-intofigureouttheorder:
*`classname.mro()`returnsalist
*`classname.__mro__`returnsatuple
*WhydoweusethiskindofalgorithmtodoMRO?Well,thealgorithmjustmakessurethateachclassisvisitedonce.However,thislogicdoesn'tmakeyourcodeeasiertoreadormaintain.
*Somepeopleinsistthatmultipleinheritancesarebadidea.Manytaskscanbedonewithoutmultipleinheritancesaswell.However,wedogetsomebenefitsbyusingthis-itallowsustoavoidbuildingprofound(很深的)inheritancerelations.(如果不用多重繼承,就要一個一個class連續繼承,關係鏈會變得很深(?))
###96.C3Linearization
*C3linearization演算法專門用來處理多重繼承
*Traditionally,**diamondproblem**occurswhenwedomultipleinheritance.Considerthis:
![](https://i.imgur.com/Q7NxcuL.png)
*Byapplyingdepthfirsttraversal,weknowthatthemethodresolutionorderwillbe:D,B,A,C,A.However,wearegettingduplicateAintheorder;it'sveryintuitive(直覺的)thatAcomesbeforeC.InthepreviousversionofPython,MROisinthisorder.Python3usesanewalgorithmcalledthe**C3LinearizationAlgorithm**.
*Wewon'tspendtimelearningwhatthisis(sincethisiswayoutofthescopeofoutcourse);however,thisC3linearizationdoessolvebothproblems;wedon'thaveduplicateAandCcomesbeforeA.
*WithC3linearization,theMROofthediamondproblemwillbe:D,B,C,A.
*ReadaboutC3linearizationfrom[here](https://en.wikipedia.org/wiki/C3_linearization)ifinterested.
###97.Private(私有)AttributesandMethods
*Inthecontextofclass,privatemeanstheattributesormethodsareonlyavailablefortheclassmembers,notfortheoutsideoftheclass.
*Whendefiningprivateattributesandmethods,wejustneedtoaddadoubleunderscore(底線)atthefrontandnounderscoreattheend;then,thatattributeormethodwouldbecomeprivate.
```python=
classRobot:
def__init__(self,name):
self.name=name
self.__age=25
def__private_method(self):
print("Hellofromprivatemethod.")
defgreet(self):
self.__private_method()
defget_age(self):
print(f"Hi,I'm{self.__age}yearsold.")
defset_age(self,inputage):
self.__age=inputage
myRobot1=Robot("Wilson")
myRobot1.greet()#間接去呼叫私有方法
print(myRobot1.__age)#AttributeError:'Robot'objecthasnoattribute'__age'
myRobot1.set_age(65)#用setter間接去設定私有屬性
myRobot1.get_age()#用getter間接去取得私有屬性
```
*InPython,byconvention,anyattributeormethodstartedwithasingleunderscoreisalsoseenasprivate.
*However,eventhoughwecanaccesstheattributedirectly,weshouldstillconsideritasaprivatevariable;trynottoaccessitasmuchaspossiable.
####OOPStyleinPython
*Traditionally,OOPdesignemphasizesinformationhiding;thisisknownas"encapsulation(封裝)".Allattributesofanobjectshouldbeasprivateaspossiable.Thepurposeofencapsulationistopreventchangingattributesofanobjectasthatmightcauseanunexpectederror.
*Whenwereallyhavetoaccessattributesofanobject,weactuallydefinepublicmethodscalledgetterandsetter.Traditionally,gettersandsettersaccessattributesanddosomemiddlewarework,suchaschangingunitsorcheckingthemaxandminvalue.
*適當的封裝,可以將物件使用介面的程式實作部份隱藏起來,不讓使用者看到,同時確保使用者無法任意更改物件內部的重要資料,若想接觸資料只能通過公開接入方法(Publiclyaccessiblemethods)的方式(如:"getter"和"setter")。
封裝可以讓程式碼更容易理解與維護,也加強了程式碼的安全性,但也要注意不要過度封裝。
```python=
classRobot:
def__init__(self,name):
self.name=name
self.__age=25
defget_age(self):
print(f"Hi,I'm{self.__age}yearsold.")
defset_age(self,inputage):
ifinputage>0andinputage<100:
self.__age=inputage
else:
print("Invalidinputvalue.")
myRobot1=Robot("Wilson")
myRobot1.set_age(-10)
```
*However,inPython,getterandsetterarenotconsideredPythonic!
*Then,wemighthaveaquestion:howdowepreventoutsidersfromrandomlyacccessingandchangingtheinnerattributesofobjects?InthePythoncommunity,afamoussayingis,"Weareallconsentingadults.".Thatmeanswetrusteachother;ifsomeonewantstomakechaos,theyhavetoberesponsiblefortheirdecision.That'swhyweseebothsingleanddoubleunderscorecanbeusedforprivatedattributes.(就算singleunderscore不是正式語法也沒關係,大家有建立默契就好)
*ThedifferentbetweenPythonOOPandtraditionalOOPhighlightstheconceptofPythonandotherporgramminglanguages.TraditionalOOPemphasizeserrorpreventionandimformationhiding;ontheotherhand,Pythonfocusesonreadability.
###98.@propertydecorator
*之後才會介紹什麼是decorator(透過裝飾器機制可以在定義函式與方法後,用簡單的方式來做修改並重新定義),此處先介紹其中的@property:可以在一個class中新增一個虛擬property
*Sometimes,whenweaccessingorassigninganattributeofanobject,wedon'twanttoaccessorassignitdirectly.
*Forexample,
*WhenwehavetochangetheunitfromCelsiustoFahrenheit.
*Whenweneedtocheckifthevalueassignmentiswithinacertainrange.
*Traditionally,thisisdonebygettersandsettersinotherprogramminglanuage.InPython,@propertydecoratorsisabuilt-indecorator,whichishelpfulindefiningvirtualpropertieseffortlessly.
```python=
classEmployee:
def__init__(self):
self.income=0
self.__tax=0#privatevariable
defearn_money(self,money):
self.income+=money
self.__tax=self.income*0.05#課程影片用"+=",應該是"="?
defget_tax(self):
returnself.__tax
wilson=Employee()
wilson.earn_money(300)
print(wilson.get_tax())#15
wilson.earn_money(500)
print(wilson.get_tax())#40
```
上述程式中的line4`self.__tax=0`其實可以透過@property簡化語法
```python=
classEmployee:
def__init__(self):
self.income=0
defearn_money(self,money):
self.income+=money
@property
deftax_amount(self):#雖然定義成function,但其實是Employee的(虛擬)property
returnself.income*0.05
wilson=Employee()
wilson.earn_money(300)
print(wilson.tax_amount)#15
wilson.earn_money(500)
print(wilson.tax_amount)#40
```
用另一個叫做@method.setter的decorator,可以幫虛擬property建立一個虛擬setter,用來設定值
```python=
classEmployee:
def__init__(self):
self.income=0
defearn_money(self,money):
self.income+=money
@property
deftax_amount(self):
returnself.income*0.05
@tax_amount.setter
deftax_amount(self,tax_number):
self.income=tax_number*20#由稅金回推原先賺了多少
wilson=Employee()
wilson.tax_amount=200
print(wilson.income)#4000
```
###99.TheMighty(強大的)HashFunction
*Ahash(雜湊)isafunctionthatconvertonvaluetoanother.
*InPython,there'sabuilt-inhash()function,anditreturnsafixed-sizedinteger.
*Hashabledatatypescanbehashedbyhash():string,int,float,bool,tuple(withhashableelements),None.
*MostofPython'simmutablebuilt-inobjectsarehashable;mutablecontainers(suchaslistsordictionaries)arenot;immutablecontainers(suchastuples)areonlyhashableiftheirelementsareallhashable.
*Objectswhichareinstancesofuserdefinedclassesarehashablebydefault.(butwecandefinethehashvaluebyimplementingthe`__hash__()`method)
*Set,listanddictionaryarenothashable.
```python=
print(hash("Howareyoudoingtoday?"))
print(hash(10))
print(hash(9.999))
print(hash(True))
print(hash((1,4,7,11)))
print(hash(None))
#result:(為啥每次執行後都不一樣?見後面)
#3497754365099253586
#10
#2303537166204481545
#1
#943204701236206362
#-9223363241360645427
```
*Thetwoideas,hashableandimmutable(不可變的),arerelatedbecauseobjectswhichareusedashashkeysmusttypicallybeimmutable,sotheirhashvaluedoesn'tchange.
*Ifitwereallowedtochange,thenthelocationofthatobjectinadatastrcturesuchasahashtablewouldchange,andthenthewholepurposeofhashingforefficiencyisdefeated.
**Review:Common-useddatatypes**
|Name|Type|Mutable|
|------------|-----|:-------:|
|Integer|int|no|
|Float|float|no|
|String|str|no|
|Boolean|bool|no|
|List|list|yes|
|Dictionaries|dict|yes|
|Tuples|tup|no|
|Sets|set|yes|
*Herearesomekeyfeaturesofagoodhashfunction:
1.Consistent(一致):eachtimewegivethesameinputtothehash()function,weneedtogetthesameoutput.
2.DistributedEvenly(均勻分布):asmallchangeininputshouldresultinahugedifferenceinoutput.Thiswillreducethenumberofhashcollisions(雜湊碰撞,兩個不同的輸入得到相同的hash結果).
3.Notinvertible(不可逆):thisfunctionshouldnotbeinvertibleforsecuritypurpose.
*Wewon'tlearnhowtocreateourownhashfunctioninthiscourse.Ifinterested,youcanlearnmoreabouthashfunctioninalgorithmanddatastructureclass.
*NotethatthehashofavalueonlyneedstobethesameforonerunofPython.InPython3.3,resultofhash()willchangeforeverynewrun.Thisistomakeithardertoguesswhathashvalueaparticularstringwillhave,whichisanimportantsecurityfeatureforapplication.
*Hashvaluesof`hash()`shouldthereforenotbestoredpermanently.Ifweneedtousehashvaluesinapermanentway,wecantakealookatthemore"serious"typesofhashes-cryptographichashfunctions(密碼雜湊函式),suchasbcrypt(常用於網頁後端加密),SHA-256(用於比特幣),RSAalgorithm,EllipticCurveCryptography.
*Theideaofhashfunctioniswidelyusedintherealworld.
*Passwordsarehashedbeforetheygetstoredinthedatabase.Bythat,evensomeonehacksintothesystem,theycannotseethepassword.Also,theycannotdoanyreverseengineering.
*Hashtable(一種資料結構)useshashfunction.InPython,dictionaryandsetbothimplementhashtable.Therefore,whendoingvaluelook-up(byusingthemembershipoperator`in`)indictionaryandset,thespeedisconstantlyfastregardlessofthesizeofdictionaryorset.(However,list'slook-uptimedependsonthesizeofthelist.)
###100.\_\_hash\_\_and\_\_eq\_\_
*`__hash__()`跟`__eq__()`前者用於定義對物件做hash()的結果,後者定義兩個物件做比較時的結果,定義的內容跟返回值都需要自訂
*Anyobjectthatimplementsthe`__hash__()`functioncanbeusedasakeyfordictionary.Aneasy,correctwaytoimplement`__hash__()`istouseakeytuple.Wecanjustreturnthehashedvalueofthekey.
```python=
classRobot:
def__init__(self,name,age,address):
self.name=name
self.age=age
self.address=address
#defineaprivatemethodkey()
def__key(self):
return(self.name,self.age,self.address)
#implement__hash__()function
def__hash__(self):
#returnhash(self.__key())#此返回值為任何Robot生成的物件去做hash會得到的值
return1000#測試一下,正常要用上面那句
robot1=Robot("Wilson",25,"Taiwan")
robot2=Robot("Will",46,"Korea")
print(hash(robot1))#1000
print(hash(robot2))#1000
```
*Also,Pythonautomaticallycallsthe`__eq__()`methodofaclasswhenyouseethe`==`operatortocomparetheinstancesoftheclass.
*Therefore,inourclass,weshouldimplementthis`__eq__()`methodby:
1.Checkifthetypematches
2.Checkifthekey()matches
```python=
classRobot:
def__init__(self,name,age,address):
self.name=name
self.age=age
self.address=address
#defineaprivatemethodkey()
def__key(self):
return(self.name,self.age,self.address)
#implement__hash__()function
def__hash__(self):
returnhash(self.__key())#此返回值為任何Robot生成的物件去做hash會得到的值
def__eq__(self,other):#用'other'代表被比較者
ifisinstance(other,Robot):#先用isinstance()檢查other是不是類別為Robot的物件
returnother.__key()==self.__key()#再比較__key()的內容
#此返回值為任何Robot生成的物件去做比較時會得到的值:True表示typematches且key()matches
returnNotImplemented#Python內建常數,表示沒有對其他類型做實現
robot1=Robot("Wilson",25,"Taiwan")
robot2=Robot("Will",46,"Korea")
robot3=Robot("Wilson",25,"Taiwan")
print(robot1==robot2)#False
print(robot1==robot3)#True,表示這兩個物件是同個class生出來的
```
###101.DunderorMagicMethods
*DunderormagicmethodsinPythonarethemethodshavongtwoprefixsandsuffixunderscoresinthemethodname.Thesearecommonlyusedfor**operatoroverloading(運算子重載,Python中允許同一運算符根據上下文具有不同含義)**.
*Dunderheremeans"doubleunderscores".
*Besidesthe`__init__`,`__eq__`,`__hash__`welearned,thefollowinglistissomedundermethodswecanimplementinPythoncalss:
*`__len__`
*`__str__`:user-readablestring
*`__repr__`:standsforrepresentation
*`__add__`
*`__gt__`:greatthan
*`__ge__`:greatthanorequalto
*`__lt__`:lessthan
*`__le__`:lessthanorequalto
*`__str__()`isusedforcreatingoutputforenduserwhile`__repr__()`ismainlyusedfordebugginganddevelopment.
*`__repr__()`goalistobeunambiguous(明白的)and`__str__()`istobereadable.
*ThePythonbuilt-inprint()methodlooksforthestringmethodoftheobject.
```python=
classRobot:
def__init__(self,name,age,address):
self.name=name
self.age=age
self.address=address
def__len__(self):
return100
def__str__(self):
return"someinformation"
def__repr__(self):
return"somedebugmessage"
def__add__(self,other):
return400
def__gt__(self,other):
return500
def__ge__(self,other):
return600
def__lt__(self,other):
return700
def__le__(self,other):
return800
robot1=Robot("Wilson",25,"Taiwan")
robot2=Robot("Will",46,"Korea")
print(len(robot1))
print(str(robot1))#或print(robot1)
print(repr(robot1))
print(robot1+robot2)
print(robot1>robot2)
print(robot1>=robot2)
print(robot1
延伸文章資訊
- 1Python 101 基礎教學(10) - 物件導向Object-Oriented ...
物件導向Object-Oriented Programming(OOP) 是以物件的形式去描述你資料應有的行為。就像我們在現實世界中定義貓、狗、人、車等等一樣,都有自己的 ...
- 2DAY08-搞懂Python的OOP - iT 邦幫忙
什麼是OOP ... 由上述程序說明了Python使用 ( ) 表示所繼承的物件,當要使用時只要 類別名稱() ... 透過本章的說明希望各位讀者們可以對Python的OOP有更進一步的了解。
- 3【Python 教學】OOP 繼承/封裝/多型基本用法Example - 測試先生
- 4Python OOPs Concepts - GeeksforGeeks
In Python, object-oriented Programming (OOPs) is a programming paradigm that uses objects and cla...
- 52022 Python全攻略:OOP - HackMD
OOP is a method of structring a program by bounding related properties and behaviors into individ...