一:最基本的屬性操做python
1 class Generic: 2 pass 3 4 g= Generic() 5 6 >>> g.attribute= "value" #建立屬性並賦值 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'
二:@propertyweb
被@property修飾的是一個方法,但此方法名能夠像屬性同樣被獲取,設置,刪除
須要注意的是,屬性的外部添加是十分簡單的,但property的外部添加不是,因此其與屬性仍是有區別的
有兩種方法建立property:
1:用@property修飾的函數
2:用property()方法
有兩種設計模式:
1:懶惰計算模式:被調用時才執行
2:主動計算模式:實例化時就執行
懶惰模式:計算手牌總和
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 #被調用時才執行計算手頭的牌之和 26 # 19 27 h.card = d.pop() #注意,能夠將@property看做@property.getter,而此地能夠看做兩步,左邊爲@property獲取屬性,=號調用@property.setter而且將右邊的d.pop()看成參數傳入。 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已經模糊了數據和行爲了,那麼到底何時咱們須要使用@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片斷的代碼app
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優化: 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
三:屬性獲取的特殊方法ide
__getattr__(), __setattr__(), and __delattr__(),__dir__(),__getattribute__()
__setattr__(): 建立屬性並賦值
__getattr__(): 首先:若是此屬性已有值,不會用到__getattr__(),直接返回值就是了。
其次:若是此屬性沒有值,此時調用__getattr__()而且返回其中設定的返回值。
最後:若是壓根沒有這屬性,報 AttributeError 錯誤。
__delattr__():刪除一個屬性
__dir__(): 返回包含屬性的list
__getattribute__():更加底層的屬性獲取方法,他默認從__dict__(或__slots__)中獲取值,若是沒有找到,調用__getattr__()做爲反饋。若是發現此值是一個dexcriptor,就調用descriptor,否者就直接返回值。
__getattr__()方法只當某個屬性沒有值時才起做用。
1:建立immutable object
什麼是immutable object:不可以在外部直接賦值一個已有屬性的值,不能建立新屬性
immutable object的一個特色是__hash__()可以返回固定的值
版本一:用__slots__建立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機制
版本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
# 注意上面兩個版本是有區別的,在版本2中能夠經過d.__dict__來增長屬性
# 而版本1中用了__slots__後就會關閉__dict__
2:建立一個一旦給定速度與時間就自動更新距離的類,讓其繼承自dict,好處是用format函數特別方便
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在於,一旦咱們想改變時間,這時發現速度與距離至少其一必定會變,按代碼順序是改變了距離,而若是咱們不想改變距離而是改變速度就不行了 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.
3:The __getattribute__() method優化
總的來講,幾乎不必用__getattribute__(),其默認的方法已近夠強大了,何況幾乎全部咱們須要的都可以經過__getattr__()實現。
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
四: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
其自己在類定義時建立,並非在__init__中建立,它是類的一部分,不一樣於方法以及屬性
用其來實現(不)可變對象:
無數據描述符:實現__set__or__delete__ or both,如果immutable對象,只用實現__set__並返回AttributeError
數據描述符: 至少實現__get__,一般實現__get__與__set__來建立個可變對象。
1:無數據描述符
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
2:數據描述符,轉換單位後自動更新
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
五:一些補充:
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.
通常來講,外部可以改變屬性值並非嚴重的事,可是當一個屬性值改變後會影響到另外一個時,咱們須要考慮用函數或者property進行一些設置。注意區別property的兩種設計方式(eager calcilation & lazy calculation) descriptor是很是高級的python用法,通常用於鏈接 python 與 non-python 的處理,好比python與SQL,python作網絡服務器,在咱們的程序裏,關於attributes咱們儘可能用property來實現,若是發現property須要寫的太複雜,那麼咱們轉向descriptor。