前言
- 前面講到例項屬性的時候,我們可以通過 例項物件.例項屬性 來訪問對應的例項屬性
- 但這種做法是不建議的,因為它破壞了類的封裝原則
- 正常情況下,例項屬性應該是隱藏的,只允許通過類提供的方法來間接實現對例項屬性的訪問和操作
class PoloBlog: # 構造方法 def __init__(self, name): self.name = name blog = PoloBlog("小菠蘿") # 破壞了封裝原則 print(blog.name) blog.name = "啊?" print(blog.name) # 輸出結果 小菠蘿 啊?
getter、setter 方法
- 不破壞類封裝原則的基礎上,操作例項屬性
- 寫過 java 的話應該知道,java 的類可以自動生成對屬性的操作方法,一個是 get,另一個是 set(一般稱為 getter、setter 方法)
- python 中雖然不能自動生成,但也可以自己寫哦
class PoloBlog: # 構造方法 def __init__(self, name): self.name = name # set 屬性的方法【setter】 def setName(self, name): self.name = name # get 屬性的方法【getter】 def getName(self): return self.name blog = PoloBlog("小菠蘿") # 獲取 blog 例項物件的 name 例項屬性 print(blog.getName()) # 設定 name 例項屬性 blog.setName("新的小菠蘿") print(blog.getName()) # 輸出結果 小菠蘿 新的小菠蘿
這樣跟 java 的寫法就差不多了,但還是有點麻煩
property() 方法的誕生
可以實現在不破壞類封裝原則的前提下,讓開發者依舊使用 對例物件.屬性 的方式操作類中的屬性
基本使用格式
屬性名 = property(fget=None, fset=None, fdel=None, doc=None)
- fget:用於獲取屬性的方法
- fset:用於設定屬性的方法
- fdel:用於刪除屬性的方法
- doc:屬性的說明文件字串
程式碼栗子
# property() 函式 class PoloBlog: # 建構函式 def __init__(self, name): self.__name = name # setter def setName(self, name): self.__name = name # getter def getName(self): return self.__name # del def delName(self): self.__name = "xxx" # property() name = property(getName, setName, delName, "小菠蘿測試筆記") # 呼叫說明文件 # help(PoloBlog.name) print(PoloBlog.name.__doc__) blog = PoloBlog("小菠蘿") # 自動呼叫 getName() print(blog.name) # 自動呼叫 setName() blog.name = "新的小菠蘿" print(blog.name) # 自動呼叫 delName() del blog.name print(blog.name) # 輸出結果 小菠蘿測試筆記 小菠蘿 新的小菠蘿 xxx
getName return 的是私有屬性 __name,注意不是 name,不然會陷入死迴圈
注意
property() 方法的四個引數都是預設引數,可以不傳參
# property() 函式 class PoloBlog: # 建構函式 def __init__(self, name, age): self.__name = name self.__age = age # setter name def setName(self, name): self.__name = name # getter name def getName(self): return self.__name # del name def delName(self): self.__name = "xxx" # setter age def setAge(self, age): self.__age = age # getter age def getAge(self): return self.__age # property() name = property(getName, setName, delName, "小菠蘿測試筆記") # 沒有 fdel、doc age = property(getAge, setAge) blog = PoloBlog("小菠蘿", 14) print(blog.age) blog.age = "24" print(blog.age) del blog.age print(blog.age) # 輸出結果 14 24 del blog.age AttributeError: can't delete attribute
因為 property() 沒有傳 fdel 方法,所以無法刪除屬性,它是一個可讀寫,不可刪的屬性
其他傳參解析
name = property(getName) # name 屬性可讀,不可寫,也不能刪除 name = property(getName, setName,delName) #name屬性可讀、可寫、也可刪除,就是沒有說明文件
@property
- 是一個裝飾器,相當於 getter 裝飾器
- 可以使用 @property 來建立只讀屬性,將一個例項方法變成一個相同名稱的只讀屬性,這樣可以防止屬性被修改
程式碼栗子
# @property class PoloBlog: def __init__(self, name): self.__name = name @property def name(self): return self.__name blog = PoloBlog("小菠蘿") print(blog.name) blog.name = "test" # 輸出結果 小菠蘿 blog.name = "test" AttributeError: can't set attribute
name 是一個只讀屬性,不可寫,相當於 __name 私有屬性只有 getter 方法,沒有 setter 方法
等價寫法
class PoloBlog: def __init__(self, name): self.__name = name def getName(self): return self.__name name = property(getName) blog = PoloBlog("小菠蘿") print(blog.name)
那想給 __name 設定值怎麼辦呢?
setter 裝飾器
語法格式
@方法名.setter def 方法名(self, value): self.__value = value ...
程式碼栗子
# @setter class PoloBlog: def __init__(self, name): self.__name = name @property def name(self): return self.__name @name.setter def name(self, name): self.__name = name blog = PoloBlog("小菠蘿") # 列印屬性值 print(blog.name) # 修改屬性 blog.name = "新的小菠蘿" print(blog.name) # 輸出結果 小菠蘿 新的小菠蘿
deleter 裝飾器
和 setter 裝飾器差不多寫法
語法格式
@方法名.deleter def 方法名(self): ...
程式碼栗子
class PoloBlog: def __init__(self, name): self.__name = name @property def name(self): return self.__name @name.setter def name(self, name): self.__name = name @name.deleter def name(self): print("刪除 __name") blog = PoloBlog("小菠蘿") # 列印屬性值 print(blog.name) # 修改屬性 blog.name = "新的小菠蘿" # 刪除屬性 del blog.name # 輸出結果 小菠蘿 刪除 __name