搜尋
首頁後端開發Python教學Python屬性和描述符的使用

關於@property裝飾器

在Python中我們使用@property裝飾器來把對函數的呼叫偽裝成對屬性的存取。

那為什麼要這樣做呢?因為@property讓我們將自訂的程式碼同變數的存取/設定聯繫在了一起,同時為你的類別保持一個簡單的存取屬性的介面。

舉個栗子,假如我們有一個需要表示電影的類別:

class Movie(object):
 def __init__(self, title, description, score, ticket):
 self.title = title
 self.description = description
 self.score = scroe
 self.ticket = ticket

你開始在專案的其他地方使用這個類,但之後你意識到:如果你不小心給電影打了負分怎麼辦?你覺得這是錯誤的行為,希望Movie類別可以阻止這個錯誤。 你首先想到的辦法是將Movie類別修改為這樣:

class Movie(object):
 def __init__(self, title, description, score, ticket):
 self.title = title
 self.description = description
     self.ticket = ticket
 if score <p></p><p>但這行不通。因為其他部分的程式碼都是直接透過Movie.score來賦值的。這個新修改的類別只會在__init__方法中捕獲錯誤的數據,但對於已經存在的類別實例就無能為力了。如果有人試著跑m.scrore= -100,那麼誰也沒辦法阻止。那該怎麼辦? </p><p>Python的property解決了這個問題。 </p><p>我們可以這樣做</p><p></p><pre class="brush:php;toolbar:false">class Movie(object):
 def __init__(self, title, description, score):
 self.title = title
 self.description = description
 self.score = score
     self.ticket = ticket
 
 @property
 def score(self):
 return self.__score
 
 
 @score.setter
 def score(self, score):
 if score <p></p><p>這樣在任何地方修改score都會偵測它是否小於0。 </p><p>property的不足</p><p>對property來說,最大的缺點就是它們不能重複使用。舉個例子,假設你想為ticket欄位也加上非負檢定。 </p><p>下面是修改過的新類別:</p><p></p><pre class="brush:php;toolbar:false">class Movie(object):
 def __init__(self, title, description, score, ticket):
 self.title = title
 self.description = description
 self.score = score
 self.ticket = ticket
 
 @property
 def score(self):
 return self.__score
 
 
 @score.setter
 def score(self, score):
 if score <p></p><p>#可以看到程式碼增加了不少,但重複的邏輯也出現了不少。雖然property可以讓類別從外部看起來介面整潔漂亮,但卻做不到內部同樣整潔漂亮。 </p><p>描述符登場</p><p>什麼是描述符? </p><p>一般來說,描述子是一個具有綁定行為的物件屬性,其屬性的存取被描述符協定方法覆寫。這些方法是__get__()  、 __set__()和__delete__() ,一個物件中只要包含了這三個方法中的至少一個就稱它為描述符。 </p><p>描述子有什麼作用? </p><p>The default behavior for attribute access is to get, set, or delete the attribute from an object's dictionary. For instance, a.x has a lookup chain starting witha.__dict__['x'], then has a lookup chain starting witha.__dict__['x'], then type(a) .__dict__['x'], and continuing through the base classes of type(a) excluding metaclasses. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default beiord instead. Where this occurs in the precedence chain depends on which descriptor methods were defined.—–摘自官方文檔</p><p>簡單的說描述符會改變一個屬性的基本的獲取、設置和刪除方式。 </p><p>先看如何用描述子來解決上面 property邏輯重複的問題。 </p><p></p><pre class="brush:php;toolbar:false">class Integer(object):
 def __init__(self, name):
 self.name = name
 
 def __get__(self, instance, owner):
 return instance.__dict__[self.name]
 
 def __set__(self, instance, value):
 if value <p></p><p>因為描述符優先級高並且會改變預設的get、set行為,這樣一來,當我們訪問或設定Movie().score的時候都會受到描述符Integer的限制。 </p><p>不過我們總不能用下面這樣的方式來建立實例。 </p><p></p><pre class="brush:php;toolbar:false">a = Movie()
a.score = 1
a.ticket = 2
a.title = ‘test'
a.descript = ‘…'

這樣太生硬了,所以我們還缺少一個建構子。

class Integer(object):
 def __init__(self, name):
 self.name = name
 
 def __get__(self, instance, owner):
 if instance is None:
  return self
 return instance.__dict__[self.name]
 
 def __set__(self, instance, value):
 if value <p></p><p>這樣在取得、設定和刪除score和ticket的時候都會進入Integer的__get__ 、 __set__ ,從而減少了重複的邏輯。 </p><p>現在雖然問題得到了解決,但是你可能會好奇這個描述符到底是如何運作的。具體來說,在__init__函數裡訪問的是自己的self.score和self.ticket,怎麼和類別屬性score和ticket關聯起來的? </p><p>描述子如何運作</p><p>看官方的說明</p><p>If an object defines both __get__() and __set__(), it is considered a data descriptor. Descriptors that only define __get__() are called non-data descriptors (they are typically used for methods but other uses are possible).</p><p>Data and non-data descriptors differ in how overrides are calculated with respect to ents in If an instance's dictionary has an entry with the same name as a data descriptor, the data descriptor takes precedence. If an instance's dictionary has an entry with the same name as a non-data descriptor the dicary cry. ##The important points to remember are:</p><p>descriptors are invoked by the __getattribute__() method</p>overriding __getattribute__() prevents automatic descriptor calls__##object.__getattribute(mtribute) 和 mtyped. different calls to __get__().<p>data descriptors always override instance dictionaries.<br>non-data descriptors may be overridden by instance dictionaries.<br><br>##類__(__getattribute__()的時候是大概是類別__()的時候是下面這樣子:<br><br></p><pre class="brush:php;toolbar:false">def __getattribute__(self, key):
 "Emulate type_getattro() in Objects/typeobject.c"
 v = object.__getattribute__(self, key)
 if hasattr(v, '__get__'):
 return v.__get__(None, self)
 return v

以下是摘自國外一篇部落格上的內容。

Given a Class “C” and an Instance “c” where “c = C(…)”, calling “c.name” means looking up an Attribute “name” on the Instance “c” like this :

Get the Class from Instance
Call the Class's special method getattribute__. All objects have a default __getattribute
Inside getattribute

Get the Class's mro as ClassParents
For each ClassParent in ClassParents
If the Attribute is in the ClassParent's dict
If is a data descriptor
Return the result from calling the data descriptor's special method __get__()
Break the for each (do not continue searching the same Attribute any further)
If the Attribute is in Instance's dict
Return the value as it is (even if the value is a data descriptor)
For each ClassParent in ClassParents
If the Attribute is in the ClassParent's dict
If is a non-data descriptor
Return the result from calling the non-data descriptor's special method __get__()
If it is NOT a descriptor
Return the value
If Class has the special method getattr
Return the result from calling the Class's special method__getattr__.

我对上面的理解是,访问一个实例的属性的时候是先遍历它和它的父类,寻找它们的__dict__里是否有同名的data descriptor如果有,就用这个data descriptor代理该属性,如果没有再寻找该实例自身的__dict__ ,如果有就返回。任然没有再查找它和它父类里的non-data descriptor,最后查找是否有__getattr__

描述符的应用场景

python的property、classmethod修饰器本身也是一个描述符,甚至普通的函数也是描述符(non-data discriptor)

django model和SQLAlchemy里也有描述符的应用

class User(db.Model):
 id = db.Column(db.Integer, primary_key=True)
 username = db.Column(db.String(80), unique=True)
 email = db.Column(db.String(120), unique=True)
 
 def __init__(self, username, email):
 self.username = username
 self.email = email
 
 def __repr__(self):
 return '<user>' % self.username</user>

总结

只有当确实需要在访问属性的时候完成一些额外的处理任务时,才应该使用property。不然代码反而会变得更加啰嗦,而且这样会让程序变慢很多。以上就是本文的全部内容,由于个人能力有限,文中如有笔误、逻辑错误甚至概念性错误,还请提出并指正。

更多Python属性和描述符的使用相关文章请关注PHP中文网!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
列表和陣列之間的選擇如何影響涉及大型數據集的Python應用程序的整體性能?列表和陣列之間的選擇如何影響涉及大型數據集的Python應用程序的整體性能?May 03, 2025 am 12:11 AM

ForhandlinglargedatasetsinPython,useNumPyarraysforbetterperformance.1)NumPyarraysarememory-efficientandfasterfornumericaloperations.2)Avoidunnecessarytypeconversions.3)Leveragevectorizationforreducedtimecomplexity.4)Managememoryusagewithefficientdata

說明如何將內存分配給Python中的列表與數組。說明如何將內存分配給Python中的列表與數組。May 03, 2025 am 12:10 AM

Inpython,ListSusedynamicMemoryAllocationWithOver-Asalose,而alenumpyArraySallaySallocateFixedMemory.1)listssallocatemoremoremoremorythanneededinentientary上,respizeTized.2)numpyarsallaysallaysallocateAllocateAllocateAlcocateExactMemoryForements,OfferingPrediCtableSageButlessemageButlesseflextlessibility。

您如何在Python數組中指定元素的數據類型?您如何在Python數組中指定元素的數據類型?May 03, 2025 am 12:06 AM

Inpython,YouCansspecthedatatAtatatPeyFelemereModeRernSpant.1)Usenpynernrump.1)Usenpynyp.dloatp.dloatp.ploatm64,formor professisconsiscontrolatatypes。

什麼是Numpy,為什麼對於Python中的數值計算很重要?什麼是Numpy,為什麼對於Python中的數值計算很重要?May 03, 2025 am 12:03 AM

NumPyisessentialfornumericalcomputinginPythonduetoitsspeed,memoryefficiency,andcomprehensivemathematicalfunctions.1)It'sfastbecauseitperformsoperationsinC.2)NumPyarraysaremorememory-efficientthanPythonlists.3)Itoffersawiderangeofmathematicaloperation

討論'連續內存分配”的概念及其對數組的重要性。討論'連續內存分配”的概念及其對數組的重要性。May 03, 2025 am 12:01 AM

Contiguousmemoryallocationiscrucialforarraysbecauseitallowsforefficientandfastelementaccess.1)Itenablesconstanttimeaccess,O(1),duetodirectaddresscalculation.2)Itimprovescacheefficiencybyallowingmultipleelementfetchespercacheline.3)Itsimplifiesmemorym

您如何切成python列表?您如何切成python列表?May 02, 2025 am 12:14 AM

SlicingaPythonlistisdoneusingthesyntaxlist[start:stop:step].Here'showitworks:1)Startistheindexofthefirstelementtoinclude.2)Stopistheindexofthefirstelementtoexclude.3)Stepistheincrementbetweenelements.It'susefulforextractingportionsoflistsandcanuseneg

在Numpy陣列上可以執行哪些常見操作?在Numpy陣列上可以執行哪些常見操作?May 02, 2025 am 12:09 AM

numpyallowsforvariousoperationsonArrays:1)basicarithmeticlikeaddition,減法,乘法和division; 2)evationAperationssuchasmatrixmultiplication; 3)element-wiseOperations wiseOperationswithOutexpliitloops; 4)

Python的數據分析中如何使用陣列?Python的數據分析中如何使用陣列?May 02, 2025 am 12:09 AM

Arresinpython,尤其是Throughnumpyandpandas,weessentialFordataAnalysis,offeringSpeedAndeffied.1)NumpyArseNable efflaysenable efficefliceHandlingAtaSetSetSetSetSetSetSetSetSetSetSetsetSetSetSetSetsopplexoperationslikemovingaverages.2)

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器