Python @property 詳解

来源:https://www.cnblogs.com/gscnblog/archive/2019/02/12/10366604.html
-Advertisement-
Play Games

本文講解了 Python 的 property 特性,即一種符合 Python 哲學地設置 getter 和 setter 的方式。 ...


本文講解了 Python 的 property 特性,即一種符合 Python 哲學地設置 getter 和 setter 的方式。

Python 有一個概念叫做 property,它能讓你在 Python 的面向對象編程中輕鬆不少。在瞭解它之前,我們先看一下為什麼 property 會被提出。

一個簡單的例子

比如說你要創建一個溫度的類Celsius,它能存儲攝氏度,也能轉換為華氏度。即:

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

我們可以使用這個類:

>>> # 創建對象 man
>>> man = Celsius()

>>> # 設置溫度
>>> man.temperature = 37

>>> # 獲取溫度
>>> man.temperature
37

>>> # 獲取華氏度
>>> man.to_fahrenheit()
98.60000000000001

最後額外的小數部分是浮點誤差,屬於正常現象,你可以在 Python 里試一下 1.1 + 2.2

在 Python 里,當我們對一個對象的屬性進行賦值或估值時(如上面的temperature),Python 實際上是在這個對象的 __dict__字典里搜索這個屬性來操作。

>>> man.__dict__
{'temperature': 37}

因此,man.temperature實際上被轉換成了man.__dict__['temperature']

假設我們這個類被程式員廣泛的應用了,他們在數以千計的客戶端代碼里使用了我們的類,你很高興。

突然有一天,有個人跑過來說,溫度不可能低於零下273度,這個類應該加上對溫度的限制。這個建議當然應該被採納。作為一名經驗豐富的程式員,你立刻想到應該使用 setter 和 getter 來限制溫度,於是你將代碼改成下麵這樣:

class Celsius:
    def __init__(self, temperature = 0):
        self.set_temperature(temperature)

    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32

    # 更新部分
    def get_temperature(self):
        return self._temperature

    def set_temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        self._temperature = value

很自然地,你使用了“私有變數”_temperature來存儲溫度,使用get_temperature()set_temperature()提供了訪問_temperature的介面,在這個過程中對溫度值進行條件判斷,防止它超過限制。這都很好。

問題是,這樣一來,使用你的類的程式員們需要把他們的代碼中無數個obj.temperature = val改為obj.set_temperature(val),把obj.temperature改為obj.get_temperature()。這種重構實在令人頭痛。

所以,這種方法不是“向下相容”的,我們要另闢蹊徑。

@property 的威力!

想要使用 Python 哲學來解決這個問題,就使用 property。直接看代碼:

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    def get_temperature(self):
        print("Getting value")
        return self._temperature

    def set_temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value

    # 重點在這裡
    temperature = property(get_temperature,set_temperature)

我們在class Celsius的最後一行使用了一個 Python 內置函數(類) property()。它接受兩個函數作為參數,一個 getter,一個 setter,並且返回一個 property 對象(這裡是temperature)。

這樣以後,任何訪問temperature的代碼都會自動轉而運行get_temperature(),任何對temperature賦值的代碼都會自動轉而運行set_temperature()我們在代碼裡加了print()便於測試它們的運行狀態。

>>> c = Celsius()  # 此時會運行 setter,因為 __init__ 里對 temperature 進行了賦值
Setting value

>>> c.temperature  # 此時會運行 getter,因為對 temperature 進行了訪問
Getting value
0

需要註意的是,實際的溫度存儲在_temperature里,temperature只是提供一個訪問的介面。

深入瞭解 Property

正如之前提到的,property()是 Python 的一個內置函數,同時它也是一個類。函數簽名為:

property(fget=None, fset=None, fdel=None, doc=None)

其中,fget是一個 getter 函數,fset是一個 setter 函數,fdel是刪除該屬性的函數,doc是一個字元串,用作註釋。函數返回一個 property 對象。

一個 property 對象有 getter()setter()deleter()三個方法用來指定相應綁定的函數。之前的

temperature = property(get_temperature,set_temperature)

實際上等價於

# 創建一個空的 property 對象
temperature = property()
# 綁定 getter
temperature = temperature.getter(get_temperature)
# 綁定 setter
temperature = temperature.setter(set_temperature)

這兩個代碼塊等價。

熟悉 Python 裝飾器的程式員肯定已經想到,上面的 property 可以用裝飾器來實現。

通過裝飾器@property,我們可以不定義沒有必要的 get_temperature()set_temperature(),這樣還避免了污染命名空間。使用方式如下:

class Celsius:
    def __init__(self, temperature = 0):
        self._temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    # Getter 裝飾器
    @property
    def temperature(self):
        print("Getting value")
        return self._temperature

    # Setter 裝飾器
    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value

你可以使用裝飾器,也可以使用之前的方法,完全看個人喜好。但使用裝飾器應該是更加 Pythonic 的方法吧。

參考

Python @property

(本文完)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • udp: 1.創建套接字 socket 2.綁定本地ip/port bind 3.收發數據 sendto/recvfrom 4.關閉套接字 close tcp客戶端: 1.創建套接字 socket 2.連接服務端 connect 3.收發數據 send/recv 4.關閉套接字 close tcp服 ...
  • “學電腦一定要有一個非常強大的心理狀態,電腦不是黑魔法,都是人想出來的,別人能夠想的出來,那麼,總有一天,我也能夠想的出來。” 指針類型的變數就是保存地址的變數。 int* p=&i;------P是一個指針,P裡面的內容為變數i的地址,即說P指向了i; int* p,q;------註意:*號... ...
  • 很多時候我們會發現輸入的一長串內容不得不全部刪除重新輸入,這時比起一直按著退格鍵不放一個清除內容按鈕更受歡迎。 今天我將介紹三種為QLineEdit添加清除內容按鈕的方法,其中兩種方法有較強的功能針對性,另一種方法則是通用的,不僅可以用來實現清除輸入內容,還可以擴展出其他功能。 本文索引 方法1:s ...
  • os.walk(top,topdown=True,onerror=None,followlinks=False) os.walk()是python中內置(built-in)的目錄樹生成(directory tree generator)函數。 對於每一個在top目錄下的子目錄(包括top目錄本身), ...
  • 1.項目啟動類application.java類名上增加@EnableEurekaServer註解,聲明是註冊中心 1 import org.springframework.boot.SpringApplication; 2 import org.springframework.boot.autoc ...
  • 平時我們使用最多的數據結構肯定是 HashMap,但是在使用的時候我們必須知道每個鍵值對的生命周期,並且手動清除它;但是如果我們不是很清楚它的生命周期,這時候就比較麻煩;通常有這樣幾種處理方式: 由一個線程定時處理,可以是 或者 ; 利用重寫 ,實現 FIFOCache 或者 LRUCache;可以 ...
  • 題目: “一幫一學習小組”是中小學中常見的學習組織方式,老師把學習成績靠前的學生跟學習成績靠後的學生排在一組。本題就請你編寫程式幫助老師自動完成這個分配工作,即在得到全班學生的排名後,在當前尚未分組的學生中,將名次最靠前的學生與名次最靠後的異性學生分為一組。 輸入格式: 輸入第一行給出正偶數N(≤5 ...
  • C/S結構瞭解 所謂的C/S就是客戶端(client)和伺服器端(server)的簡稱,也就是在基於這個的基礎上編寫相關的代碼;一個就是客戶端一個就是服務端。 TCP(client) 客戶端編寫 因為是在Python2.7的版本所以使用該socket包 import socket 定義地址和埠 t ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...