Python虛擬機中描述器的王炸應(yīng)用分享
property
當(dāng)你在編寫Python代碼時,你可能會遇到一些需要通過方法來訪問或設(shè)置的屬性。Python中的 property 裝飾器提供了一種優(yōu)雅的方式來處理這種情況,允許你將這些方法封裝為屬性,從而使代碼更加簡潔和易于閱讀。在本文中,我將向你介紹 property 裝飾器的工作原理以及如何在你的代碼中使用它。
什么是 property
Python 中的 property 是一種裝飾器,它允許你定義一個方法,使其看起來像一個屬性。換句話說,property 允許你以屬性的方式訪問或設(shè)置類的數(shù)據(jù)成員,而不必直接調(diào)用一個方法。
在 Python 中,屬性通常是一個對象的數(shù)據(jù)成員,它們可以通過直接訪問對象來獲取或設(shè)置。然而,有時候你可能需要在獲取或設(shè)置屬性時執(zhí)行某些額外的操作,例如進行類型檢查、范圍檢查或計算屬性等。在這種情況下,使用 property 裝飾器可以讓你以屬性的方式訪問或設(shè)置這些屬性,并在訪問或設(shè)置時執(zhí)行額外的操作。
如何使用 property
讓我們看一個簡單的例子,假設(shè)你正在編寫一個表示矩形的類,并且你想要在計算矩形的面積時執(zhí)行一些額外的操作。你可以使用 property 裝飾器來實現(xiàn)這個功能,如下所示:
class Rectangle: def __init__(self, width, height): self._width = width self._height = height @property def width(self): return self._width @width.setter def width(self, value): if value <= 0: raise ValueError("Width must be positive") self._width = value @property def height(self): return self._height @height.setter def height(self, value): if value <= 0: raise ValueError("Height must be positive") self._height = value @property def area(self): return self._width * self._height
在這個示例中,我們使用 property 裝飾器定義了三個屬性:width、height和area。每個屬性都有一個 getter 方法和一個 setter 方法,它們分別負責(zé)獲取和設(shè)置屬性的值。當(dāng)你使用類的實例訪問這些屬性時,你會發(fā)現(xiàn)它們似乎就像是一個普通的屬性,而不是一個方法。
注意,getter 方法沒有參數(shù),而 setter 方法接受一個參數(shù)。當(dāng)你通過類的實例訪問屬性時,你只需要使用點運算符即可訪問這些屬性,就像這樣:
rect = Rectangle(10, 20) print(rect.width) print(rect.height) print(rect.area)
輸出結(jié)果:
10
20
200
你也可以像下面這樣設(shè)置屬性的值:
rect.width = 5 rect.height = 10 print(rect.width) print(rect.height) print(rect.area)
輸出結(jié)果如下所示:
5
10
50
在設(shè)置 width 或 height 屬性的值時,會執(zhí)行對應(yīng)的 setter 方法進行類型檢查和范圍檢查。如果值不符合要求,將會拋出一個 ValueError 異常。這使得你的代碼更加健壯和可靠。
除了在屬性的 getter 和 setter 方法中執(zhí)行額外的操作外,你還可以使用 property 裝飾器計算屬性。計算屬性是指,當(dāng)你訪問屬性時,它不是從類的實例中獲取數(shù)據(jù),而是基于類的其他數(shù)據(jù)成員進行計算。例如,如果你有一個表示溫度的類,你可以定義一個計算屬性,用于將攝氏度轉(zhuǎn)換為華氏度,如下所示:
class Temperature: def __init__(self, celsius): self._celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): self._celsius = value @property def fahrenheit(self): return (self._celsius * 9/5) + 32
在這個示例中,我們定義了一個 Temperature 類,它包含一個 celsius 屬性和一個 fahrenheit 屬性。celsius 屬性是一個普通的屬性,可以直接訪問和設(shè)置。而 fahrenheit 屬性是一個計算屬性,它基于 celsius 屬性計算而來。當(dāng)你訪問 fahrenheit 屬性時,它將自動計算出相應(yīng)的華氏度并返回。你可以會對上面的代碼有點疑惑celsius.setter
是什么,他是那里來的,事實上在它上面的 @property
執(zhí)行之后 celsius 已經(jīng)不再是一個函數(shù)了,而是一個 property 的類產(chǎn)生的對象了,因此 celsius.setter
是 property 類中的 setter
屬性了,事實上他是一個類的方法了,而裝飾器 @celsius.setter
就是將 def celsius(self, value)
這個函數(shù)作為參數(shù)傳遞給方法 celsius.setter
。
我們介紹了 Python 中的 property 裝飾器,它允許你將方法封裝為屬性,并在訪問或設(shè)置屬性時執(zhí)行額外的操作。通過使用 property 裝飾器,你可以編寫更加簡潔、優(yōu)雅和可讀的代碼,同時使代碼更加健壯和可靠。
property 的本質(zhì)
property 是 python 內(nèi)置的一個類,注意它是類。在前面的內(nèi)容當(dāng)中我們已經(jīng)詳細討論過了裝飾器的原理,并且從字節(jié)碼的角度進行了分析。因此我們可以很容易理解上面 Temperature
類。我們可以將裝飾器展開:
class Temperature: def __init__(self, celsius): self._celsius = celsius def celsius1(self): return self._celsius celsius = property(celsius1) def celsius2(self, value): self._celsius = value celsius = celsius.setter(celsius2) def fahrenheit(self): return (self._celsius * 9 / 5) + 32 fahrenheit = property(fahrenheit) if __name__ == '__main__': t = Temperature(10) print(t.celsius) t.celsius = 100 print(t.celsius) print(t.fahrenheit)
上面的程序輸出結(jié)果如下所示:
10
100
212.0
可以看到上面的程序正確的輸出了結(jié)果,符合我們對與 property 的理解和使用。從上面的分析我們可以看到 property 本質(zhì)就是一個 python 的類,因此我可以完全自己實現(xiàn)一個和內(nèi)置的 property 類相同功能的類。
在 python 語言層面實現(xiàn) property 機制
具體的實現(xiàn)代碼如下所示:
class Property: "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None: doc = fget.__doc__ self.__doc__ = doc self._name = '' def __set_name__(self, owner, name): self._name = name def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError(f"property '{self._name}' has no getter") return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError(f"property '{self._name}' has no setter") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError(f"property '{self._name}' has no deleter") self.fdel(obj) def getter(self, fget): prop = type(self)(fget, self.fset, self.fdel, self.__doc__) prop._name = self._name return prop def setter(self, fset): prop = type(self)(self.fget, fset, self.fdel, self.__doc__) prop._name = self._name return prop def deleter(self, fdel): prop = type(self)(self.fget, self.fset, fdel, self.__doc__) prop._name = self._name return prop
現(xiàn)在對上面我們自己實現(xiàn)的類對象進行使用測試:
class Rectangle: def __init__(self, width, height): self._width = width self._height = height @Property def width(self): return self._width @width.setter def width(self, value): if value <= 0: raise ValueError("Width must be positive") self._width = value @Property def height(self): return self._height @height.setter def height(self, value): if value <= 0: raise ValueError("Height must be positive") self._height = value @Property def area(self): return self._width * self._height if __name__ == '__main__': rect = Rectangle(10, 20) print(rect.width) print(rect.height) print(rect.area) rect.width = 5 rect.height = 10 print(rect.width) print(rect.height) print(rect.area)
上面的程序輸出結(jié)果如下所示:
10
20
200
5
10
50
可以看到正確的輸出了結(jié)果。
現(xiàn)在我們來好好分析一下我們在上面使用到的自己實現(xiàn)的 Property
類是如何被調(diào)用的,在前面的內(nèi)容當(dāng)中我們已經(jīng)討論過了,只有類屬性才可能是描述器,我們在使用 @Property
的時候是獲取到對應(yīng)的函數(shù),更準(zhǔn)確的說是獲得對象的 get 函數(shù),然后使用 @Property
的類當(dāng)中的原來的函數(shù)就變成了 Property
對象了,后面就可以使用對象的 setter
方法了。
然后在使用 rect.width
或者 rect.height
方法的時候就活觸發(fā)描述器的機制, rect 對象就會被傳入到描述器的 __get__
方法,然后在這個方法當(dāng)中將傳入的對象再傳給之前得到的 fget
函數(shù),就完美的實現(xiàn)了我們想要的效果。
classmethod 和 staticmethod
在 Python 中,staticmethod 和 classmethod 是兩個常用的裝飾器,它們分別用于定義靜態(tài)方法和類方法。
staticmethod
staticmethod 是一個裝飾器,它可以將一個函數(shù)定義為靜態(tài)方法。靜態(tài)方法與類實例無關(guān),可以在不創(chuàng)建類實例的情況下直接調(diào)用,但它們?nèi)匀豢梢酝ㄟ^類名訪問。
下面是一個簡單的示例:
class MyClass: @staticmethod def my_static_method(x, y): return x + y print(MyClass.my_static_method(1, 2))
在這個示例中,我們定義了一個 MyClass 類,并使用 @staticmethod 裝飾器將 my_static_method 方法定義為靜態(tài)方法。然后我們可以通過 MyClass.my_static_method(1, 2) 直接調(diào)用該方法,而不需要創(chuàng)建 MyClass 的實例。需要注意的是,靜態(tài)方法沒有對類或?qū)嵗M行任何修改,因此它們通常用于一些獨立的、無狀態(tài)的函數(shù),或者在類中定義的一些幫助函數(shù)。
那么 staticmethod 是如何在語法層面實現(xiàn)的呢?這又離不開描述器了,在上面的代碼當(dāng)中我們使用 staticmethod
裝飾函數(shù) my_static_method
然后在類 MyClass
當(dāng)中會有一個類 staticmethod 的對象,且名字為 my_static_method 。我們需要注意到的是上面的過程用一行代碼表示為 my_static_method = staticmethod(my_static_method)
,傳入的 my_static_method 就是 my_static_method 函數(shù),那么這就很簡單了,當(dāng)使用 my_static_method 的屬性時候,我們可以在描述器的函數(shù) __get__
當(dāng)中直接返回傳入的函數(shù)即可。
我們自己實現(xiàn)的 StaticMethod 如下所示:
class StaticMethod: "Emulate PyStaticMethod_Type() in Objects/funcobject.c" def __init__(self, f): self.f = f f = functools.update_wrapper(self, f) def __get__(self, obj, objtype=None): return self.f def __call__(self, *args, **kwds): return self.f(*args, **kwds)
我們使用上面自己實現(xiàn)的類:
class MyClass(object): @StaticMethod def demo(): return "demo" if __name__ == '__main__': a = MyClass() print(a.demo())
上面的程序會輸出字符串 "demo"
。
classmethod
classmethod 是另一個裝飾器,它可以將一個函數(shù)定義為類方法。類方法與靜態(tài)方法類似,但它們接收的第一個參數(shù)是類對象而不是實例對象。類方法通常用于實現(xiàn)與類有關(guān)的操作,如工廠方法或構(gòu)造函數(shù)。
下面是一個使用 classmethod 的示例:
class MyClass: num_instances = 0 def __init__(self): MyClass.num_instances += 1 @classmethod def get_num_instances(cls): return cls.num_instances obj1 = MyClass() obj2 = MyClass() print(MyClass.get_num_instances())
在這個示例中,我們定義了一個 MyClass 類,它包含一個類變量 num_instances 和一個構(gòu)造函數(shù)。然后,我們使用 @classmethod 裝飾器將 get_num_instances 方法定義為類方法,并將 cls 參數(shù)用于訪問類變量 num_instances。
在創(chuàng)建 MyClass 的兩個實例后,我們調(diào)用 MyClass.get_num_instances() 來獲取當(dāng)前創(chuàng)建的實例數(shù)。因為我們使用了類方法,所以可以直接通過類名調(diào)用該方法。
需要注意的是,類方法可以在類和實例之間共享,因為它們都可以訪問類變量。另外,類方法可以被子類繼承和重寫,因為它們接收的第一個參數(shù)是類對象,而不是固定的類名。
在小節(jié)中,我們介紹了 Python 中的兩種常用裝飾器,即 staticmethod 和 classmethod。staticmethod 用于定義與類實例無關(guān)的靜態(tài)方法,而 classmethod 用于定義與類相關(guān)的操作,如工廠方法或構(gòu)造函數(shù)。兩種裝飾器都可以通過類名進行訪問,但 classmethod 還可以被子類繼承和重寫,因為它們接收的第一個參數(shù)是類對象。
需要注意的是,staticmethod 和 classmethod 都可以被類或?qū)嵗{(diào)用,但它們不同的是,classmethod 的第一個參數(shù)是類對象,而 staticmethod 沒有這樣的參數(shù)。因此,classmethod 可以訪問類變量,而 staticmethod 不能訪問類變量。
下面是一個更具體的比較:
class MyClass: class_var = 'class_var' @staticmethod def static_method(): print('This is a static method') @classmethod def class_method(cls): print('This is a class method') print(f'The class variable is: {cls.class_var}') obj = MyClass() # 靜態(tài)方法可以被類或?qū)嵗{(diào)用 MyClass.static_method() obj.static_method() # 類方法可以被類或?qū)嵗{(diào)用,并且可以訪問類變量 MyClass.class_method() obj.class_method()
在這個示例中,我們定義了一個 MyClass 類,并分別定義了靜態(tài)方法和類方法。在調(diào)用靜態(tài)方法時,我們可以使用類名或?qū)嵗M行調(diào)用,因為靜態(tài)方法與類或?qū)嵗裏o關(guān)。而在調(diào)用類方法時,我們必須使用類名或?qū)嵗M行調(diào)用,并且類方法可以訪問類變量。總的來說,staticmethod 和 classmethod 是 Python 中兩個非常有用的裝飾器,它們可以幫助我們更好地組織和管理代碼。需要根據(jù)實際情況來選擇使用哪種裝飾器,以便實現(xiàn)最佳的代碼設(shè)計和可維護性。
同樣的道理我們可以實現(xiàn)自己的 ClassMethod
class ClassMethod: "Emulate PyClassMethod_Type() in Objects/funcobject.c" def __init__(self, f): self.f = f functools.update_wrapper(self, f) def __get__(self, obj, cls=None): if cls is None: cls = type(obj) return MethodType(self.f, cls)
我們對上面的代碼進行測試:
class Myclass: @ClassMethod def demo(cls): return "demo" if __name__ == '__main__': a = Myclass() print(a.demo())
上面的代碼可以正確的輸出字符串"demo"
。
總結(jié)
在本篇文章當(dāng)中主要給大家介紹了描述器的三個應(yīng)用,仔細介紹了這三個類的使用方法,并且詳細介紹了如何使用 python 實現(xiàn)同樣的效果,這對于我們深入理解 python 面向?qū)ο缶幊谭浅S袔椭覀兛梢岳斫夂芏嗪诳萍嫉膬?nèi)容,對于整個類的語法有更加深入的理解。
以上就是Python虛擬機中描述器的王炸應(yīng)用分享的詳細內(nèi)容,更多關(guān)于Python虛擬機描述器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python+opencv實現(xiàn)動態(tài)物體追蹤
這篇文章主要為大家詳細介紹了python+opencv實現(xiàn)動態(tài)物體的追蹤,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01python 利用已有Ner模型進行數(shù)據(jù)清洗合并代碼
今天小編就為大家分享一篇python 利用已有Ner模型進行數(shù)據(jù)清洗合并代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12Python實現(xiàn)根據(jù)指定端口探測服務(wù)器/模塊部署的方法
這篇文章主要介紹了Python根據(jù)指定端口探測服務(wù)器/模塊部署的方法,非常具有實用價值,需要的朋友可以參考下2014-08-08python數(shù)據(jù)結(jié)構(gòu):數(shù)據(jù)類型
這篇文章主要介紹了python數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)類型,在?Python?以及其他所有面向?qū)ο缶幊陶Z言中,類都是對數(shù)據(jù)的構(gòu)成(狀態(tài))以及數(shù)據(jù)?能做什么(行為)的描述,下面我們就來你看看python數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)類型商務(wù)詳細介紹,需要的小伙伴可以參考一下2021-12-12