Python - 物件導向程式設計 - @property

小菠蘿測試筆記 發表於 2021-08-30
Python 物件導向

前言

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