python3類的學習筆記:屬性方法@property


@staticmethod:靜態方法,靜態方法是不可以訪問實例變量或類變量的,這個類方法實際上跟類沒有什么關系,它只是類下面的一個函數,跟類本身沒關系,只是名義上歸類管。
它與類唯一的關聯就是需要通過類名來調用這個方法
如果非要傳參數,只有傳自己
@classmethod:類方法只能訪問類變量,不能訪問實例變量

@property :屬性方法,屬性方法的作用就是通過@property把一個方法變成一個靜態屬性
例如:
1 class Dog(object): 2     def __init__(self,name): 3         self.name=name 4     @property  #把一個方法變成一個靜態屬性
5     def eat(self): 6         print('%s is eating %s' %(self.name,'包子')) 7 d1=Dog('Jack') 8 d1.eat  #不加(),輸出:Jack is eating 包子
 1 #如果要傳參數
 2 class Dog(object):  3     def __init__(self,name):  4         self.name=name  5         self.__food=None  6  @property  7     def eat(self):  8         print('%s is eating %s' %(self.name,self.__food))  9 
10  @eat.setter 11     def eat(self,food): 12         print('要傳的參數:',food) 13         self.__food=food 14 d1=Dog('Jack') 15 d1.eat 16 d1.eat='包子'
17 d1.eat    #self.__food=food把參數傳值了再執行屬性方法

 

 

摘自其他人筆記

定義類Student,擁有變量名name和score

1 class Student(object): 2     def __init__(self,name,score): 3         self.name = name 4         self.score = score  

但是,上述這樣定義score是不會進行參數檢查的,也就意味着我們不能執行必要的參數以及錯誤處理。

我們可以定義相應的set和get成員函數來訪問成員變量score,並且進行參數檢查。如下所示:

 1 class Student(object):  2     def __init__(self,name,score):  3         self.name = name  4         self.score = score  5     def get_score(self):  6         return self.score  7     def set_score(self,score):  8         if not isinstance(score, int):  9             raise ValueError(”invalid score!!!”) 10         if score < 0 or score > 100: 11             raise ValueError(”score must be between [0,100]!!!”) 12         self._score = score  

上述代碼定義了score成員的set和get函數。(可能實際應用時,修改分數比較常見)

現在,我們改變參數的代碼是這樣的:

1 s1 = Student() 2 s1.set_score(9999) #這里會拋出異常 

上述的第二種方式實現了set函數的參數檢查,但是修改score的代碼從簡單的 s1.score = 90 變成了 s1.set_score(90) .我們怎么樣才能做到既檢驗輸入的參數又使得修改score的代碼不變呢?

@Property便是這個作用。

下面,我們討論Python的高級特性 @Property。簡單的說@Properyty就是將成員函數的調用變成屬性賦值。

於是有了下面的代碼:

 1 class Student(object):  2     def __init__(self,name,score):  3         self._name = name  4         self._score = score  5  @property  6     def score(self):  7         return self._score  8  @score.setter  9     def score(self,score): 10         if not isinstance(score,int): 11             raise ValueError(”invalid score!!!”) 12         if score < 0 or score > 100: 13             raise ValueError(”score must be between [0,100]!!!”) 14         self._score = score 15  @property 16     def name(self): 17         return self._name 18   
19 s1 = Student(”Lily”, 90) 20 s1.name = ”Luly” 21 s1.score = 100  

關於上述代碼的說明:

  • 可能你已經發現了,我的成員變量改成了_name 與 _score,這里首先是為了增加可讀性,這兩個變量是私有的。其次的原因見下面的誤區分析。
  • 上述代碼中的 s1.name = “Luly” 行會出現編譯錯誤 AttributeError: can’t set attribute ,也就是說這里不能直接這樣改變,這是為什么呢?可以看到,在代碼中,我並沒有提供名稱為name的set函數, 這里值得注意的是,s1._name = “Lucy” 是可以運行通過的。但是我們之前說過了,假設用戶足夠自覺,不會去操作 _xxx 或者 __xxx這樣的變量名。
  • 按照上述代碼的初衷,也就是說name是類的只讀的屬性。score是可修改的。
  • 關於@property 修飾的函數 score 就是個簡單的get函數,該函數不需要任何參數(self不需要傳入值),因此我們可以這樣來調用這個函數 ,即 s1.score 即可。(這就是Property的用處,將函數調用轉化為屬性訪問),相當於給score加了一層包裹。
    • 關於@score.setter 便是針對與 score函數包裹的成員變量的的set函數。當我們需要修改_score的值時,使用score函數,但是就像score是類的成員屬性一樣使用,例如上面的: s1.score = 100,實際上等價於 s1.score(100).

      注意,這里的函數名不一定要是score,可以是任何的字符串,這里只是為了方面說score函數是_score的包裹,例如下面的代碼:我們將score改成了AA,但是這樣在:

  •  1 class Student(object):  2   
     3     def __init__(self,name,score):  4         self._name = name  5         self._score = score  6  
     7  @property  8     def AA(self):  9         return self._score 10  @AA.setter 11     def AA(self,score): 12         if not isinstance(score,int): 13             raise ValueError(“invalid score!!!”) 14         if score < 0 or score > 100: 15             raise ValueError(“score must be between [0,100]!!!”) 16         self._score = score 17  @property 18     def name(self): 19         return self._name 20             
    21 s1 = Student(”Lily”, 90) 22 s1.name = ”Luly” 23 s1.AA = 100 # 這里相當於是 s1.AA(100) 

    好了,關於@Property的概念與用法就講完了。本質上是定義了新的函數,該函數們執行set與get的功能,並且有@Property的包裹。並且將這些定義的函數當作屬性一樣來賦值。

    可能存在的陷阱:

    下面的代碼是個大的陷阱,因為現在的函數已經不再是單純的函數,而是可以直接用 = 來調用,例如上面的 score函數 的調用竟然是 s1.score = 100 .這樣就會出現下面的問題:

  •  1 class Student(object):  2     def __init__(self,name,score):  3         self.name = name  4         self.score = score  5  @property  6     def score(self):  7         return self.score  8  @score.setter  9     def score(self,score): 10         if not isinstance(score,int): 11             raise ValueError(”invalid score!!!”) 12         if score < 0 or score > 100: 13             raise ValueError(”score must be between [0,100]!!!”) 14         self.score = score 15  @property 16     def name(self): 17         return self.name 18     def func(self): 19         self.score = score 20   
    21 s1 = Student(”Lily”, 90) 22 s1.func()  

    上面的代碼有兩個很大的錯誤,

    • 你會發現,你無法定義Student的任何實例,為什么呢? 首先@property把score和name兩個成員函數可以當作成員變量來訪問,那么在定義實例時,調用init函數的時候,self.name = name,這一句,Python會將左端的self.name當作函數調用,然而我們並未給name變量 定義set函數,於是錯誤信息為:AttributeError: can’t set attribute.
    • 好的,我們接下來注釋掉
  • 1 @property 2 def name(self): 3     return self.name  

    這兩行,那么接下來的運行還是錯誤的,為什么呢?是因為init函數代碼的第二行 self.score = score, 很慶幸我們定義了score的set函數, 那么self.score調用score函數,當執行到score函數的最后一句self.score = score時, 我們回發現,式子的左端還是score函數調用, 如此往復,最終以函數遞歸深度達到上限退出程序。

    這里其實是一個很好的代碼習慣,那就是盡量不要讓函數名與變量名同名,便可以避免這些錯誤。所以,比如說,這里的變量名self.score改為:self._score就可以避免遞歸錯誤。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM