Python 装饰器之Property: Setter 和Getter

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

Unfortunately, it is widespread belief that a proper Python class should encapsulate private attributes by using getters and setters. Search AQuestAfterPerspectives AQuestAfterPerspectives Home Projects Tags About Contents Demo Case1 Case2 Case3 Case4 Reference Python装饰器之Property:Setter和Getter Propertyvs.GetterandSetterinPythondecorators Herb Lastupdatedon Feb15,2021 4minread Tutorial Project Follow Follow Getters(alsoknownas‘accessors’)andsetters(aka.‘mutators’)areusedinmanyobjectorientedprogramminglanguagestoensuretheprincipleofdataencapsulation.Dataencapsulation-asyoucanlearninaintroductiononObjectOrientedProgrammingofthetutorial-isseenasthebundlingofdatawiththemethodsthatoperateonthem.Thesemethodsareofcoursethegetterforretrievingthedataandthesetterforchangingthedata.Accordingtothisprinciple,theattributesofaclassaremadeprivatetohideandprotectthemfromtheothercodes. Unfortunately,itiswidespreadbeliefthataproperPythonclassshouldencapsulateprivateattributesbyusinggettersandsetters.Assoonasoneoftheseprogrammersintroducesanewattribute,heorshewillmakeitaprivatevariableandcreates“automatically”agetterandasetterforthisattributes.SuchprogrammersmayevenuseaneditororanIDE,whichautomaticallycreatesgettersandsettersforallprivateattributes.Thesetoolsevenwarntheprogrammerifsheorheusesapublicattribute!Javaprogrammerswillwrinkletheirbrows,screwuptheirnoses,orevenscreamwithhorrorwhentheyreadthefollowing:ThePythonicwaytointroduceattributesistomakethempublic. Source:Propertiesvs.GettersandSetters Using@propertydecoratorstoachievegettersandsettersbehaviour. Demo 用一个简单例子来开局,体会一般: classPerson: def__init__(self,name): self.name1=name self.name2='小白' #利用property装饰器将获取name方法转换为获取对象的属性 @property defname(self): returnself.name1+'!' #利用property装饰器将设置name方法转换为获取对象的属性 @name.setter#@属性名.setter defname3(self,n): self.name1='小绿'ifn=='小灰'else'小宝' p=Person('小黑') print(p.name,p.name1,p.name2,p.name3) p.name3='小灰' print(p.name,p.name1,p.name2,p.name3) p.name3='小2' print(p.name,p.name1,p.name2,p.name3) p.name='123' Output: 小黑!小黑小白小黑! 小绿!小绿小白小绿! 小宝!小宝小白小宝! --------------------------------------------------------------------------- AttributeErrorTraceback(mostrecentcalllast) in ---->1p.name='123' AttributeError:can'tsetattribute 上图中的例子,我们可以直观的感受到@property装饰器将调用方法改为了调用对象,即p.name()改为了p.name。

另外,@name.setter装饰器不仅将调用方法改为了获取指定对象的属性,即p.name3对应于p.name()亦p.name。

此外,对其赋值时相当于调用了方法,即有p.name3=n对应于p.name3(n)。

值得留意的是,上述例子背后其实是在操作私有属性p.name,使用者是透过setter方法来管理输入的值,并对p.name等属性参数进行赋值影响,直接对私有属性p.name进行赋值是不被允许的。

了解了其背后的执行逻辑和规律以后,下面给几个标准写法和实例: Case1 我们可以用其来对属性的赋值做判断和异常检测。

#Pythonprogramshowingtheuseof #@propertyfromhttps://www.geeksforgeeks.org/getter-and-setter-in-python/ classGeeks: def__init__(self): self._age=0 #usingpropertydecorator #agetterfunction @property defage(self): print("gettermethodcalled") returnself._age #asetterfunction @age.setter defage(self,a): if(a<18): raiseValueError("Sorryyouageisbeloweligibilitycriteria") print("settermethodcalled") self._age=a Case2 另一种写法就是可以将setter和getter作为私有方法隐藏起来: #https://www.datacamp.com/community/tutorials/property-getters-setters classFinalClass: def__init__(self,var): ##callingtheset_a()methodtosetthevalue'a'bycheckingcertainconditions self.__set_a(var) ##gettermethodtogetthepropertiesusinganobject def__get_a(self): returnself.__a ##settermethodtochangethevalue'a'usinganobject def__set_a(self,var): ##conditiontocheckwhethervarissuitableornot ifvar>0andvar%2==0: self.__a=var else: self.__a=2 a=property(__get_a,__set_a) Case3 这个例子来自stackoverflow上的回答,可以参考其是如何避免delete受保护的属性。

#https://stackoverflow.com/a/36943813/8656360 classProtective(object): """protectedpropertydemo""" # def__init__(self,start_protected_value=0): self.protected_value=start_protected_value # @property defprotected_value(self): returnself._protected_value # @protected_value.setter defprotected_value(self,value): ifvalue!=int(value): raiseTypeError("protected_valuemustbeaninteger") if0<=value<=100: self._protected_value=int(value) else: raiseValueError("protected_valuemustbe"+ "between0and100inclusive") # @protected_value.deleter defprotected_value(self): raiseAttributeError("donotdelete,protected_valuecanbesetto0") Output: >>>p1=Protective(3) >>>p1.protected_value 3 >>>p1=Protective(5.0) >>>p1.protected_value 5 >>>p2=Protective(-5) Traceback(mostrecentcalllast): File"",line1,in File"",line3,in__init__ File"",line15,inprotected_value ValueError:protectected_valuemustbebetween0and100inclusive >>>p1.protected_value=7.3 Traceback(mostrecentcalllast): File"",line1,in File"",line17,inprotected_value TypeError:protected_valuemustbeaninteger >>>p1.protected_value=101 Traceback(mostrecentcalllast): File"",line1,in File"",line15,inprotected_value ValueError:protectected_valuemustbebetween0and100inclusive >>>delp1.protected_value Traceback(mostrecentcalllast): File"",line1,in File"",line18,inprotected_value AttributeError:donotdelete,protected_valuecanbesetto0 Case4 最后一个例子非常有趣,发现可以利用Property下的Setter和Getter决定多种属性之间的动态依赖关系。

FYI:在信号处理中,时域下的采样率sampling_rate,时长time_duration和采样点总数Nt三个变量中是任意两个可以推导出第三个变量。

#https://github.com/stephengreen/lfi-gw/blob/11cd4f650af793db45ebc78892443cc2b0b60f40/lfigw/waveform_generator.py#L250 classDSP(): def__init__(self,): self.sampling_rate=1024 self.time_duration=8 @property deff_max(self): """Setthemaximumfrequencytohalfthesamplingrate.""" returnself.sampling_rate/2.0 @f_max.setter deff_max(self,f_max): self.sampling_rate=2.0*f_max @property defdelta_t(self): return1.0/self.sampling_rate @delta_t.setter defdelta_t(self,delta_t): self.sampling_rate=1.0/delta_t @property defdelta_f(self): return1.0/self.time_duration @delta_f.setter defdelta_f(self,delta_f): self.time_duration=1.0/delta_f @property defNt(self): returnint(self.time_duration*self.sampling_rate) @property defNf(self): returnint(self.f_max/self.delta_f)+1 defOUTPUT(): print('-'*20+'''\nsampling_rate:{} time_duration:{} f_max:{} delta_t:{} delta_f:{} Nt:{} Nf:{}\n'''.format(t.sampling_rate,t.time_duration,t.f_max,t.delta_t,t.delta_f,t.Nt,t.Nf)+'-'*20) Output: >>>t=DSP() >>>OUTPUT() -------------------- sampling_rate:1024 time_duration:8 f_max:512.0 delta_t:0.0009765625 delta_f:0.125 Nt:8192 Nf:4097 -------------------- >>>t.f_max=256 >>>OUTPUT() -------------------- sampling_rate:512.0 time_duration:8 f_max:256.0 delta_t:0.001953125 delta_f:0.125 Nt:4096 Nf:2049 -------------------- >>>t.delta_t=1/8192 >>>OUTPUT() -------------------- sampling_rate:8192.0 time_duration:8 f_max:4096.0 delta_t:0.0001220703125 delta_f:0.125 Nt:65536 Nf:32769 -------------------- Reference 此文探讨非常浅显,仅简明扼要,更多的讨论素材和教程细节,可参阅下方的参考文献: property和setter装饰器-cnblogs [Python]setter和getter Propertiesvs.GettersandSetters Python@propertydecorator-Programiz Propertyvs.GettersandSettersinPython GetterandSetterinPython AGreatAnswerfor“What’sthepythonicwaytousegettersandsetters?”-stackoverflow ThePythonStandardLibrary-Built-inFunctions-classproperty Python SignalProcessing Next Unit3:Structure&Paragraphs(学术写作) Previous Unit2:Verbs(学术写作) Related 谱分析(spectralanalysis)的SciPy代码解析 Python中负数取余问题 恒Q变换(Constant-Qtransform) S变换(Stockweltransform) 傅里叶变换算法及其python实现 Cite × Copy Download



請為這篇文章評分?