Photo by Jeff Sheldon on Unsplash
本篇接續淺談Python類別(Class)文章,細談Python類別(Class)中的屬性(Attribute)觀念。我們知道屬性(Attribute)可以分為實體屬性(Instance Attribute)與類別屬性(Class Attribute),今天就來探討它們的用法以及不一樣的地方。另外,也會介紹Python屬性(Property)的應用方式。- 實體屬性(Instance Attribute)
- 類別屬性(Class Attribute)
- 屬性(Property)
一、實體屬性(Instance Attribute)
需伴隨物件(Object)的生成來建立,也就是透過點(.)的語法或在建構式(Constructor)中所生成的屬性(Attribute)。各物件(Object)的實體屬性(Instance Attribute)各自獨立,修改某一個物件(Object)的實體屬性(Instance Attribute)值時,不會影響到其他物件。如下範例:
# 汽車類別
class Cars:
pass
mazda = Cars()
mazda.color = "blue"
mazda.seat = 4
toyota = Cars()
toyota.color = "red"
toyota.seat = 6
print("mazda color: ", mazda.color)
print("mazda seat: ", mazda.seat)
print("toyota color: ", toyota.color)
print("toyota seat: ", toyota.seat)
執行結果
# 汽車類別
class Cars:
# 建構式
def __init__(self, color, seat):
self.color = color
self.seat = seat
self.weight = 140
mazda = Cars("blue", 4)
mazda.color = "yellow"
mazda.seat = 8
mazda.weight = 200
toyota = Cars("red", 6)
print("mazda color: ", mazda.color)
print("mazda seat: ", mazda.seat)
print("mazda weight: ", mazda.weight)
print("toyota color: ", toyota.color)
print("toyota seat: ", toyota.seat)
print("toyota weight: ", toyota.weight)
執行結果二、類別屬性(Class Attribute)
定義在類別層級的屬性(Attribute),也就是在建構式(Constructor)之外的屬性(Attribute)。可以不需要建立物件(Object),直接透過類別名稱存取。各物件共享類別屬性(Class Attribute)值,也就是說當我們修改類別屬性(Class Attribute)值時,每一個透過此類別(Class)所建立的物件(Object),都會受到影響。如下範例:
# 汽車類別
class Cars:
door = 4
# 建構式
def __init__(self, color, seat):
self.color = color
self.seat = seat
self.weight = 140
mazda = Cars("blue", 4)
toyota = Cars("red", 6)
print("mazda original door: ", mazda.door) # door原值
print("toyota original door: ", toyota.door) # door原值
Cars.door = 6
print("mazda new door: ", mazda.door) # door新值
print("toyota new door: ", toyota.door) # door新值
執行結果
三、屬性(Property)
# 汽車類別
class Cars:
# 建構式
def __init__(self, weight):
self.weight = weight #車重屬性
mazda = Cars(-200)
我們初始化屬性(Attribute)時,傳入車重屬性值為-200,Python編譯器不會報任何錯誤,但是實際上車重不可能為負的,這時候我們要怎麼防止來源端傳入不正確的資料,而間接影響程式邏輯?有寫過其他物件導向程式語言的開發人員可能會這樣做:
# 汽車類別
class Cars:
# 建構式
def __init__(self, weight):
self.set_weight(weight)
def get_weight(self):
return self.__weight
def set_weight(self, value):
if value <= 0:
raise ValueError("Car weight cannot be 0 or less.")
self.__weight = value
mazda = Cars(-200)
執行結果雖然此方法可以達到檢核的目的,但是這樣的寫法不"Pythonic",意思是沒有寫出Python的特點或風格,我們可以使用Python的屬性(Property)來達到相同的效果。如下範例:
# 汽車類別
class Cars:
# 建構式
def __init__(self, weight):
self.weight = weight
@property
def weight(self):
return self.__weight
@weight.setter
def weight(self, value):
if value <= 0:
raise ValueError("Car weight cannot be 0 or less.")
self.__weight = value
在讀取屬性(Attribute)的方法(原get_weight()方法)上方加上@property Decorator,並且將方法名稱修改為weight,這個weight就是屬性(Property)。接著在設定屬性(Attribute)的方法(原set_weight()方法)上方加上@property.setter,也就是@weight.setter,意思就是告訴類別(Class)當來源端要設定屬性(Property)值時,要呼叫這個方法(Method)。同樣我們將方法名稱修改為weight,最後別忘了修改建構式(Constructor)中的屬性(Property)設定。對來源端來說,設定或讀取的屬性(Attribute),事實上在類別(Class)中,是呼叫了屬性(Property)的設定方法(setter)及讀取方法(getter)。
我們來測試一下修改後的結果:
mazda = Cars(100)
print(mazda.weight)
設定mazda物件(Object)的車重屬性(Property)為100時,建構式(Constructor)呼叫加了@weight.setter的設定屬性(Property)方法(Method),而第3行在存取物件(Object)的屬性(Property)時,則是呼叫加了@property的讀取屬性(Property)方法(Method)。另外我們傳入負數,同樣會得到ValueError的例外錯誤訊息,如下範例:
mazda = Cars(-200)
執行結果四、小結
以上就是針對Python屬性的詳細介紹,希望透過此文章,可以有更進一步的認識,在練習的過程中若有碰到任何問題或說明不清楚的地方,歡迎留言與我分享!如果您喜歡我的文章,請幫我按五下Like(使用Google或Facebook帳號免費註冊),支持我創作教學文章,回饋由LikeCoin基金會出資,完全不會花到錢,感謝大家。
有想要看的教學內容嗎?歡迎利用以下的Google表單讓我知道,將有機會成為教學文章,分享給大家😊
請問要怎麼區分我的類別要不要有實體方法或類別方法
回覆刪除要怎麼區分物件需不需要實體或類別屬性....
您好,當您想要「每一個物件都共同擁有相同的屬性值」,就使用「類別屬性」。相反的,如果您「不想要每一個物件都擁有相同的屬性值,而是在建立物件時,動態的進行設定」,則使用「實體屬性」。
刪除Hello Mile, 想請教您在property的case中的一些問題 :
回覆刪除1. 請問 使用property 定義 實體函數 weight 的method時,一定要使用weight當作命名 而不能使用別的命名嗎 例如 def weight_of_car
2. 請問在第10行與16行我們在呼叫 weight 屬性時,可以不要使用self.__weight 而是 使用 self.weight 嗎? ,為什麼一定要在weight前加上__?
我有稍微 google 使用__的用途主要是變數命名具有唯一性,但我不解的是,為什麼在使用property時我們必須要使用這種方法。
謝謝您
你好我不是筆者不過剛好我也有一樣的困擾所以做了些測試
刪除筆者這段提到
"@property.setter,也就是@weight.setter,意思就是告訴類別(Class)當來源端要設定屬性(Property)值時,要呼叫這個方法(Method)。"
先回答你的問題
1.property 定義 實體函數 weight 的method時 作者命名為weight是因為
建構函式裡的變數self.weight=weight
self.weight <=這個變數你命名為什麼你 @property和@setter方法就要命名成什麼
方法裡的變數名稱則是:
@property
def 你要設定的變數名稱:
return self.自己取名
@你要設定的變數名稱.setter
def 你要設定的變數名稱(self,你傳入的參數):
self.你在property return取的變數名稱 = 你傳入的參數
你可試試我底下的code會比較清楚我盡力了
def __init__(self, name):
self.aaname = name
# 方法(Method)
@property
def aaname(self):
return self.realname
@aaname.setter
def aaname(self,create_name):
self.realname=create_name
我做測試的心得:
當你在建構這個物件時(呼應筆者"當來源端要設定屬性(Property)值時"),
你要建立weight的值你會去呼叫@weight.setter方法
不信你可以在@weight.setter方法print(一串文字)你會發現你重建構時就會出現你print的文字了即使你沒對物件作任何其它方法的呼叫
樓上說的沒錯,但這樣也可以輸出。
刪除# 汽車類別
class Cars:
# 建構式
def __init__(self, weight):
self.test = weight
@property
def fuck(self):
print('這裡是 property')
return self.__test2
@fuck.setter
def test(self, value):
print('我近來囉')
if value <= 0:
raise ValueError("Car weight cannot be 0 or less.")
self.__test2 = value
mazda = Cars(1)
# mazda = Cars(-200)
print(mazda.fuck)
然後我嘗試不加 __ ,我發現它會一直重複輸出直到跑出
'RecursionError: maximum recursion depth exceeded while calling a Python object'
推測是不加__的話,它會重複呼叫 property 跟 setter 直到超出程式限制的遞迴限制,而我們每次只需要檢查一次,所以要加。