最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python @property裝飾器全解析

 更新時(shí)間:2026年02月07日 15:30:11   作者:Tipriest_  
在Python中,@property 是一個(gè)非常常用、也很優(yōu)雅的裝飾器,用來把“方法”偽裝成“屬性”,這篇文章主要介紹了Python @property裝飾器介紹,需要的朋友可以參考下

在 Python 中,@property 是一個(gè)非常常用、也很優(yōu)雅的裝飾器,用來把“方法”偽裝成“屬性”。它最常見的用途是:對(duì)屬性的訪問進(jìn)行控制,同時(shí)保持簡潔的屬性訪問語法。

下面從幾個(gè)方面幫你系統(tǒng)介紹一下 @property

一、為什么需要@property?

假設(shè)你有一個(gè)類,直接暴露公共屬性:

class Person:
    def __init__(self, name, age):
        self.name = name      # 公共屬性
        self.age = age        # 公共屬性

一開始你只是簡單存?zhèn)€年齡,后來你發(fā)現(xiàn):

  • 想對(duì) age 做合法性檢查(不能是負(fù)數(shù))
  • 想在設(shè)置年齡時(shí)做一些額外處理(比如同步更新別的數(shù)據(jù))

如果屬性是直接暴露的:

p = Person("Tom", 20)
p.age = -5   # 沒有任何阻止

你可以改成用方法:

class Person:
    def __init__(self, name, age):
        self._age = None
        self.set_age(age)
    def get_age(self):
        return self._age
    def set_age(self, value):
        if value < 0:
            raise ValueError("年齡不能為負(fù)數(shù)")
        self._age = value

但這樣使用時(shí)會(huì)變成:

p = Person("Tom", 20)
p.set_age(30)
print(p.get_age())

寫起來不如屬性直觀。并且如果項(xiàng)目已經(jīng)有很多地方寫了 p.age,現(xiàn)在改成 p.get_age() / p.set_age() 會(huì)造成兼容性問題。

@property 的作用就是:
在“不改變外部訪問方式”的前提下,為屬性增加 getter/setter 等邏輯。

二、@property的基本用法(只讀屬性)

最簡單的用法是將一個(gè)方法變成“只讀屬性”:

class Circle:
    def __init__(self, radius):
        self._radius = radius
    @property
    def radius(self):   # getter
        return self._radius

使用時(shí):

c = Circle(10)
print(c.radius)   # 像訪問屬性一樣調(diào)用,實(shí)際調(diào)用的是 radius() 方法
# 輸出:10
c.radius = 20     # 這里會(huì)報(bào)錯(cuò),因?yàn)闆]有定義 setter
# AttributeError: can't set attribute

此時(shí) radius 就是一個(gè)只讀屬性:可以獲取,不能直接賦值。

三、定義可讀可寫屬性:@xxx.setter

常見場景是既要讀,也要寫,并在寫入時(shí)做檢查或處理:

class Circle:
    def __init__(self, radius):
        self._radius = radius
    @property
    def radius(self):
        """半徑屬性:讀取時(shí)從 _radius 取值"""
        return self._radius
    @radius.setter
    def radius(self, value):
        """設(shè)置半徑時(shí)進(jìn)行校驗(yàn)"""
        if value <= 0:
            raise ValueError("半徑必須為正數(shù)")
        self._radius = value

使用:

c = Circle(10)
print(c.radius)  # 10
c.radius = 5     # 實(shí)際調(diào)用 radius.setter
print(c.radius)  # 5
c.radius = -1    # ValueError: 半徑必須為正數(shù)

要點(diǎn):

  • @radius.setter 中的名字必須和 @property 的函數(shù)名完全一致。
  • setter 只定義“寫邏輯”,讀邏輯仍然在 @property 的函數(shù)里。

四、帶有刪除邏輯的屬性:@xxx.deleter

有時(shí)你希望通過 del obj.attr 來觸發(fā)一些自定義行為,可以用 @xxx.deleter

class Demo:
    def __init__(self, value):
        self._value = value
    @property
    def value(self):
        print("獲取 value")
        return self._value
    @value.setter
    def value(self, v):
        print("設(shè)置 value")
        self._value = v
    @value.deleter
    def value(self):
        print("刪除 value")
        del self._value

使用:

d = Demo(10)
print(d.value)   # 獲取 value -> 10
d.value = 20     # 設(shè)置 value
del d.value      # 刪除 value

五、計(jì)算屬性:不存儲(chǔ)結(jié)果,只在訪問時(shí)計(jì)算

@property 不一定要綁定一個(gè)內(nèi)部存儲(chǔ)字段,也可以每次訪問時(shí)動(dòng)態(tài)計(jì)算:

import math
class Circle:
    def __init__(self, radius):
        self._radius = radius
    @property
    def radius(self):
        return self._radius
    @radius.setter
    def radius(self, r):
        if r <= 0:
            raise ValueError("半徑必須為正數(shù)")
        self._radius = r
    @property
    def area(self):
        """面積屬性:只讀,實(shí)時(shí)計(jì)算"""
        return math.pi * self._radius ** 2

使用:

c = Circle(10)
print(c.area)  # 314.159...
c.radius = 5
print(c.area)  # 會(huì)自動(dòng)變成半徑為 5 時(shí)的面積

area 不需要額外存儲(chǔ)字段 _area,也不需要在每次修改半徑時(shí)手動(dòng)更新面積,訪問時(shí)自動(dòng)按最新半徑計(jì)算。

六、命名習(xí)慣:前有下劃線的“內(nèi)部屬性”

常見寫法:

  • 公共接口屬性:obj.x
  • 內(nèi)部真實(shí)存儲(chǔ)字段:obj._x
class Person:
    def __init__(self, age):
        self._age = None
        self.age = age  # 這里會(huì)走 setter
    @property
    def age(self):
        return self._age
    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError("年齡不能為負(fù)數(shù)")
        self._age = value

初學(xué)者常見錯(cuò)誤是 setter 里面寫成 self.age = value

    @age.setter
    def age(self, value):
        self.age = value  # 錯(cuò)誤!會(huì)導(dǎo)致無限遞歸

正確做法是操作“內(nèi)部字段”例如 self._age,避免調(diào)用自己。

七、@property的優(yōu)點(diǎn)總結(jié)

保持接口穩(wěn)定
一開始可以簡單寫成公有屬性:

class Person:
    def __init__(self, age):
        self.age = age

以后如果需要加校驗(yàn),只要改成:

class Person:
    def __init__(self, age):
        self._age = None
        self.age = age
    @property
    def age(self):
        return self._age
    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError("年齡不能為負(fù)數(shù)")
        self._age = value

外部代碼依然寫 p.age,不需要改任何調(diào)用。

語法簡潔、可讀性好
相比 obj.get_x() / obj.set_x()obj.x 更符合屬性的直覺。

延遲計(jì)算 / 動(dòng)態(tài)計(jì)算
對(duì)一些計(jì)算開銷不大的數(shù)據(jù),用 @property 動(dòng)態(tài)計(jì)算非常方便。
如果計(jì)算很昂貴,還可以結(jié)合緩存(例如自己實(shí)現(xiàn)緩存,或用 functools.cached_property)。

方便加入調(diào)試日志、權(quán)限控制等邏輯
例如在 setter 里打印日志、做權(quán)限檢查,或者在 getter 里做懶加載等。

八、與傳統(tǒng) getter/setter 的區(qū)別

  • Java / C++ 風(fēng)格:顯式的 get_xxx()、set_xxx() 方法。
  • Python:更提倡屬性訪問風(fēng)格 + @property 隱藏實(shí)現(xiàn)細(xì)節(jié)。

也就是說,Python 代碼里你幾乎看不到:

obj.get_age()
obj.set_age(20)

而是:

obj.age
obj.age = 20

但又不犧牲封裝性:內(nèi)部實(shí)現(xiàn)完全可以通過 @property 控制。

九、一些注意事項(xiàng)

  • 不要濫用 @property
    • 若屬性只是單純存取,不需要任何檢查或計(jì)算,直接公有字段也完全沒問題。
    • 過度包裝會(huì)增加心智負(fù)擔(dān)。
  • 避免在 getter 中做特別耗時(shí)的操作
    • 因?yàn)樵L問語法很輕量,調(diào)用者不會(huì)意識(shí)到“這個(gè)屬性代價(jià)這么大”,容易造成性能問題。
    • 對(duì)耗時(shí)計(jì)算,建議用普通方法,或者明確注釋說明。
  • 繼承時(shí)的覆蓋
    • 子類可以重寫父類的 @property,仍然使用同名屬性,這對(duì)庫設(shè)計(jì)非常有用。

十、一個(gè)綜合示例

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self._balance = None
        self.balance = balance   # 走 setter 做校驗(yàn)
    @property
    def balance(self):
        """賬戶余額,不能為負(fù)數(shù)"""
        return self._balance
    @balance.setter
    def balance(self, value):
        if value < 0:
            raise ValueError("余額不能為負(fù)數(shù)")
        self._balance = float(value)
    @property
    def is_rich(self):
        """計(jì)算屬性:根據(jù)余額判斷是否“富有”"""
        return self._balance >= 1_000_000

使用:

acc = BankAccount("Alice", 1000)
print(acc.balance)   # 1000.0
acc.balance = 2000
print(acc.is_rich)   # False
acc.balance = 2_000_000
print(acc.is_rich)   # True
acc.balance = -10    # ValueError: 余額不能為負(fù)數(shù)

到此這篇關(guān)于Python @property裝飾器全解析的文章就介紹到這了,更多相關(guān)Python @property裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

抚宁县| 保亭| 宁蒗| 曲麻莱县| 双柏县| 乐昌市| 隆化县| 遵义市| 门头沟区| 信阳市| 葵青区| 丹东市| 山东| 池州市| 嘉祥县| 泸定县| 湘潭县| 侯马市| 巴林左旗| 芮城县| 岚皋县| 师宗县| 清镇市| 卓资县| 南宁市| 横峰县| 万宁市| 成安县| 襄樊市| 阳春市| 贵溪市| 定远县| 上思县| 大英县| 旬邑县| 岗巴县| 新田县| 类乌齐县| 大宁县| 永清县| 和林格尔县|