Properties vs. Getters and Setters | OOP | python-course.eu

文章推薦指數: 80 %
投票人數:10人

Unfortunately, it is widespread belief that a proper Python class should encapsulate private attributes by using getters and setters. PythonTrainingCourses LivePythonclassesbyhighlyexperiencedinstructors: Instructor-ledtrainingcoursesbyBerndKlein InthisTkinterchapter IntrotoObjectOrientedProgramming ObjectOrientedProgramming Classvs.InstanceAttributes Propertiesvs.GettersandSetters ImplementingaCustomPropertyClass IntroductiontoDescriptors Inheritance MultipleInheritance MultipleInheritance:Example MagicMethods CallableInstancesofClasses InheritanceExample Slots:AvoidingDynamicallyCreatedAttributes PolynomialClass DynamicallyCreatingClasseswithtype RoadtoMetaclasses Metaclasses CountFunctioncallswiththehelpofaMetaclass The'ABC'ofAbstractBaseClasses ClassroomTrainingCourses ThiswebsitecontainsafreeandextensiveonlinetutorialbyBerndKlein,usingmaterialfromhisclassroomPythontrainingcourses. Ifyouareinterestedinaninstructor-ledclassroomtrainingcourse,havealookatthesePythonclasses: Instructor-ledtrainingcoursebyBerndKleinatBodenseo Image©kabliczech-Fotolia.com PDFversion PDFversionofthissite HelpNeeded Thiswebsiteisfreeofannoyingads.Wewanttokeepitlikethis.Youcanhelpwithyourdonation: Theneedfordonations 3.Propertiesvs.GettersandSetters ByBerndKlein.Lastmodified:01Feb2022. Onthispage➤ Properties Getters(alsoknownas'accessors')andsetters(aka.'mutators')areusedinmanyobjectorientedprogramminglanguagestoensuretheprincipleofdataencapsulation.Dataencapsulation-aswehavelearntinourintroductiononObjectOrientedProgrammingofourtutorial-isseenasthebundlingofdatawiththemethodsthatoperateonthem.Thesemethodsareofcoursethegetterforretrievingthedataandthesetterforchangingthedata.Accordingtothisprinciple,theattributesofaclassaremadeprivatetohideandprotectthem. Unfortunately,itiswidespreadbeliefthataproperPythonclassshouldencapsulateprivateattributesbyusinggettersandsetters.Assoonasoneoftheseprogrammersintroducesanewattribute,heorshewillmakeitaprivatevariableandcreates"automatically"agetterandasetterforthisattribute.SuchprogrammersmayevenuseaneditororanIDE,whichautomaticallycreatesgettersandsettersforallprivateattributes.Thesetoolsevenwarntheprogrammerifsheorheusesapublicattribute!Javaprogrammerswillwrinkletheirbrows,screwuptheirnoses,orevenscreamwithhorrorwhentheyreadthefollowing:ThePythonicwaytointroduceattributesistomakethempublic. Wewillexplainthislater.First,wedemonstrateinthefollowingexample,howwecandesignaclassinaJavaesquewaywithgettersandsetterstoencapsulatetheprivateattributeself.__x: classP: def__init__(self,x): self.__x=x defget_x(self): returnself.__x defset_x(self,x): self.__x=x Wecanseeinthefollowingdemosessionhowtoworkwiththisclassandthemethods: frommutatorsimportP p1=P(42) p2=P(4711) p1.get_x() OUTPUT: 42 p1.set_x(47) p1.set_x(p1.get_x()+p2.get_x()) p1.get_x() OUTPUT: 4758 Whatdoyouthinkabouttheexpression"p1.set_x(p1.get_x()+p2.get_x())"?It'sugly,isn'tit?It'saloteasiertowriteanexpressionlikethefollowing,ifwehadapublicattributex: p1.x=p1.x+p2.x SuchanassignmentiseasiertowriteandabovealleasiertoreadthantheJavaesqueexpression. Let'srewritetheclassPinaPythonicway.Nogetter,nosetterandinsteadoftheprivateattributeself.__xweuseapublicone: classP: def__init__(self,x): self.x=x Beautiful,isn'tit?Justthreelinesofcode,ifwedon'tcounttheblankline! frompimportP p1=P(42) p2=P(4711) p1.x OUTPUT: 42 p1.x=47 p1.x=p1.x+p2.x p1.x OUTPUT: 4758 "But,but,but,but,but...",wecanhearthemhowlingandscreaming,"ButthereisNOdataENCAPSULATION!"Yes,inthiscasethereisnodataencapsulation.Wedon'tneeditinthiscase.Theonlythingget_xandset_xinourstartingexampledidwas"gettingthedatathrough"withoutdoinganythingadditionally. Butwhathappensifwewanttochangetheimplementationinthefuture?Thisisaseriousargument.Let'sassumewewanttochangetheimplementationlikethis:Theattributexcanhavevaluesbetween0and1000.Ifavaluelargerthan1000isassigned,xshouldbesetto1000.Correspondingly,xshouldbesetto0,ifthevalueislessthan0. ItiseasytochangeourfirstPclasstocoverthisproblem.Wechangetheset_xmethodaccordingly: classP: def__init__(self,x): self.set_x(x) defget_x(self): returnself.__x defset_x(self,x): ifx<0: self.__x=0 elifx>1000: self.__x=1000 else: self.__x=x ThefollowingPythonsessionshowsthatitworksthewaywewantittowork: frommutators1importP p1=P(1001) p1.get_x() OUTPUT: 1000 p2=P(15) p2.get_x() OUTPUT: 15 p3=P(-1) p3.get_x() OUTPUT: 0 Butthereisacatch:Let'sassumewedesignedourclasswiththepublicattributeandnomethods: classP2: def__init__(self,x): self.x=x Peoplehavealreadyuseditalotandtheyhavewrittencodelikethis: p1=P2(42) p1.x=1001 p1.x OUTPUT: 1001 IfwewouldchangeP2nowinthewayoftheclassP,ournewclasswouldbreaktheinterface,becausetheattributexwillnotbeavailableanymore.That'swhyinJavae.g.peoplearerecommendedtouseonlyprivateattributeswithgettersandsetters,sothattheycanchangetheimplementationwithouthavingtochangetheinterface. ButPythonoffersasolutiontothisproblem.Thesolutioniscalledproperties! Theclasswithapropertylookslikethis: classP: def__init__(self,x): self.x=x @property defx(self): returnself.__x @x.setter defx(self,x): ifx<0: self.__x=0 elifx>1000: self.__x=1000 else: self.__x=x Amethodwhichisusedforgettingavalueisdecoratedwith"@property",i.e.weputthislinedirectlyinfrontoftheheader.Themethodwhichhastofunctionasthesetterisdecoratedwith"@x.setter".Ifthefunctionhadbeencalled"f",wewouldhavetodecorateitwith"@f.setter".Twothingsarenoteworthy:Wejustputthecodeline"self.x=x"inthe__init__methodandthepropertymethodxisusedtocheckthelimitsofthevalues.Thesecondinterestingthingisthatwewrote"two"methodswiththesamenameandadifferentnumberofparameters"defx(self)"and"defx(self,x)".Wehavelearnedinapreviouschapterofourcoursethatthisisnotpossible.Itworkshereduetothedecorating: fromp2importP p1=P(1001) p1.x OUTPUT: 1000 p1.x=-12 p1.x OUTPUT: 0 Alternatively,wecouldhaveusedadifferentsyntaxwithoutdecoratorstodefinetheproperty.Asyoucansee,thecodeisdefinitelylesselegantandwehavetomakesurethatweusethegetterfunctioninthe__init__methodagain: classP: def__init__(self,x): self.set_x(x) defget_x(self): returnself.__x defset_x(self,x): ifx<0: self.__x=0 elifx>1000: self.__x=1000 else: self.__x=x x=property(get_x,set_x) Thereisstillanotherprobleminthemostrecentversion.Wehavenowtwowaystoaccessorchangethevalueofx:Eitherbyusing"p1.x=42"orby"p1.set_x(42)".ThiswayweareviolatingoneofthefundamentalsofPython:"Thereshouldbeone--andpreferablyonlyone--obviouswaytodoit."(seeZenofPython) Wecaneasilyfixthisproblembyturningthegetterandthesettermethodsintoprivatemethods,whichcan'tbeaccessedanymorebytheusersofourclassP: classP: def__init__(self,x): self.__set_x(x) def__get_x(self): returnself.__x def__set_x(self,x): ifx<0: self.__x=0 elifx>1000: self.__x=1000 else: self.__x=x x=property(__get_x,__set_x) Eventhoughwefixedthisproblembyusingaprivategetterandsetter,theversionwiththedecorator"@property"isthePythonicwaytodoit! Fromwhatwehavewrittensofar,andwhatcanbeseeninotherbooksandtutorialsaswell,wecouldeasilygettheimpressionthatthereisaone-to-oneconnectionbetweenproperties(ormutatormethods)andtheattributes,i.e.thateachattributehasorshouldhaveitsownproperty(orgetter-setter-pair)andtheotherwayaround.EveninotherobjectorientedlanguagesthanPython,it'susuallynotagoodideatoimplementaclasslikethat.Themainreasonisthatmanyattributesareonlyinternallyneededandcreatinginterfacesfortheuseroftheclassincreasesunnecessarilytheusabilityoftheclass.Thepossibleuserofaclassshouldn'tbe"drowned"withumpteen-ofmainlyunnecessary-methodsorproperties! Thefollowingexampleshowsaclass,whichhasinternalattributes,whichcan'tbeaccessedfromoutside.Thesearetheprivateattributesself.__potential_physicalandself.__potential_psychic.Furthermoreweshowthatapropertycanbededucedfromthevaluesofmorethanoneattribute.Theproperty"condition"ofourexamplereturnstheconditionoftherobotinadescriptivestring.Theconditiondependsonthesumofthevaluesofthepsychicandthephysicalconditionsoftherobot. classRobot: def__init__(self,name,build_year,lk=0.5,lp=0.5): self.name=name self.build_year=build_year self.__potential_physical=lk self.__potential_psychic=lp @property defcondition(self): s=self.__potential_physical+self.__potential_psychic ifs<=-1: return"Ifeelmiserable!" elifs<=0: return"Ifeelbad!" elifs<=0.5: return"Couldbeworse!" elifs<=1: return"Seemstobeokay!" else: return"Great!" if__name__=="__main__": x=Robot("Marvin",1979,0.2,0.4) y=Robot("Caliban",1993,-0.4,0.3) print(x.condition) print(y.condition) OUTPUT: Seemstobeokay! Ifeelbad! LivePythontraining Enjoyingthispage?WeofferlivePythontrainingcoursescoveringthecontentofthissite. See:LivePythoncoursesoverview Enrolhere PublicinsteadofPrivateAttributes Let'ssummarizetheusageofprivateandpublicattributes,gettersandsetters,andproperties:Let'sassumethatwearedesigninganewclassandweponderingaboutaninstanceorclassattribute"OurAtt",whichweneedforthedesignofourclass.Wehavetoobservethefollowingissues: Willthevalueof"OurAtt"beneededbythepossibleusersofourclass? Ifnot,wecanorshouldmakeitaprivateattribute. Ifithastobeaccessed,wemakeitaccessibleasapublicattribute Wewilldefineitasaprivateattributewiththecorrespondingproperty,ifandonlyifwehavetodosomechecksortransformationofthedata.(Asanexample,youcanhavealookagainatourclassP,wheretheattributehastobeintheintervalbetween0and1000,whichisensuredbytheproperty"x") Alternatively,youcoulduseagetterandasetter,butusingapropertyisthePythonicwaytodealwithit! Let'sassumewedefined"OurAtt"asapublicattribute.Ourclasshasbeensuccessfullyusedbyotherusersforquiteawhile. classOurClass: def__init__(self,a): self.OurAtt=a x=OurClass(10) print(x.OurAtt) OUTPUT: 10 NowcomesthepointwhichfrightenssometraditionalOOPistasoutoftheirwits:Imagine"OurAtt"hasbeenusedasaninteger.Now,ourclasshastoensurethat"OurAtt"hastobeavaluebetween0and1000?Withoutproperty,thisisreallyahorriblescenario!Duetopropertiesit'seasy:Wecreateapropertyversionof"OurAtt". classOurClass: def__init__(self,a): self.OurAtt=a @property defOurAtt(self): returnself.__OurAtt @OurAtt.setter defOurAtt(self,val): ifval<0: self.__OurAtt=0 elifval>1000: self.__OurAtt=1000 else: self.__OurAtt=val x=OurClass(10) print(x.OurAtt) OUTPUT: 10 Thisisgreat,isn'tit?Youcanstartwiththesimplestimplementationimaginable,andyouarefreetolatermigratetoapropertyversionwithouthavingtochangetheinterface!Sopropertiesarenotjustareplacementforgettersandsetters! Somethingelseyoumighthavealreadynoticed:Fortheusersofaclass,propertiesaresyntacticallyidenticaltoordinaryattributes. LivePythontraining Enjoyingthispage?WeofferlivePythontrainingcoursescoveringthecontentofthissite. See:LivePythoncoursesoverview UpcomingonlineCourses IntensiveAdvancedCourse 28Mar2022to01Apr2022 30May2022to03Jun2022 29Aug2022to02Sep2022 17Oct2022to21Oct2022 ObjectOrientedProgrammingwithPython 30Mar2022to01Apr2022 01Jun2022to03Jun2022 31Aug2022to02Sep2022 19Oct2022to21Oct2022 Enrolhere



請為這篇文章評分?