前言

  • 前面講到例項屬性的時候,我們可以通過 例項物件.例項屬性 來訪問對應的例項屬性
  • 但這種做法是不建議的,因為它破壞了類的封裝原則
  • 正常情況下,例項屬性應該是隱藏的,只允許通過類提供的方法來間接實現對例項屬性的訪問和操作
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