Python內(nèi)置函數(shù)之property函數(shù)用法詳解
在 Python 中,property 是一個內(nèi)置的裝飾器,用于將類方法轉(zhuǎn)換為類屬性,實現(xiàn)對屬性的高級控制(如類型檢查、只讀限制、計算屬性等)。
以下是對 property 的詳細解析與實戰(zhàn)案例:
一、核心概念與基礎語法
1.property的本質(zhì)
- 屬性封裝:將方法偽裝成屬性,通過點號(
.)直接訪問 - 訪問控制:自定義屬性的 getter、setter 和 deleter 方法
- 計算屬性:動態(tài)計算屬性值,無需顯式存儲
2. 基礎語法(兩種方式)
# 方式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)百萬次),建議直接訪問屬性
五、與其他技術的對比
| 技術 | 用途 | 實現(xiàn)方式 |
|---|---|---|
| property | 屬性封裝與控制 | 裝飾器或 property () 函數(shù) |
| getter/setter | 傳統(tǒng)的訪問控制方法 | 顯式定義方法 |
| getattr | 動態(tài)屬性獲取 | 魔法方法(所有屬性都經(jīng)過它) |
| setattr | 動態(tài)屬性設置 | 魔法方法(所有屬性都經(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 裝飾器 |
| 復雜的屬性訪問控制 | 自定義描述符類 |
合理使用 property 可以提高代碼的安全性、可維護性和可讀性,尤其在需要控制屬性訪問邏輯的場景中表現(xiàn)出色。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Django認證系統(tǒng)user對象實現(xiàn)過程解析
這篇文章主要介紹了Django認證系統(tǒng)user對象實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-03-03
Python解析命令行讀取參數(shù)--argparse模塊使用方法
這篇文章主要介紹了Python解析命令行讀取參數(shù)--argparse模塊使用方法,需要的朋友可以參考下2018-01-01
一文帶你深入理解python中pytest-repeat插件的工作原理
這篇文章主要和大家一起深入探討到底pytest_repeat插件的具體功能是如何實現(xiàn)的呢,相信具體了解了該插件,其他三方插件也可以很快了解它內(nèi)部運行機制,所以本文詳細講解了python pytest-repeat插件的工作原理,需要的朋友可以參考下2023-09-09
python smtplib模塊自動收發(fā)郵件功能(二)
這篇文章主要為大家詳細介紹了python smtplib模塊自動收發(fā)郵件功能的第二篇,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05
在Ubuntu中安裝并配置Pycharm教程的實現(xiàn)方法
這篇文章主要介紹了在Ubuntu中安裝并配置Pycharm教程的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-01-01

