Python內(nèi)置函數(shù)之property函數(shù)用法詳解
在 Python 中,property 是一個內(nèi)置的裝飾器,用于將類方法轉(zhuǎn)換為類屬性,實現(xiàn)對屬性的高級控制(如類型檢查、只讀限制、計算屬性等)。
以下是對 property 的詳細解析與實戰(zhàn)案例:
一、核心概念與基礎(chǔ)語法
1.property的本質(zhì)
- 屬性封裝:將方法偽裝成屬性,通過點號(
.)直接訪問 - 訪問控制:自定義屬性的 getter、setter 和 deleter 方法
- 計算屬性:動態(tài)計算屬性值,無需顯式存儲
2. 基礎(chǔ)語法(兩種方式)
# 方式1:使用 property() 函數(shù)
class Person:
def __init__(self, age):
self._age = age # 私有屬性
def get_age(self):
return self._age
def set_age(self, value):
if value < 0:
raise ValueError("年齡不能為負數(shù)")
self._age = value
age = property(get_age, set_age) # 創(chuàng)建 property 對象
# 方式2:使用裝飾器(推薦)
class Person:
def __init__(self, age):
self._age = age
@property # 相當于 age = property(age)
def age(self):
return self._age
@age.setter # 相當于 age = age.setter(setter)
def age(self, value):
if value < 0:
raise ValueError("年齡不能為負數(shù)")
self._age = value
3. 使用示例
p = Person(25) print(p.age) # 調(diào)用 getter 方法,輸出: 25 p.age = 30 # 調(diào)用 setter 方法 p.age = -5 # 拋出 ValueError: 年齡不能為負數(shù)
二、進階用法
1. 只讀屬性(僅實現(xiàn) getter)
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14 * self.radius ** 2
c = Circle(5)
print(c.area) # 輸出: 78.5
c.area = 100 # 報錯: AttributeError: can't set attribute
2. 計算屬性(動態(tài)計算)
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
@property
def perimeter(self):
return 2 * (self.width + self.height)
r = Rectangle(3, 4)
print(r.area) # 輸出: 12
print(r.perimeter) # 輸出: 14
3. 刪除屬性(實現(xiàn) deleter)
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.deleter
def name(self):
print("刪除名字...")
del self._name
p = Person("Alice")
print(p.name) # 輸出: Alice
del p.name # 輸出: 刪除名字...
print(p.name) # 報錯: AttributeError
4. 類型檢查與驗證
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if not isinstance(value, (int, float)):
raise TypeError("溫度必須是數(shù)字")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 1.8 + 32
t = Temperature(25)
print(t.fahrenheit) # 輸出: 77.0
t.celsius = "30" # 報錯: TypeError
三、實戰(zhàn)案例
1. 數(shù)據(jù)庫字段映射
class User:
def __init__(self, db_row):
self._db_row = db_row
@property
def id(self):
return self._db_row["id"]
@property
def username(self):
return self._db_row["username"]
@property
def email(self):
return self._db_row["email"]
# 使用示例
db_data = {"id": 1, "username": "john", "email": "john@example.com"}
user = User(db_data)
print(user.username) # 輸出: john
2. 緩存計算結(jié)果
import math
class Factorial:
def __init__(self, number):
self.number = number
self._result = None
@property
def result(self):
if self._result is None:
print("計算階乘...")
self._result = math.factorial(self.number)
return self._result
f = Factorial(5)
print(f.result) # 輸出: 計算階乘... 120
print(f.result) # 直接返回緩存結(jié)果: 120
3. 權(quán)限控制
class Account:
def __init__(self, balance, owner):
self._balance = balance
self._owner = owner
@property
def balance(self):
return self._balance
@balance.setter
def balance(self, value):
if self._owner != "admin":
raise PermissionError("只有管理員可以修改余額")
self._balance = value
acc = Account(1000, "user")
print(acc.balance) # 輸出: 1000
acc.balance = 2000 # 報錯: PermissionError
四、深入理解與注意事項
1.property的底層實現(xiàn)
property 是一個描述符(descriptor)類,其簡化實現(xiàn)如下:
class Property:
def __init__(self, fget=None, fset=None, fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self, instance, owner):
if instance is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(instance)
def __set__(self, instance, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(instance, value)
def __delete__(self, instance):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(instance)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel)
2. 繼承與property
- 子類可重寫父類的 property:
class Parent: @property def value(self): return 10 class Child(Parent): @property def value(self): return 20
3. 性能考量
- 少量調(diào)用:
property的性能開銷極?。s 100ns / 次) - 大量調(diào)用:若性能敏感(如循環(huán)百萬次),建議直接訪問屬性
五、與其他技術(shù)的對比
| 技術(shù) | 用途 | 實現(xiàn)方式 |
|---|---|---|
| property | 屬性封裝與控制 | 裝飾器或 property () 函數(shù) |
| getter/setter | 傳統(tǒng)的訪問控制方法 | 顯式定義方法 |
| getattr | 動態(tài)屬性獲取 | 魔法方法(所有屬性都經(jīng)過它) |
| setattr | 動態(tài)屬性設(shè)置 | 魔法方法(所有屬性都經(jīng)過它) |
六、常見誤區(qū)與最佳實踐
1. 避免過度使用property
- 推薦場景:需要驗證、計算或訪問控制的屬性
- 不推薦場景:簡單的數(shù)據(jù)封裝(直接使用公有屬性)
2. 私有屬性命名約定
- 使用單下劃線
_attr表示受保護屬性(非強制私有) - 使用雙下劃線
__attr進行名稱修飾(Name Mangling)
3. 初始化時調(diào)用 setter
class Person:
def __init__(self, age):
self.age = age # 調(diào)用 setter 進行驗證
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("年齡不能為負數(shù)")
self._age = value
七、總結(jié)
| 場景 | 推薦實現(xiàn)方式 |
|---|---|
| 簡單屬性 | 直接使用公有屬性 |
| 需要類型檢查 / 驗證 | 使用 property 裝飾器 |
| 只讀屬性 | 只實現(xiàn) getter 方法 |
| 計算屬性 | 使用 property 裝飾器 |
| 復(fù)雜的屬性訪問控制 | 自定義描述符類 |
合理使用 property 可以提高代碼的安全性、可維護性和可讀性,尤其在需要控制屬性訪問邏輯的場景中表現(xiàn)出色。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
python-opencv中的cv2.inRange函數(shù)用法說明
這篇文章主要介紹了python-opencv中的cv2.inRange函數(shù)用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04
python自定義函數(shù)實現(xiàn)最大值的輸出方法
今天小編就為大家分享一篇python自定義函數(shù)實現(xiàn)最大值的輸出方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07
Python時間序列數(shù)據(jù)的預(yù)處理方法總結(jié)
這篇文章主要介紹了Python時間序列數(shù)據(jù)的預(yù)處理方法總結(jié),時間序列數(shù)據(jù)隨處可見,要進行時間序列分析,我們必須先對數(shù)據(jù)進行預(yù)處理。時間序列預(yù)處理技術(shù)對數(shù)據(jù)建模的準確性有重大影響2022-07-07

