Python's property(): Add Managed Attributes to Your Classes

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

Decorators are everywhere in Python. They're functions that take another function as an argument and return a new function with added ... Start Here LearnPython PythonTutorials→In-deptharticlesandtutorials VideoCourses→Step-by-stepvideolessons Quizzes→Checkyourlearningprogress LearningPaths→Guidedstudyplansforacceleratedlearning Community→LearnwithotherPythonistas Topics→Focusonaspecificareaorskilllevel UnlockAllContent Store RPMembership PythonBasicsBook PythonTricksBook CPythonInternalsBook TheRealPythonCourse ManagingPythonDependencies SublimeText+PythonSetup PythonicWallpapersPack PythonMugs,T-Shirts,andMore PythonistaCafeCommunity BrowseAll» More PythonNewsletter PythonPodcast PythonJobBoard MeettheTeam BecomeaTutorialAuthor BecomeaVideoInstructor Search Join Sign‑In Python'sproperty():AddManagedAttributestoYourClasses byLeodanisPozoRamos Oct13,2021 best-practices intermediate python MarkasCompleted Tweet Share Email TableofContents ManagingAttributesinYourClasses TheGetterandSetterApproachinPython ThePythonicApproach GettingStartedWithPython’sproperty() CreatingAttributesWithproperty() Usingproperty()asaDecorator ProvidingRead-OnlyAttributes CreatingRead-WriteAttributes ProvidingWrite-OnlyAttributes PuttingPython’sproperty()IntoAction ValidatingInputValues ProvidingComputedAttributes CachingComputedAttributes LoggingAttributeAccessandMutation ManagingAttributeDeletion CreatingBackward-CompatibleClassAPIs OverridingPropertiesinSubclasses Conclusion Removeads WithPython’sproperty(),youcancreatemanagedattributesinyourclasses.Youcanusemanagedattributes,alsoknownasproperties,whenyouneedtomodifytheirinternalimplementationwithoutchangingthepublicAPIoftheclass.ProvidingstableAPIscanhelpyouavoidbreakingyourusers’codewhentheyrelyonyourclassesandobjects. PropertiesarearguablythemostpopularwaytocreatemanagedattributesquicklyandinthepurestPythonicstyle. Inthistutorial,you’lllearnhowto: Createmanagedattributesorpropertiesinyourclasses Performlazyattributeevaluationandprovidecomputedattributes AvoidsetterandgettermethodstomakeyourclassesmorePythonic Createread-only,read-write,andwrite-onlyproperties Createconsistentandbackward-compatibleAPIsforyourclasses You’llalsowriteafewpracticalexamplesthatuseproperty()forvalidatinginputdata,computingattributevaluesdynamically,loggingyourcode,andmore.Togetthemostoutofthistutorial,youshouldknowthebasicsofobject-orientedprogramminganddecoratorsinPython. FreeBonus:5ThoughtsOnPythonMastery,afreecourseforPythondevelopersthatshowsyoutheroadmapandthemindsetyou’llneedtotakeyourPythonskillstothenextlevel. ManagingAttributesinYourClasses Whenyoudefineaclassinanobject-orientedprogramminglanguage,you’llprobablyendupwithsomeinstanceandclassattributes.Inotherwords,you’llendupwithvariablesthatareaccessiblethroughtheinstance,class,orevenboth,dependingonthelanguage.Attributesrepresentorholdtheinternalstateofagivenobject,whichyou’lloftenneedtoaccessandmutate. Typically,youhaveatleasttwowaystomanageanattribute.Eitheryoucanaccessandmutatetheattributedirectlyoryoucanusemethods.Methodsarefunctionsattachedtoagivenclass.Theyprovidethebehaviorsandactionsthatanobjectcanperformwithitsinternaldataandattributes. Ifyouexposeyourattributestotheuser,thentheybecomepartofthepublicAPIofyourclasses.Youruserwillaccessandmutatethemdirectlyintheircode.Theproblemcomeswhenyouneedtochangetheinternalimplementationofagivenattribute. Sayyou’reworkingonaCircleclass.Theinitialimplementationhasasingleattributecalled.radius.Youfinishcodingtheclassandmakeitavailabletoyourendusers.TheystartusingCircleintheircodetocreatealotofawesomeprojectsandapplications.Goodjob! Nowsupposethatyouhaveanimportantuserthatcomestoyouwithanewrequirement.Theydon’twantCircletostoretheradiusanylonger.Theyneedapublic.diameterattribute. Atthispoint,removing.radiustostartusing.diametercouldbreakthecodeofsomeofyourendusers.Youneedtomanagethissituationinawayotherthanremoving.radius. ProgramminglanguagessuchasJavaandC++encourageyoutoneverexposeyourattributestoavoidthiskindofproblem.Instead,youshouldprovidegetterandsettermethods,alsoknownasaccessorsandmutators,respectively.ThesemethodsofferawaytochangetheinternalimplementationofyourattributeswithoutchangingyourpublicAPI. Note:Getterandsettermethodsareoftenconsideredananti-patternandasignalofpoorobject-orienteddesign.Themainargumentbehindthispropositionisthatthesemethodsbreakencapsulation.Theyallowyoutoaccessandmutatethecomponentsofyourobjects. Intheend,theselanguagesneedgetterandsettermethodsbecausetheydon’tprovideasuitablewaytochangetheinternalimplementationofanattributeifagivenrequirementchanges.ChangingtheinternalimplementationwouldrequireanAPImodification,whichcanbreakyourendusers’code. RemoveadsTheGetterandSetterApproachinPython Technically,there’snothingthatstopsyoufromusinggetterandsettermethodsinPython.Here’showthisapproachwouldlook: #point.py classPoint: def__init__(self,x,y): self._x=x self._y=y defget_x(self): returnself._x defset_x(self,value): self._x=value defget_y(self): returnself._y defset_y(self,value): self._y=value Inthisexample,youcreatePointwithtwonon-publicattributes._xand._ytoholdtheCartesiancoordinatesofthepointathand. Note:Pythondoesn’thavethenotionofaccessmodifiers,suchasprivate,protected,andpublic,torestrictaccesstoattributesandmethods.InPython,thedistinctionisbetweenpublicandnon-publicclassmembers. Ifyouwanttosignalthatagivenattributeormethodisnon-public,thenyouhavetousethewell-knownPythonconventionofprefixingthenamewithanunderscore(_).That’sthereasonbehindthenamingoftheattributes._xand._y. Notethatthisisjustaconvention.Itdoesn’tstopyouandotherprogrammersfromaccessingtheattributesusingdotnotation,asinobj._attr.However,it’sbadpracticetoviolatethisconvention. Toaccessandmutatethevalueofeither._xor._y,youcanusethecorrespondinggetterandsettermethods.GoaheadandsavetheabovedefinitionofPointinaPythonmoduleandimporttheclassintoyourinteractiveshell. Here’showyoucanworkwithPointinyourcode: >>>>>>frompointimportPoint >>>point=Point(12,5) >>>point.get_x() 12 >>>point.get_y() 5 >>>point.set_x(42) >>>point.get_x() 42 >>>#Non-publicattributesarestillaccessible >>>point._x 42 >>>point._y 5 With.get_x()and.get_y(),youcanaccessthecurrentvaluesof._xand._y.Youcanusethesettermethodtostoreanewvalueinthecorrespondingmanagedattribute.Fromthiscode,youcanconfirmthatPythondoesn’trestrictaccesstonon-publicattributes.Whetherornotyoudosoisuptoyou. ThePythonicApproach EventhoughtheexampleyoujustsawusesthePythoncodingstyle,itdoesn’tlookPythonic.Intheexample,thegetterandsettermethodsdon’tperformanyfurtherprocessingwith._xand._y.YoucanrewritePointinamoreconciseandPythonicway: >>>>>>classPoint: ...def__init__(self,x,y): ...self.x=x ...self.y=y ... >>>point=Point(12,5) >>>point.x 12 >>>point.y 5 >>>point.x=42 >>>point.x 42 Thiscodeuncoversafundamentalprinciple.ExposingattributestotheenduserisnormalandcommoninPython.Youdon’tneedtoclutteryourclasseswithgetterandsettermethodsallthetime,whichsoundsprettycool!However,howcanyouhandlerequirementchangesthatwouldseemtoinvolveAPIchanges? UnlikeJavaandC++,PythonprovideshandytoolsthatallowyoutochangetheunderlyingimplementationofyourattributeswithoutchangingyourpublicAPI.Themostpopularapproachistoturnyourattributesintoproperties. Note:Anothercommonapproachtoprovidemanagedattributesistousedescriptors.Inthistutorial,however,you’lllearnaboutproperties. Propertiesrepresentanintermediatefunctionalitybetweenaplainattribute(orfield)andamethod.Inotherwords,theyallowyoutocreatemethodsthatbehavelikeattributes.Withproperties,youcanchangehowyoucomputethetargetattributewheneveryouneedtodoso. Forexample,youcanturnboth.xand.yintoproperties.Withthischange,youcancontinueaccessingthemasattributes.You’llalsohaveanunderlyingmethodholding.xand.ythatwillallowyoutomodifytheirinternalimplementationandperformactionsonthemrightbeforeyourusersaccessandmutatethem. Note:Propertiesaren’texclusivetoPython.LanguagessuchasJavaScript,C#,Kotlin,andothersalsoprovidetoolsandtechniquesforcreatingpropertiesasclassmembers. ThemainadvantageofPythonpropertiesisthattheyallowyoutoexposeyourattributesaspartofyourpublicAPI.Ifyoueverneedtochangetheunderlyingimplementation,thenyoucanturntheattributeintoapropertyatanytimewithoutmuchpain. Inthefollowingsections,you’lllearnhowtocreatepropertiesinPython. RemoveadsGettingStartedWithPython’sproperty() Python’sproperty()isthePythonicwaytoavoidformalgetterandsettermethodsinyourcode.Thisfunctionallowsyoutoturnclassattributesintopropertiesormanagedattributes.Sinceproperty()isabuilt-infunction,youcanuseitwithoutimportinganything.Additionally,property()wasimplementedinCtoensureoptimalperformance. Note:It’scommontorefertoproperty()asabuilt-infunction.However,propertyisaclassdesignedtoworkasafunctionratherthanasaregularclass.That’swhymostPythondeveloperscallitafunction.That’salsothereasonwhyproperty()doesn’tfollowthePythonconventionfornamingclasses. Thistutorialfollowsthecommonpracticesofcallingproperty()afunctionratherthanaclass.However,insomesections,you’llseeitcalledaclasstofacilitatetheexplanation. Withproperty(),youcanattachgetterandsettermethodstogivenclassattributes.Thisway,youcanhandletheinternalimplementationforthatattributewithoutexposinggetterandsettermethodsinyourAPI.Youcanalsospecifyawaytohandleattributedeletionandprovideanappropriatedocstringforyourproperties. Here’sthefullsignatureofproperty(): property(fget=None,fset=None,fdel=None,doc=None) Thefirsttwoargumentstakefunctionobjectsthatwillplaytheroleofgetter(fget)andsetter(fset)methods.Here’sasummaryofwhateachargumentdoes: Argument Description fget Functionthatreturnsthevalueofthemanagedattribute fset Functionthatallowsyoutosetthevalueofthemanagedattribute fdel Functiontodefinehowthemanagedattributehandlesdeletion doc Stringrepresentingtheproperty’sdocstring Thereturnvalueofproperty()isthemanagedattributeitself.Ifyouaccessthemanagedattribute,asinobj.attr,thenPythonautomaticallycallsfget().Ifyouassignanewvaluetotheattribute,asinobj.attr=value,thenPythoncallsfset()usingtheinputvalueasanargument.Finally,ifyourunadelobj.attrstatement,thenPythonautomaticallycallsfdel(). Note:Thefirstthreeargumentstoproperty()takefunctionobjects.Youcanthinkofafunctionobjectasthefunctionnamewithoutthecallingpairofparentheses. Youcanusedoctoprovideanappropriatedocstringforyourproperties.YouandyourfellowprogrammerswillbeabletoreadthatdocstringusingPython’shelp().Thedocargumentisalsousefulwhenyou’reworkingwithcodeeditorsandIDEsthatsupportdocstringaccess. Youcanuseproperty()eitherasafunctionoradecoratortobuildyourproperties.Inthefollowingtwosections,you’lllearnhowtousebothapproaches.However,youshouldknowupfrontthatthedecoratorapproachismorepopularinthePythoncommunity. CreatingAttributesWithproperty() Youcancreateapropertybycallingproperty()withanappropriatesetofargumentsandassigningitsreturnvaluetoaclassattribute.Alltheargumentstoproperty()areoptional.However,youtypicallyprovideatleastasetterfunction. ThefollowingexampleshowshowtocreateaCircleclasswithahandypropertytomanageitsradius: #circle.py classCircle: def__init__(self,radius): self._radius=radius def_get_radius(self): print("Getradius") returnself._radius def_set_radius(self,value): print("Setradius") self._radius=value def_del_radius(self): print("Deleteradius") delself._radius radius=property( fget=_get_radius, fset=_set_radius, fdel=_del_radius, doc="Theradiusproperty." ) Inthiscodesnippet,youcreateCircle.Theclassinitializer,.__init__(),takesradiusasanargumentandstoresitinanon-publicattributecalled._radius.Thenyoudefinethreenon-publicmethods: ._get_radius()returnsthecurrentvalueof._radius ._set_radius()takesvalueasanargumentandassignsitto._radius ._del_radius()deletestheinstanceattribute._radius Onceyouhavethesethreemethodsinplace,youcreateaclassattributecalled.radiustostorethepropertyobject.Toinitializetheproperty,youpassthethreemethodsasargumentstoproperty().Youalsopassasuitabledocstringforyourproperty. Inthisexample,youusekeywordargumentstoimprovethecodereadabilityandpreventconfusion.Thatway,youknowexactlywhichmethodgoesintoeachargument. TogiveCircleatry,runthefollowingcodeinyourPythonshell: >>>>>>fromcircleimportCircle >>>circle=Circle(42.0) >>>circle.radius Getradius 42.0 >>>circle.radius=100.0 Setradius >>>circle.radius Getradius 100.0 >>>delcircle.radius Deleteradius >>>circle.radius Getradius Traceback(mostrecentcalllast): ... AttributeError:'Circle'objecthasnoattribute'_radius' >>>help(circle) HelponCircleinmodule__main__object: classCircle(builtins.object) ... |radius |Theradiusproperty. The.radiuspropertyhidesthenon-publicinstanceattribute._radius,whichisnowyourmanagedattributeinthisexample.Youcanaccessandassign.radiusdirectly.Internally,Pythonautomaticallycalls._get_radius()and._set_radius()whenneeded.Whenyouexecutedelcircle.radius,Pythoncalls._del_radius(),whichdeletestheunderlying._radius. UsinglambdaFunctionsasGetterMethodsShow/Hide Besidesusingregularnamedfunctionstoprovidegettermethodsinyourproperties,youcanalsouselambdafunctions. Here’saversionofCircleinwhichthe.radiuspropertyusesalambdafunctionasitsgettermethod: >>>>>>classCircle: ...def__init__(self,radius): ...self._radius=radius ...radius=property(lambdaself:self._radius) ... >>>circle=Circle(42.0) >>>circle.radius 42.0 Ifthefunctionalityofyourgettermethodislimitedtojustreturningthecurrentvalueofthemanagedattribute,thenusingalambdafunctioncanbeahandyapproach. Propertiesareclassattributesthatmanageinstanceattributes.Youcanthinkofapropertyasacollectionofmethodsbundledtogether.Ifyouinspect.radiuscarefully,thenyoucanfindtherawmethodsyouprovidedasthefget,fset,andfdelarguments: >>>>>>fromcircleimportCircle >>>Circle.radius.fget >>>Circle.radius.fset >>>Circle.radius.fdel >>>dir(Circle.radius) [...,'__get__',...,'__set__',...] Youcanaccessthegetter,setter,anddeletermethodsinagivenpropertythroughthecorresponding.fget,.fset,and.fdel. Propertiesarealsooverridingdescriptors.Ifyouusedir()tochecktheinternalmembersofagivenproperty,thenyou’llfind.__set__()and.__get__()inthelist.Thesemethodsprovideadefaultimplementationofthedescriptorprotocol. Note:Ifyouwanttobetterunderstandtheinternalimplementationofpropertyasaclass,thencheckoutthepurePythonPropertyclassdescribedinthedocumentation. Thedefaultimplementationof.__set__(),forexample,runswhenyoudon’tprovideacustomsettermethod.Inthiscase,yougetanAttributeErrorbecausethere’snowaytosettheunderlyingproperty. RemoveadsUsingproperty()asaDecorator DecoratorsareeverywhereinPython.They’refunctionsthattakeanotherfunctionasanargumentandreturnanewfunctionwithaddedfunctionality.Withadecorator,youcanattachpre-andpost-processingoperationstoanexistingfunction. WhenPython2.2introducedproperty(),thedecoratorsyntaxwasn’tavailable.Theonlywaytodefinepropertieswastopassgetter,setter,anddeletermethods,asyoulearnedbefore.ThedecoratorsyntaxwasaddedinPython2.4,andnowadays,usingproperty()asadecoratoristhemostpopularpracticeinthePythoncommunity. Thedecoratorsyntaxconsistsofplacingthenameofthedecoratorfunctionwithaleading@symbolrightbeforethedefinitionofthefunctionyouwanttodecorate: @decorator deffunc(a): returna Inthiscodefragment,@decoratorcanbeafunctionorclassintendedtodecoratefunc().Thissyntaxisequivalenttothefollowing: deffunc(a): returna func=decorator(func) Thefinallineofcodereassignsthenamefunctoholdtheresultofcallingdecorator(func).Notethatthisisthesamesyntaxyouusedtocreateapropertyinthesectionabove. Python’sproperty()canalsoworkasadecorator,soyoucanusethe@propertysyntaxtocreateyourpropertiesquickly: 1#circle.py 2 3classCircle: 4def__init__(self,radius): 5self._radius=radius 6 7@property 8defradius(self): 9"""Theradiusproperty.""" 10print("Getradius") 11returnself._radius 12 [email protected] 14defradius(self,value): 15print("Setradius") 16self._radius=value 17 [email protected] 19defradius(self): 20print("Deleteradius") 21delself._radius Thiscodelooksprettydifferentfromthegetterandsettermethodsapproach.CirclenowlooksmorePythonicandclean.Youdon’tneedtousemethodnamessuchas._get_radius(),._set_radius(),and._del_radius()anymore.Nowyouhavethreemethodswiththesamecleananddescriptiveattribute-likename.Howisthatpossible? Thedecoratorapproachforcreatingpropertiesrequiresdefiningafirstmethodusingthepublicnamefortheunderlyingmanagedattribute,whichis.radiusinthiscase.Thismethodshouldimplementthegetterlogic.Intheaboveexample,lines7to11implementthatmethod. Lines13to16definethesettermethodfor.radius.Inthiscase,thesyntaxisfairlydifferent.Insteadofusing@propertyagain,[email protected]?Takeanotherlookatthedir()output: >>>>>>dir(Circle.radius) [...,'deleter',...,'getter','setter'] Besides.fget,.fset,.fdel,andabunchofotherspecialattributesandmethods,propertyalsoprovides.deleter(),.getter(),and.setter().Thesethreemethodseachreturnanewproperty. Whenyoudecoratethesecond.radius()[email protected](line13),youcreateanewpropertyandreassigntheclass-levelname.radius(line8)toholdit.Thisnewpropertycontainsthesamesetofmethodsoftheinitialpropertyatline8withtheadditionofthenewsettermethodprovidedonline14.Finally,thedecoratorsyntaxreassignsthenewpropertytothe.radiusclass-levelname. Themechanismtodefinethedeletermethodissimilar.Thistime,[email protected],yougetafull-fledgedpropertywiththegetter,setter,anddeletermethods. Finally,howcanyouprovidesuitabledocstringsforyourpropertieswhenyouusethedecoratorapproach?IfyoucheckCircleagain,you’llnotethatyoualreadydidsobyaddingadocstringtothegettermethodonline9. ThenewCircleimplementationworksthesameastheexampleinthesectionabove: >>>>>>fromcircleimportCircle >>>circle=Circle(42.0) >>>circle.radius Getradius 42.0 >>>circle.radius=100.0 Setradius >>>circle.radius Getradius 100.0 >>>delcircle.radius Deleteradius >>>circle.radius Getradius Traceback(mostrecentcalllast): ... AttributeError:'Circle'objecthasnoattribute'_radius' >>>help(circle) HelponCircleinmodule__main__object: classCircle(builtins.object) ... |radius |Theradiusproperty. Youdon’tneedtouseapairofparenthesesforcalling.radius()asamethod.Instead,youcanaccess.radiusasyouwouldaccessaregularattribute,whichistheprimaryuseofproperties.Theyallowyoutotreatmethodsasattributes,andtheytakecareofcallingtheunderlyingsetofmethodsautomatically. Here’sarecapofsomeimportantpointstorememberwhenyou’recreatingpropertieswiththedecoratorapproach: The@propertydecoratormustdecoratethegettermethod. Thedocstringmustgointhegettermethod. Thesetteranddeletermethodsmustbedecoratedwiththenameofthegettermethodplus.setterand.deleter,respectively. Uptothispoint,you’vecreatedmanagedattributesusingproperty()asafunctionandasadecorator.IfyoucheckyourCircleimplementationssofar,thenyou’llnotethattheirgetterandsettermethodsdon’taddanyrealextraprocessingontopofyourattributes. Ingeneral,youshouldavoidturningattributesthatdon’trequireextraprocessingintoproperties.Usingpropertiesinthosesituationscanmakeyourcode: Unnecessarilyverbose Confusingtootherdevelopers Slowerthancodebasedonregularattributes Unlessyouneedsomethingmorethanbareattributeaccess,don’twriteproperties.They’reawasteofCPUtime,andmoreimportantly,they’reawasteofyourtime.Finally,youshouldavoidwritingexplicitgetterandsettermethodsandthenwrappingtheminaproperty.Instead,[email protected]’scurrentlythemostPythonicwaytogo. RemoveadsProvidingRead-OnlyAttributes Probablythemostelementaryusecaseofproperty()istoprovideread-onlyattributesinyourclasses.SayyouneedanimmutablePointclassthatdoesn’tallowtheusertomutatetheoriginalvalueofitscoordinates,xandy.Toachievethisgoal,youcancreatePointlikeinthefollowingexample: #point.py classPoint: def__init__(self,x,y): self._x=x self._y=y @property defx(self): returnself._x @property defy(self): returnself._y Here,youstoretheinputargumentsintheattributes._xand._y.Asyoualreadylearned,usingtheleadingunderscore(_)innamestellsotherdevelopersthatthey’renon-publicattributesandshouldn’tbeaccessedusingdotnotation,suchasinpoint._x.Finally,youdefinetwogettermethodsanddecoratethemwith@property. Nowyouhavetworead-onlyproperties,.xand.y,asyourcoordinates: >>>>>>frompointimportPoint >>>point=Point(12,5) >>>#Readcoordinates >>>point.x 12 >>>point.y 5 >>>#Writecoordinates >>>point.x=42 Traceback(mostrecentcalllast): ... AttributeError:can'tsetattribute Here,point.xandpoint.yarebare-boneexamplesofread-onlyproperties.Theirbehaviorreliesontheunderlyingdescriptorthatpropertyprovides.Asyoualreadysaw,thedefault.__set__()implementationraisesanAttributeErrorwhenyoudon’tdefineapropersettermethod. YoucantakethisimplementationofPointalittlebitfurtherandprovideexplicitsettermethodsthatraiseacustomexceptionwithmoreelaborateandspecificmessages: #point.py classWriteCoordinateError(Exception): pass classPoint: def__init__(self,x,y): self._x=x self._y=y @property defx(self): returnself._x @x.setter defx(self,value): raiseWriteCoordinateError("xcoordinateisread-only") @property defy(self): returnself._y @y.setter defy(self,value): raiseWriteCoordinateError("ycoordinateisread-only") Inthisexample,youdefineacustomexceptioncalledWriteCoordinateError.ThisexceptionallowsyoutocustomizethewayyouimplementyourimmutablePointclass.Now,bothsettermethodsraiseyourcustomexceptionwithamoreexplicitmessage.GoaheadandgiveyourimprovedPointatry! CreatingRead-WriteAttributes Youcanalsouseproperty()toprovidemanagedattributeswithread-writecapabilities.Inpractice,youjustneedtoprovidetheappropriategettermethod(“read”)andsettermethod(“write”)toyourpropertiesinordertocreateread-writemanagedattributes. SayyouwantyourCircleclasstohavea.diameterattribute.However,takingtheradiusandthediameterintheclassinitializerseemsunnecessarybecauseyoucancomputetheoneusingtheother.Here’saCirclethatmanages.radiusand.diameterasread-writeattributes: #circle.py importmath classCircle: def__init__(self,radius): self.radius=radius @property defradius(self): returnself._radius @radius.setter defradius(self,value): self._radius=float(value) @property defdiameter(self): returnself.radius*2 @diameter.setter defdiameter(self,value): self.radius=value/2 Here,youcreateaCircleclasswitharead-write.radius.Inthiscase,thegettermethodjustreturnstheradiusvalue.Thesettermethodconvertstheinputvaluefortheradiusandassignsittothenon-public._radius,whichisthevariableyouusetostorethefinaldata. ThereisasubtledetailtonoteinthisnewimplementationofCircleandits.radiusattribute.Inthiscase,theclassinitializerassignstheinputvaluetothe.radiuspropertydirectlyinsteadofstoringitinadedicatednon-publicattribute,suchas._radius. Why?Becauseyouneedtomakesurethateveryvalueprovidedasaradius,includingtheinitializationvalue,goesthroughthesettermethodandgetsconvertedtoafloating-pointnumber. Circlealsoimplementsa.diameterattributeasaproperty.Thegettermethodcomputesthediameterusingtheradius.Thesettermethoddoessomethingcurious.Insteadofstoringtheinputdiametervalueinadedicatedattribute,itcalculatestheradiusandwritestheresultinto.radius. Here’showyourCircleworks: >>>>>>fromcircleimportCircle >>>circle=Circle(42) >>>circle.radius 42.0 >>>circle.diameter 84.0 >>>circle.diameter=100 >>>circle.diameter 100.0 >>>circle.radius 50.0 Both.radiusand.diameterworkasnormalattributesintheseexamples,providingacleanandPythonicpublicAPIforyourCircleclass. RemoveadsProvidingWrite-OnlyAttributes Youcanalsocreatewrite-onlyattributesbytweakinghowyouimplementthegettermethodofyourproperties.Forexample,youcanmakeyourgettermethodraiseanexceptioneverytimeauseraccessestheunderlyingattributevalue. Here’sanexampleofhandlingpasswordswithawrite-onlyproperty: #users.py importhashlib importos classUser: def__init__(self,name,password): self.name=name self.password=password @property defpassword(self): raiseAttributeError("Passwordiswrite-only") @password.setter defpassword(self,plaintext): salt=os.urandom(32) self._hashed_password=hashlib.pbkdf2_hmac( "sha256",plaintext.encode("utf-8"),salt,100_000 ) TheinitializerofUsertakesausernameandapasswordasargumentsandstoresthemin.nameand.password,respectively.Youuseapropertytomanagehowyourclassprocessestheinputpassword.ThegettermethodraisesanAttributeErrorwheneverausertriestoretrievethecurrentpassword.Thisturns.passwordintoawrite-onlyattribute: >>>>>>fromusersimportUser >>>john=User("John","secret") >>>john._hashed_password b'b\xc7^ai\x9f3\xd2g...\x89^-\x92\xbe\xe6' >>>john.password Traceback(mostrecentcalllast): ... AttributeError:Passwordiswrite-only >>>john.password="supersecret" >>>john._hashed_password b'\xe9l$\x9f\xaf\x9d...b\xe8\xc8\xfcaU\r_' Inthisexample,youcreatejohnasaUserinstancewithaninitialpassword.Thesettermethodhashesthepasswordandstoresitin._hashed_password.Notethatwhenyoutrytoaccess.passworddirectly,yougetanAttributeError.Finally,assigninganewvalueto.passwordtriggersthesettermethodandcreatesanewhashedpassword. Inthesettermethodof.password,youuseos.urandom()togeneratea32-byterandomstringasyourhashingfunction’ssalt.Togeneratethehashedpassword,youusehashlib.pbkdf2_hmac().Thenyoustoretheresultinghashedpasswordinthenon-publicattribute._hashed_password.Doingsoensuresthatyouneversavetheplaintextpasswordinanyretrievableattribute. PuttingPython’sproperty()IntoAction Sofar,you’velearnedhowtousePython’sproperty()built-infunctiontocreatemanagedattributesinyourclasses.Youusedproperty()asafunctionandasadecoratorandlearnedaboutthedifferencesbetweenthesetwoapproaches.Youalsolearnedhowtocreateread-only,read-write,andwrite-onlyattributes. Inthefollowingsections,you’llcodeafewexamplesthatwillhelpyougetabetterpracticalunderstandingofcommonusecasesofproperty(). ValidatingInputValues Oneofthemostcommonusecasesofproperty()isbuildingmanagedattributesthatvalidatetheinputdatabeforestoringorevenacceptingitasasecureinput.Datavalidationisacommonrequirementincodethattakesinputfromusersorotherinformationsourcesthatyouconsideruntrusted. Python’sproperty()providesaquickandreliabletoolfordealingwithinputdatavalidation.Forexample,thinkingbacktothePointexample,youmayrequirethevaluesof.xand.ytobevalidnumbers.Sinceyourusersarefreetoenteranytypeofdata,youneedtomakesurethatyourpointonlyacceptsnumbers. Here’sanimplementationofPointthatmanagesthisrequirement: #point.py classPoint: def__init__(self,x,y): self.x=x self.y=y @property defx(self): returnself._x @x.setter defx(self,value): try: self._x=float(value) print("Validated!") exceptValueError: raiseValueError('"x"mustbeanumber')fromNone @property defy(self): returnself._y @y.setter defy(self,value): try: self._y=float(value) print("Validated!") exceptValueError: raiseValueError('"y"mustbeanumber')fromNone Thesettermethodsof.xand.yusetry…exceptblocksthatvalidateinputdatausingthePythonEAFPstyle.Ifthecalltofloat()succeeds,thentheinputdataisvalid,andyougetValidated!onyourscreen.Iffloat()raisesaValueError,thentheusergetsaValueErrorwithamorespecificmessage. Note:Intheexampleabove,youusethesyntaxraise…fromNonetohideinternaldetailsrelatedtothecontextinwhichyou’reraisingtheexception.Fromtheenduser’sviewpoint,thesedetailscanbeconfusingandmakeyourclasslookunpolished. Checkoutthesectionontheraisestatementinthedocumentationformoreinformationaboutthistopic. It’simportanttonotethatassigningthe.xand.ypropertiesdirectlyin.__init__()ensuresthatthevalidationalsooccursduringobjectinitialization.Notdoingsoisacommonmistakewhenusingproperty()fordatavalidation. Here’showyourPointclassworksnow: >>>>>>frompointimportPoint >>>point=Point(12,5) Validated! Validated! >>>point.x 12.0 >>>point.y 5.0 >>>point.x=42 Validated! >>>point.x 42.0 >>>point.y=100.0 Validated! >>>point.y 100.0 >>>point.x="one" Traceback(mostrecentcalllast): ... ValueError:"x"mustbeanumber >>>point.y="1o" Traceback(mostrecentcalllast): ... ValueError:"y"mustbeanumber Ifyouassign.xand.yvaluesthatfloat()canturnintofloating-pointnumbers,thenthevalidationissuccessful,andthevalueisaccepted.Otherwise,yougetaValueError. ThisimplementationofPointuncoversafundamentalweaknessofproperty().Didyouspotit? That’sit!Youhaverepetitivecodethatfollowsspecificpatterns.ThisrepetitionbreakstheDRY(Don’tRepeatYourself)principle,soyouwouldwanttorefactorthiscodetoavoidit.Todoso,youcanabstractouttherepetitivelogicusingadescriptor: #point.py classCoordinate: def__set_name__(self,owner,name): self._name=name def__get__(self,instance,owner): returninstance.__dict__[self._name] def__set__(self,instance,value): try: instance.__dict__[self._name]=float(value) print("Validated!") exceptValueError: raiseValueError(f'"{self._name}"mustbeanumber')fromNone classPoint: x=Coordinate() y=Coordinate() def__init__(self,x,y): self.x=x self.y=y Nowyourcodeisabitshorter.YoumanagedtoremoverepetitivecodebydefiningCoordinateasadescriptorthatmanagesyourdatavalidationinasingleplace.Thecodeworksjustlikeyourearlierimplementation.Goaheadandgiveitatry! Ingeneral,ifyoufindyourselfcopyingandpastingpropertydefinitionsallaroundyourcodeorifyouspotrepetitivecodelikeintheexampleabove,thenyoushouldconsiderusingaproperdescriptor. RemoveadsProvidingComputedAttributes Ifyouneedanattributethatbuildsitsvaluedynamicallywheneveryouaccessit,thenproperty()isthewaytogo.Thesekindsofattributesarecommonlyknownascomputedattributes.They’rehandywhenyouneedthemtolooklikeeagerattributes,butyouwantthemtobelazy. Themainreasonforcreatingeagerattributesistooptimizecomputationcostswhenyouaccesstheattributeoften.Ontheotherhand,ifyourarelyuseagivenattribute,thenalazypropertycanpostponeitscomputationuntilneeded,whichcanmakeyourprogramsmoreefficient. Here’sanexampleofhowtouseproperty()tocreateacomputedattribute.areainaRectangleclass: classRectangle: def__init__(self,width,height): self.width=width self.height=height @property defarea(self): returnself.width*self.height Inthisexample,theRectangleinitializertakeswidthandheightasargumentsandstorestheminregularinstanceattributes.Theread-onlyproperty.areacomputesandreturnstheareaofthecurrentrectangleeverytimeyouaccessit. Anothercommonusecaseofpropertiesistoprovideanauto-formattedvalueforagivenattribute: classProduct: def__init__(self,name,price): self._name=name self._price=float(price) @property defprice(self): returnf"${self._price:,.2f}" Inthisexample,.priceisapropertythatformatsandreturnsthepriceofaparticularproduct.Toprovideacurrency-likeformat,youuseanf-stringwithappropriateformattingoptions. Note:Thisexampleusesfloating-pointnumberstorepresentcurrencies,whichisbadpractice.Instead,youshouldusedecimal.Decimalfromthestandardlibrary. Asafinalexampleofcomputedattributes,sayyouhaveaPointclassthatuses.xand.yasCartesiancoordinates.Youwanttoprovidepolarcoordinatesforyourpointsothatyoucanusetheminafewcomputations.Thepolarcoordinatesystemrepresentseachpointusingthedistancetotheoriginandtheanglewiththehorizontalcoordinateaxis. Here’saCartesiancoordinatesPointclassthatalsoprovidescomputedpolarcoordinates: #point.py importmath classPoint: def__init__(self,x,y): self.x=x self.y=y @property defdistance(self): returnround(math.dist((0,0),(self.x,self.y))) @property defangle(self): returnround(math.degrees(math.atan(self.y/self.x)),1) defas_cartesian(self): returnself.x,self.y defas_polar(self): returnself.distance,self.angle ThisexampleshowshowtocomputethedistanceandangleofagivenPointobjectusingits.xand.yCartesiancoordinates.Here’showthiscodeworksinpractice: >>>>>>frompointimportPoint >>>point=Point(12,5) >>>point.x 12 >>>point.y 5 >>>point.distance 13 >>>point.angle 22.6 >>>point.as_cartesian() (12,5) >>>point.as_polar() (13,22.6) Whenitcomestoprovidingcomputedorlazyattributes,property()isaprettyhandytool.However,ifyou’recreatinganattributethatyouusefrequently,thencomputingiteverytimecanbecostlyandwasteful.Agoodstrategyistocachethemoncethecomputationisdone. CachingComputedAttributes Sometimesyouhaveagivencomputedattributethatyouusefrequently.Constantlyrepeatingthesamecomputationmaybeunnecessaryandexpensive.Toworkaroundthisproblem,youcancachethecomputedvalueandsaveitinanon-publicdedicatedattributeforfurtherreuse. Topreventunexpectedbehaviors,youneedtothinkofthemutabilityoftheinputdata.Ifyouhaveapropertythatcomputesitsvaluefromconstantinputvalues,thentheresultwillneverchange.Inthatcase,youcancomputethevaluejustonce: #circle.py fromtimeimportsleep classCircle: def__init__(self,radius): self.radius=radius self._diameter=None @property defdiameter(self): ifself._diameterisNone: sleep(0.5)#Simulateacostlycomputation self._diameter=self.radius*2 returnself._diameter EventhoughthisimplementationofCircleproperlycachesthecomputeddiameter,ithasthedrawbackthatifyoueverchangethevalueof.radius,then.diameterwon’treturnacorrectvalue: >>>>>>fromcircleimportCircle >>>circle=Circle(42.0) >>>circle.radius 42.0 >>>circle.diameter#Withdelay 84.0 >>>circle.diameter#Withoutdelay 84.0 >>>circle.radius=100.0 >>>circle.diameter#Wrongdiameter 84.0 Intheseexamples,youcreateacirclewitharadiusequalto42.0.The.diameterpropertycomputesitsvalueonlythefirsttimeyouaccessit.That’swhyyouseeadelayinthefirstexecutionandnodelayinthesecond.Notethateventhoughyouchangethevalueoftheradius,thediameterstaysthesame. Iftheinputdataforacomputedattributemutates,thenyouneedtorecalculatetheattribute: #circle.py fromtimeimportsleep classCircle: def__init__(self,radius): self.radius=radius @property defradius(self): returnself._radius @radius.setter defradius(self,value): self._diameter=None self._radius=value @property defdiameter(self): ifself._diameterisNone: sleep(0.5)#Simulateacostlycomputation self._diameter=self._radius*2 returnself._diameter Thesettermethodofthe.radiuspropertyresets._diametertoNoneeverytimeyouchangethevalueoftheradius.Withthislittleupdate,.diameterrecalculatesitsvaluethefirsttimeyouaccessitaftereverymutationof.radius: >>>>>>fromcircleimportCircle >>>circle=Circle(42.0) >>>circle.radius 42.0 >>>circle.diameter#Withdelay 84.0 >>>circle.diameter#Withoutdelay 84.0 >>>circle.radius=100.0 >>>circle.diameter#Withdelay 200.0 >>>circle.diameter#Withoutdelay 200.0 Cool!Circleworkscorrectlynow!Itcomputesthediameterthefirsttimeyouaccessitandalsoeverytimeyouchangetheradius. Anotheroptiontocreatecachedpropertiesistousefunctools.cached_property()fromthestandardlibrary.Thisfunctionworksasadecoratorthatallowsyoutotransformamethodintoacachedproperty.Thepropertycomputesitsvalueonlyonceandcachesitasanormalattributeduringthelifetimeoftheinstance: #circle.py fromfunctoolsimportcached_property fromtimeimportsleep classCircle: def__init__(self,radius): self.radius=radius @cached_property defdiameter(self): sleep(0.5)#Simulateacostlycomputation returnself.radius*2 Here,.diametercomputesandcachesitsvaluethefirsttimeyouaccessit.Thiskindofimplementationissuitableforthosecomputationsinwhichtheinputvaluesdon’tmutate.Here’showitworks: >>>>>>fromcircleimportCircle >>>circle=Circle(42.0) >>>circle.diameter#Withdelay 84.0 >>>circle.diameter#Withoutdelay 84.0 >>>circle.radius=100 >>>circle.diameter#Wrongdiameter 84.0 >>>#Allowdirectassignment >>>circle.diameter=200 >>>circle.diameter#Cachedvalue 200 Whenyouaccess.diameter,yougetitscomputedvalue.Thatvalueremainsthesamefromthispointon.However,unlikeproperty(),cached_property()doesn’tblockattributemutationsunlessyouprovideapropersettermethod.That’swhyyoucanupdatethediameterto200inthelastcoupleoflines. Ifyouwanttocreateacachedpropertythatdoesn’tallowmodification,thenyoucanuseproperty()andfunctools.cache()likeinthefollowingexample: #circle.py fromfunctoolsimportcache fromtimeimportsleep classCircle: def__init__(self,radius): self.radius=radius @property @cache defdiameter(self): sleep(0.5)#Simulateacostlycomputation returnself.radius*2 Thiscodestacks@propertyontopof@cache.Thecombinationofbothdecoratorsbuildsacachedpropertythatpreventsmutations: >>>>>>fromcircleimportCircle >>>circle=Circle(42.0) >>>circle.diameter#Withdelay 84.0 >>>circle.diameter#Withoutdelay 84.0 >>>circle.radius=100 >>>circle.diameter 84.0 >>>circle.diameter=200 Traceback(mostrecentcalllast): ... AttributeError:can'tsetattribute Intheseexamples,whenyoutrytoassignanewvalueto.diameter,yougetanAttributeErrorbecausethesetterfunctionalitycomesfromtheinternaldescriptorofproperty. RemoveadsLoggingAttributeAccessandMutation Sometimesyouneedtokeeptrackofwhatyourcodedoesandhowyourprogramsflow.AwaytodothatinPythonistouselogging.Thismoduleprovidesallthefunctionalityyouwouldrequireforloggingyourcode.It’llallowyoutoconstantlywatchthecodeandgenerateusefulinformationabouthowitworks. Ifyoueverneedtokeeptrackofhowandwhenyouaccessandmutateagivenattribute,thenyoucantakeadvantageofproperty()forthat,too: #circle.py importlogging logging.basicConfig( format="%(asctime)s:%(message)s", level=logging.INFO, datefmt="%H:%M:%S" ) classCircle: def__init__(self,radius): self._msg='"radius"was%s.Currentvalue:%s' self.radius=radius @property defradius(self): """Theradiusproperty.""" logging.info(self._msg%("accessed",str(self._radius))) returnself._radius @radius.setter defradius(self,value): try: self._radius=float(value) logging.info(self._msg%("mutated",str(self._radius))) exceptValueError: logging.info('validationerrorwhilemutating"radius"') Here,youfirstimportlogginganddefineabasicconfiguration.ThenyouimplementCirclewithamanagedattribute.radius.Thegettermethodgeneratesloginformationeverytimeyouaccess.radiusinyourcode.Thesettermethodlogseachmutationthatyouperformon.radius.Italsologsthosesituationsinwhichyougetanerrorbecauseofbadinputdata. Here’showyoucanuseCircleinyourcode: >>>>>>fromcircleimportCircle >>>circle=Circle(42.0) >>>circle.radius 14:48:59:"radius"wasaccessed.Currentvalue:42.0 42.0 >>>circle.radius=100 14:49:15:"radius"wasmutated.Currentvalue:100 >>>circle.radius 14:49:24:"radius"wasaccessed.Currentvalue:100 100 >>>circle.radius="value" 15:04:51:validationerrorwhilemutating"radius" Loggingusefuldatafromattributeaccessandmutationcanhelpyoudebugyourcode.Loggingcanalsohelpyouidentifysourcesofproblematicdatainput,analyzetheperformanceofyourcode,spotusagepatterns,andmore. ManagingAttributeDeletion Youcanalsocreatepropertiesthatimplementdeletingfunctionality.Thismightbearareusecaseofproperty(),buthavingawaytodeleteanattributecanbehandyinsomesituations. Sayyou’reimplementingyourowntreedatatype.Atreeisanabstractdatatypethatstoreselementsinahierarchy.Thetreecomponentsarecommonlyknownasnodes.Eachnodeinatreehasaparentnode,exceptfortherootnode.Nodescanhavezeroormorechildren. Nowsupposeyouneedtoprovideawaytodeleteorclearthelistofchildrenofagivennode.Here’sanexamplethatimplementsatreenodethatusesproperty()toprovidemostofitsfunctionality,includingtheabilitytoclearthelistofchildrenofthenodeathand: #tree.py classTreeNode: def__init__(self,data): self._data=data self._children=[] @property defchildren(self): returnself._children @children.setter defchildren(self,value): ifisinstance(value,list): self._children=value else: delself.children self._children.append(value) @children.deleter defchildren(self): self._children.clear() def__repr__(self): returnf'{self.__class__.__name__}("{self._data}")' Inthisexample,TreeNoderepresentsanodeinyourcustomtreedatatype.EachnodestoresitschildreninaPythonlist.Thenyouimplement.childrenasapropertytomanagetheunderlyinglistofchildren.Thedeletermethodcalls.clear()onthelistofchildrentoremovethemall: >>>>>>fromtreeimportTreeNode >>>root=TreeNode("root") >>>child1=TreeNode("child1") >>>child2=TreeNode("child2") >>>root.children=[child1,child2] >>>root.children [TreeNode("child1"),TreeNode("child2")] >>>delroot.children >>>root.children [] Here,youfirstcreatearootnodetostartpopulatingthetree.Thenyoucreatetwonewnodesandassignthemto.childrenusingalist.Thedelstatementtriggerstheinternaldeletermethodof.childrenandclearsthelist. CreatingBackward-CompatibleClassAPIs Asyoualreadyknow,propertiesturnmethodcallsintodirectattributelookups.ThisfeatureallowsyoutocreatecleanandPythonicAPIsforyourclasses.Youcanexposeyourattributespubliclywithouttheneedforgetterandsettermethods. Ifyoueverneedtomodifyhowyoucomputeagivenpublicattribute,thenyoucanturnitintoaproperty.Propertiesmakeitpossibletoperformextraprocessing,suchasdatavalidation,withouthavingtomodifyyourpublicAPIs. Supposeyou’recreatinganaccountingapplicationandyouneedabaseclasstomanagecurrencies.Tothisend,youcreateaCurrencyclassthatexposestwoattributes,.unitsand.cents: classCurrency: def__init__(self,units,cents): self.units=units self.cents=cents #Currencyimplementation... ThisclasslookscleanandPythonic.Nowsaythatyourrequirementschange,andyoudecidetostorethetotalnumberofcentsinsteadoftheunitsandcents.Removing.unitsand.centsfromyourpublicAPItousesomethinglike.total_centswouldbreakmorethanoneclient’scode. Inthissituation,property()canbeanexcellentoptiontokeepyourcurrentAPIunchanged.Here’showyoucanworkaroundtheproblemandavoidbreakingyourclients’code: #currency.py CENTS_PER_UNIT=100 classCurrency: def__init__(self,units,cents): self._total_cents=units*CENTS_PER_UNIT+cents @property defunits(self): returnself._total_cents//CENTS_PER_UNIT @units.setter defunits(self,value): self._total_cents=self.cents+value*CENTS_PER_UNIT @property defcents(self): returnself._total_cents%CENTS_PER_UNIT @cents.setter defcents(self,value): self._total_cents=self.units*CENTS_PER_UNIT+value #Currencyimplementation... Nowyourclassstoresthetotalnumberofcentsinsteadofindependentunitsandcents.However,youruserscanstillaccessandmutate.unitsand.centsintheircodeandgetthesameresultasbefore.Goaheadandgiveitatry! Whenyouwritesomethinguponwhichmanypeoplearegoingtobuild,youneedtoguaranteethatmodificationstotheinternalimplementationdon’taffecthowendusersworkwithyourclasses. RemoveadsOverridingPropertiesinSubclasses WhenyoucreatePythonclassesthatincludepropertiesandreleasetheminapackageorlibrary,youshouldexpectyouruserstodoalotofdifferentthingswiththem.Oneofthosethingscouldbesubclassingthemtocustomizetheirfunctionalities.Inthesecases,yourusershavetobecarefulandbeawareofasubtlegotcha.Ifyoupartiallyoverrideaproperty,thenyoulosethenon-overriddenfunctionality. Forexample,supposeyou’recodinganEmployeeclasstomanageemployeeinformationinyourcompany’sinternalaccountingsystem.YoualreadyhaveaclasscalledPerson,andyouthinkaboutsubclassingittoreuseitsfunctionalities. Personhasa.nameattributeimplementedasaproperty.Thecurrentimplementationof.namedoesn’tmeettherequirementofreturningthenameinuppercaseletters.Here’showyouendupsolvingthis: #persons.py classPerson: def__init__(self,name): self._name=name @property defname(self): returnself._name @name.setter defname(self,value): self._name=value #Personimplementation... classEmployee(Person): @property defname(self): returnsuper().name.upper() #Employeeimplementation... InEmployee,youoverride.nametomakesurethatwhenyouaccesstheattribute,yougettheemployeenameinuppercase: >>>>>>frompersonsimportEmployee,Person >>>person=Person("John") >>>person.name 'John' >>>person.name="JohnDoe" >>>person.name 'JohnDoe' >>>employee=Employee("John") >>>employee.name 'JOHN' Great!Employeeworksasyouneed!Itreturnsthenameusinguppercaseletters.However,subsequenttestsuncoveranunexpectedbehavior: >>>>>>employee.name="JohnDoe" Traceback(mostrecentcalllast): ... AttributeError:can'tsetattribute Whathappened?Well,whenyouoverrideanexistingpropertyfromaparentclass,youoverridethewholefunctionalityofthatproperty.Inthisexample,youreimplementedthegettermethodonly.Becauseofthat,.namelosttherestofthefunctionalityfromthebaseclass.Youdon’thaveasettermethodanylonger. Theideaisthatifyoueverneedtooverrideapropertyinasubclass,thenyoushouldprovideallthefunctionalityyouneedinthenewversionofthepropertyathand. Conclusion Apropertyisaspecialtypeofclassmemberthatprovidesfunctionalitythat’ssomewhereinbetweenregularattributesandmethods.PropertiesallowyoutomodifytheimplementationofinstanceattributeswithoutchangingthepublicAPIoftheclass.BeingabletokeepyourAPIsunchangedhelpsyouavoidbreakingcodeyouruserswroteontopofolderversionsofyourclasses. PropertiesarethePythonicwaytocreatemanagedattributesinyourclasses.Theyhaveseveralusecasesinreal-worldprogramming,makingthemagreatadditiontoyourskillsetasaPythondeveloper. Inthistutorial,youlearnedhowto: CreatemanagedattributeswithPython’sproperty() Performlazyattributeevaluationandprovidecomputedattributes Avoidsetterandgettermethodswithproperties Createread-only,read-write,andwrite-onlyattributes Createconsistentandbackward-compatibleAPIsforyourclasses Youalsowroteseveralpracticalexamplesthatwalkedyouthroughthemostcommonusecasesofproperty().Thoseexamplesincludeinputdatavalidation,computedattributes,loggingyourcode,andmore. MarkasCompleted 🐍PythonTricks💌 Getashort&sweetPythonTrickdeliveredtoyourinboxeverycoupleofdays.Nospamever.Unsubscribeanytime.CuratedbytheRealPythonteam. SendMePythonTricks» AboutLeodanisPozoRamos LeodanisisanindustrialengineerwholovesPythonandsoftwaredevelopment.He'saself-taughtPythondeveloperwith6+yearsofexperience.He'sanavidtechnicalwriterwithagrowingnumberofarticlespublishedonRealPythonandothersites. »MoreaboutLeodanis EachtutorialatRealPythoniscreatedbyateamofdeveloperssothatitmeetsourhighqualitystandards.Theteammemberswhoworkedonthistutorialare: Aldren Bartosz Martin Sadie MasterReal-WorldPythonSkillsWithUnlimitedAccesstoReal Python Joinusandgetaccesstohundredsoftutorials,hands-onvideocourses,andacommunityofexpert Pythonistas: LevelUpYourPythonSkills» MasterReal-WorldPythonSkillsWithUnlimitedAccesstoReal Python Joinusandgetaccesstohundredsoftutorials,hands-onvideocourses,andacommunityofexpertPythonistas: LevelUpYourPythonSkills» WhatDoYouThink? Tweet Share Email RealPythonCommentPolicy:Themostusefulcommentsarethosewrittenwiththegoaloflearningfromorhelpingoutotherreaders—afterreadingthewholearticleandalltheearliercomments.Complaintsandinsultsgenerallywon’tmakethecuthere. What’syour#1takeawayorfavoritethingyoulearned?Howareyougoingtoputyournewfoundskillstouse?Leaveacommentbelowandletusknow. KeepLearning RelatedTutorialCategories: best-practices intermediate python KeepreadingReal Pythonbycreatingafreeaccountorsigning in: Continue» Alreadyhaveanaccount?Sign-In —FREEEmailSeries— 🐍PythonTricks💌 GetPythonTricks» 🔒Nospam.Unsubscribeanytime. AllTutorialTopics advanced api basics best-practices community databases data-science devops django docker flask front-end gamedev gui intermediate machine-learning projects python testing tools web-dev web-scraping TableofContents ManagingAttributesinYourClasses TheGetterandSetterApproachinPython ThePythonicApproach GettingStartedWithPython’sproperty() CreatingAttributesWithproperty() Usingproperty()asaDecorator ProvidingRead-OnlyAttributes CreatingRead-WriteAttributes ProvidingWrite-OnlyAttributes PuttingPython’sproperty()IntoAction ValidatingInputValues ProvidingComputedAttributes CachingComputedAttributes LoggingAttributeAccessandMutation ManagingAttributeDeletion CreatingBackward-CompatibleClassAPIs OverridingPropertiesinSubclasses Conclusion MarkasCompleted Tweet Share Email Almostthere!Completethisformandclickthebuttonbelowtogaininstantaccess: × 5ThoughtsOnPythonMastery StarttheClass»



請為這篇文章評分?