python面对对象编程-------5:获取属性的四种办法:@property, __setattr__(__getattr__) ,descriptor...
一:最基本的屬性操作
1 class Generic: 2 pass 3 4 g= Generic() 5 6 >>> g.attribute= "value" #創(chuàng)建屬性并賦值 7 >>> g.attribute 8 'value' 9 >>> g.unset 10 Traceback (most recent call last): 11 File "<stdin>", line 1, in <module> 12 AttributeError: 'Generic' object has no attribute 'unset' 13 >>> del g.attribute #注意,此地時直接刪除了這個屬性 14 >>> g.attribute 15 Traceback (most recent call last): 16 File "<stdin>", line 1, in <module> 17 AttributeError: 'Generic' object has no attribute 'attribute' 基本的屬性操作?
二:@property
被@property修飾的是一個方法,但此方法名可以像屬性一樣被獲取,設置,刪除需要注意的是,屬性的外部添加是十分簡單的,但property的外部添加不是,所以其與屬性還是有區(qū)別的
有兩種方法創(chuàng)建property:
1:用@property修飾的函數(shù)
2:用property()方法
有兩種設計模式:
1:懶惰計算模式:被調用時才執(zhí)行
2:主動計算模式:實例化時就執(zhí)行
懶惰模式:計算手牌總和 1 class Hand_Lazy(Hand): 2 def __init__( self, dealer_card, *cards ): 3 self.dealer_card= dealer_card 4 self._cards= list(cards) 5 @property 6 def total( self ): 7 delta_soft = max(c.soft-c.hard for c in self._cards) 8 hard_total = sum(c.hard for c in self._cards) 9 if hard_total+delta_soft <= 21: 10 return hard_total+delta_soft 11 return hard_total 12 @property 13 def card( self ): 14 return self._cards 15 @card.setter 16 def card( self, aCard ): 17 self._cards.append( aCard ) 18 @card.deleter 19 def card( self ): 20 self._cards.pop(-1) 21 22 23 d= Deck() 24 h= Hand_Lazy( d.pop(), d.pop(), d.pop() ) 25 h.total #被調用時才執(zhí)行計算手頭的牌之和 26 # 19 27 h.card = d.pop() #注意,可以將@property看作@property.getter,而此地可以看作兩步,左邊為@property獲取屬性,=號調用@property.setter并且將右邊的d.pop()當作參數(shù)傳入。 28 h.total 29 # 29 懶惰模式
主動計算模式
1 # 將計算嵌入到@card.setter中,每新添加一張手牌就立馬更新手牌總和 2 class Hand_Eager(Hand): 3 def __init__( self, dealer_card, *cards ): 4 self.dealer_card= dealer_card 5 self.total= 0 6 self._delta_soft= 0 7 self._hard_total= 0 8 self._cards= list() 9 for c in cards: 10 self.card = c 11 @property 12 def card( self ): 13 return self._cards 14 @card.setter 15 def card( self, aCard ): 16 self._cards.append(aCard) 17 self._delta_soft = max(aCard.soft-aCard.hard,self._delta_soft) 18 self._hard_total += aCard.hard 19 self._set_total() 20 @card.deleter 21 def card( self ): 22 removed= self._cards.pop(-1) 23 self._hard_total -= removed.hard 24 # Issue: was this the only ace? 25 self._delta_soft = max( c.soft-c.hard for c in self._cards) 26 self._set_total() 27 28 def _set_total( self ): 29 if self._hard_total+self._delta_soft <= 21: 30 self.total= self._hard_total+self._delta_soft 31 else: 32 self.total= self._hard_total 33 34 d= Deck() 35 h1= Hand_Lazy( d.pop(), d.pop(), d.pop() ) 36 print( h1.total ) 37 h2= Hand_Eager( d.pop(), d.pop(), d.pop() ) 38 print( h2.total ) 主動計算模式其實@property已經(jīng)模糊了數(shù)據(jù)和行為了,那么到底什么時候我們需要使用@property呢?
1:需要使用類中其他屬性計算得到【也就是上面的情況】
2:對于難以查找或者計算的東西,將這個值以私有屬性的形式緩存到本地,而后再次訪問就快捷很多:
1 from urllib.request import urlopen 2 class WebPage: 3 def __init__(self,url): 4 self.url = url 5 self._content = None 6 7 @property 8 def content(self): 9 if not self._content: 10 print("retriving new page") 11 self._content = urlopen(self.url).read() 12 13 return self._content 14 15 import time 16 webpage = WebPage("http://ccphillips.net/") 17 now = time.time() 18 content1 = webpage.content 19 print(time.time()-now) 20 now = time.time() 21 content2 = webpage.content 22 print(time.time()-now) 23 24 輸出: 25 retriving new page 26 14.51249384880066 27 0.0 #!!!! 用于緩存內容補充:廖雪峰的關于@property片段的代碼
1 class Student: 2 def get_score(self): 3 return self._score 4 5 def set_score(self,value): 6 if not isinstance(value,int): 7 raise ValueError('must be integer') 8 if value < 0 or value > 100: 9 raise ValueError('0~100') 10 self._score = value 11 12 s=Student() 13 s.set_score(60) 14 s.get_score() 15 # 60 16 17 # 用@property優(yōu)化: 18 # 注意,可以把@property看作getter,而setter與deletter都是基于getter的 19 class Student: 20 @property 21 def score(self): 22 return self._score 23 24 @score.setter 25 def score(self,value): 26 if not isinstance(value,int): 27 raise ValueError('must be integer') 28 if value < 0 or value > 100: 29 raise ValueError('0~100') 30 self._score = value 31 32 s=Student() 33 s.score = 60 34 s.score 35 # 60 廖雪峰@property?
三:屬性獲取的特殊方法
__getattr__(), __setattr__(), and __delattr__(),__dir__(),__getattribute__()__setattr__(): 創(chuàng)建屬性并賦值
__getattr__(): 首先:如果此屬性已有值,不會用到__getattr__(),直接返回值就是了。
其次:如果此屬性沒有值,此時調用__getattr__()并且返回其中設定的返回值。
最后:如果壓根沒有這屬性,報 AttributeError 錯誤。
__delattr__():刪除一個屬性
__dir__(): 返回包含屬性的list
__getattribute__():更加底層的屬性獲取方法,他默認從__dict__(或__slots__)中獲取值,如果沒有找到,調用__getattr__()作為反饋。如果發(fā)現(xiàn)此值是一個dexcriptor,就調用descriptor,否者就直接返回值。 __getattr__()方法只當某個屬性沒有值時才起作用。
1:創(chuàng)建immutable object
什么是immutable object:不能夠在外部直接賦值一個已有屬性的值,不能創(chuàng)建新屬性
immutable object的一個特點是__hash__()能夠返回固定的值
版本一:用__slots__創(chuàng)建immutable object: 1 class BlackJackCard: 2 """Abstract Superclass""" 3 __slots__ = ( 'rank', 'suit', 'hard', 'soft' ) #__slots__限定了只有這些屬性可用 4 def __init__( self, rank, suit, hard, soft ): 5 super().__setattr__( 'rank', rank ) 6 super().__setattr__( 'suit', suit ) 7 super().__setattr__( 'hard', hard ) 8 super().__setattr__( 'soft', soft ) 9 def __str__( self ): 10 return "{0.rank}{0.suit}".format( self ) 11 def __setattr__( self, name, value ): 12 raise AttributeError( "'{__class__.__name__}' has no attribute '{name}'".format( __class__= self.__class__, name= name ) ) 13 14 # We defined __setattr__() to raise an exception rather than do anything useful. 15 # __init__() use the superclass version of __setattr__() so that values can be properly set in spite of the absence of a working __setattr__() method in this class. 16 # 我們知道,python并不阻止人干壞事,所以可以通過 object.__setattr__(c, 'bad', 5) 來繞過immutable機制 __slots__創(chuàng)建immutable object
版本2:?我們還可以通過繼承 tuple 并且覆蓋__getattr__()來寫immutable object。
1 class BlackJackCard2( tuple ): 2 def __new__( cls, rank, suit, hard, soft ): # tuple(iterable) -> tuple initialized from iterable's items 3 return super().__new__( cls, (rank, suit, hard, soft) ) 4 5 def __getattr__( self, name ): #translate __getattr__(name) requests to self[index] requests 6 return self[{'rank':0, 'suit':1, 'hard':2 , 'soft':3}[name]] 7 8 def __setattr__( self, name, value ): 9 raise AttributeError 10 11 >>> d = BlackJackCard2( 'A', '?', 1, 11 ) 12 >>> d.rank 13 'A' 14 >>> d.suit 15 '?' 16 >>> d.bad= 2 #不能改變屬性值了 17 Traceback (most recent call last): 18 File "<stdin>", line 1, in <module> 19 File "<stdin>", line 7, in __setattr__AttributeError 繼承tuple實現(xiàn)immutable object # 注意上面兩個版本是有區(qū)別的,在版本2中可以通過d.__dict__來增加屬性# 而版本1中用了__slots__后就會關閉__dict__
2:創(chuàng)建一個一旦給定速度與時間就自動更新距離的類,讓其繼承自dict,好處是用format函數(shù)特別方便 1 class RateTimeDistance( dict ): 2 def __init__( self, *args, **kw ): 3 super().__init__( *args, **kw ) 4 self._solve() 5 def __getattr__( self, name ): 6 return self.get(name,None) #對應字典的get方法 7 def __setattr__( self, name, value ): 8 self[name]= value #對應字典的賦值方法 9 self._solve() #在__setattr__中調用方法既是一旦賦值就能能夠完成計算 10 def __dir__( self ): 11 return list(self.keys()) 12 def _solve(self): 13 if self.rate is not None and self.time is not None: 14 self['distance'] = self.rate*self.time 15 elif self.rate is not None and self.distance is not None: 16 self['time'] = self.distance / self.rate 17 elif self.time is not None and self.distance is not None: 18 self['rate'] = self.distance / self.time 19 20 >>> rtd= RateTimeDistance( rate=6.3, time=8.25, distance=None ) 21 >>> print( "Rate={rate}, Time={time}, Distance={distance}".format(**rtd ) ) 22 Rate=6.3, Time=8.25, Distance=51.975 23 # It's also important to note that once all three values are set, this object can't be changed to provide new solutions easily. 24 # 上面有個bug在于,一旦我們想改變時間,這時發(fā)現(xiàn)速度與距離至少其一一定會變,按代碼順序是改變了距離,而如果我們不想改變距離而是改變速度就不行了 25 # 或者是兩個都不想改變,唯一的辦法不改變其中一個就是先把一個值設為None 26 27 # 解決辦法:design a model that tracked the order that the variables were set in 28 # this model could save us from having to clear one variable before setting another to recompute a related result. 綜合__settattr__,__getattr__,__dir__以及主動計算
?
3:The __getattribute__() method
總的來說,幾乎沒必要用__getattribute__(),其默認的方法已近夠強大了,況且?guī)缀跛形覀冃枰亩寄軌蛲ㄟ^__getattr__()實現(xiàn)。
1 class BlackJackCard3: 2 """Abstract Superclass""" 3 def __init__( self, rank, suit, hard, soft ): 4 super().__setattr__( 'rank', rank ) 5 super().__setattr__( 'suit', suit ) 6 super().__setattr__( 'hard', hard ) 7 super().__setattr__( 'soft', soft ) 8 def __setattr__( self, name, value ): 9 if name in self.__dict__: 10 raise AttributeError( "Cannot set {name}".format(name=name) ) 11 raise AttributeError( "'{__class__.__name__}' has no attribute'{name}'".format( __class__= self.__class__, name= name ) ) 12 def __getattribute__( self, name ): 13 if name.startswith('_'): 14 raise AttributeError 15 return object.__getattribute__( self, name ) 16 17 >>> c = BlackJackCard3( 'A', '?', 1, 11 ) 18 >>> c.rank= 12 19 Traceback (most recent call last): 20 File "<stdin>", line 1, in <module> 21 File "<stdin>", line 9, in __setattr__ 22 File "<stdin>", line 13, in __getattribute__ 23 AttributeError 24 >>> c.__dict__['rank']= 12 25 Traceback (most recent call last): 26 File "<stdin>", line 1, in <module> 27 File "<stdin>", line 13, in __getattribute__ 28 AttributeError __getattribute__?
?四:descriptors
Descriptor.__get__( self, instance, owner ),Descriptor.__set__( self, instance, value ),Descriptor.__delete__( self, instance )instance: the self variable of the object being accessed
owner : the owning class object
value : the new value that the descriptor needs to be set to.
描述符是一個類:在達到屬性前處理,可用于get,set,delete
其本身在類定義時創(chuàng)建,并不是在__init__中創(chuàng)建,它是類的一部分,不同于方法以及屬性
用其來實現(xiàn)(不)可變對象:
無數(shù)據(jù)描述符:實現(xiàn)__set__or__delete__ or both,若是immutable對象,只用實現(xiàn)__set__并返回AttributeError
數(shù)據(jù)描述符: 至少實現(xiàn)__get__,通常實現(xiàn)__get__與__set__來創(chuàng)建個可變對象。
1:無數(shù)據(jù)描述符
1 class UnitValue_1: 2 """Measure and Unit combined.""" 3 def __init__( self, unit ): 4 self.value= None 5 self.unit= unit 6 self.default_format= "5.2f" 7 def __set__( self, instance, value ): 8 self.value= value 9 def __str__( self ): 10 return "{value:{spec}} {unit}".format( spec=self.default_format, **self.__dict__) 11 def __format__( self, spec="5.2f" ): 12 #print( "formatting", spec ) 13 if spec == "": spec= self.default_format 14 return "{value:{spec}} {unit}".format( spec=spec,**self.__dict__) 15 16 # The following is a class that does rate-time-distance calculations eagerly: 17 class RTD_1: 18 rate= UnitValue_1( "kt" ) 19 time= UnitValue_1( "hr" ) 20 distance= UnitValue_1( "nm" ) 21 def __init__( self, rate=None, time=None, distance=None ): 22 if rate is None: 23 self.time = time 24 self.distance = distance 25 self.rate = distance / time 26 if time is None: 27 self.rate = rate 28 self.distance = distance 29 self.time = distance / rate 30 if distance is None: 31 self.rate = rate 32 self.time = time 33 self.distance = rate * time 34 def __str__( self ): 35 return "rate: {0.rate} time: {0.time} distance:{0.distance}".format(self) 36 37 # As soon as the object is created and the attributes loaded, the missing value is computed. 38 # Once computed, the descriptor can be examined to get the value or the unit's name. 39 # Additionally, the descriptor has a handy response to str() and formatting requests 40 41 >>> m1 = RTD_1( rate=5.8, distance=12 ) 42 >>> str(m1) 43 'rate: 5.80 kt time: 2.07 hr distance: 12.00 nm' 44 >>> print( "Time:", m1.time.value, m1.time.unit ) 45 Time: 2.0689655172413794 hr 無數(shù)據(jù)描述符的例子2:數(shù)據(jù)描述符,轉換單位后自動更新
1 class Unit: 2 conversion= 1.0 3 def __get__( self, instance, owner ): 4 return instance.kph * self.conversion #kph:千米每小時 5 def __set__( self, instance, value ): 6 instance.kph= value / self.conversion 7 8 # The following are the two conversion descriptors: 9 class Knots( Unit ): 10 conversion= 0.5399568 11 class MPH( Unit ): 12 conversion= 0.62137119 13 # The following is a unit descriptor for a standard unit, kilometers per hour: 14 class KPH( Unit ): 15 def __get__( self, instance, owner ): 16 return instance._kph 17 def __set__( self, instance, value ): 18 instance._kph= value 19 20 21 class Measurement: 22 kph= KPH() 23 knots= Knots() 24 mph= MPH() 25 def __init__( self, kph=None, mph=None, knots=None ): 26 if kph: 27 self.kph= kph 28 elif mph: 29 self.mph= mph 30 elif knots: 31 self.knots= knots 32 else: 33 raise TypeError 34 def __str__( self ): 35 return "rate: {0.kph} kph = {0.mph} mph = {0.knots}knots".format(self) 36 37 # 在不同進制下自動完成轉換 38 >>> m2 = Measurement( knots=5.9 ) 39 >>> str(m2) 40 'rate: 10.92680006993152 kph = 6.789598762345432 mph = 5.9 knots' 41 >>> m2.kph 42 10.92680006993152 43 >>> m2.mph 44 6.789598762345432 數(shù)據(jù)描述符例子?
?
五:一些補充:
Internally, Python uses descriptors to implement features such as method functions,static method functions, and properties. Many of the cool use cases for descriptors
are already first-class features of the language
In Python, it's considerably simpler to treat all attributes as public. This means the following:
They should be well documented.
They should properly reflect the state of the object; they shouldn't be temporary or transient values.
In the rare case of an attribute that has a potentially confusing (or brittle)
value, a single leading underscore character (_) marks the name as "not part
of the defined interface." It's not really private.
一般來說,外部能夠改變屬性值并不是嚴重的事,但是當一個屬性值改變后會影響到另一個時,我們需要考慮用函數(shù)或者property進行一些設置。
注意區(qū)別property的兩種設計方式(eager calcilation & lazy calculation)
descriptor是非常高級的python用法,一般用于連接 python 與 non-python 的處理,比如python與SQL,python做網(wǎng)絡服務器,
在我們的程序里,關于attributes我們盡量用property來實現(xiàn),如果發(fā)現(xiàn)property需要寫的太復雜,那么我們轉向descriptor。
?
轉載于:https://www.cnblogs.com/pengsixiong/p/5382421.html
總結
以上是生活随笔為你收集整理的python面对对象编程-------5:获取属性的四种办法:@property, __setattr__(__getattr__) ,descriptor...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从svn导入项目后处理一些报错信息
- 下一篇: 一些javascript免费中文书籍