亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

目錄
描述符的定義
描述符基礎(chǔ)
描述符的原理
描述符觸發(fā)
描述符優(yōu)先級(jí)
Property
在運(yùn)行時(shí)創(chuàng)建描述符
靜態(tài)方法和類方法
靜態(tài)方法
類方法
其他的魔術(shù)方法
__getattr__
應(yīng)用
__getitem__
References
引言
首頁(yè) 后端開(kāi)發(fā) Python教程 Python黑魔法之描述符

Python黑魔法之描述符

Feb 09, 2017 am 10:52 AM
python

引言

Descriptors(描述符)是Python語(yǔ)言中一個(gè)深?yuàn)W但很重要的一個(gè)黑魔法,它被廣泛應(yīng)用于Python語(yǔ)言的內(nèi)核,熟練掌握描述符將會(huì)為Python程序員的工具箱添加一個(gè)額外的技巧。本文我將講述描述符的定義以及一些常見(jiàn)的場(chǎng)景,并且在文末會(huì)補(bǔ)充一下__getattr__,__getattribute__, __getitem__這三個(gè)同樣涉及到屬性訪問(wèn)的魔術(shù)方法。

描述符的定義

descr__get__(self,?obj,?objtype=None)?-->?value

descr.__set__(self,?obj,?value)?-->?None

descr.__delete__(self,?obj)?-->?None

只要一個(gè)object attribute(對(duì)象屬性)定義了上面三個(gè)方法中的任意一個(gè),那么這個(gè)類就可以被稱為描述符類。

描述符基礎(chǔ)

下面這個(gè)例子中我們創(chuàng)建了一個(gè)RevealAcess類,并且實(shí)現(xiàn)了__get__方法,現(xiàn)在這個(gè)類可以被稱為一個(gè)描述符類。

class?RevealAccess(object):
????def?__get__(self,?obj,?objtype):
????????print('self?in?RevealAccess:?{}'.format(self))
????????print('self:?{}\nobj:?{}\nobjtype:?{}'.format(self,?obj,?objtype))


class?MyClass(object):
????x?=?RevealAccess()
????def?test(self):
????????print('self?in?MyClass:?{}'.format(self))

EX1實(shí)例屬性

接下來(lái)我們來(lái)看一下__get__方法的各個(gè)參數(shù)的含義,在下面這個(gè)例子中,self即RevealAccess類的實(shí)例x,obj即MyClass類的實(shí)例m,objtype顧名思義就是MyClass類自身。從輸出語(yǔ)句可以看出,m.x訪問(wèn)描述符x會(huì)調(diào)用__get__方法。

>>>?m?=?MyClass()
>>>?m.test()
self?in?MyClass:?<__main__.MyClass object at 0x7f19d4e42160>

>>>?m.x
self?in?RevealAccess:?<__main__.RevealAccess object at 0x7f19d4e420f0>
self:?<__main__.RevealAccess object at 0x7f19d4e420f0>
obj:?<__main__.MyClass object at 0x7f19d4e42160>
objtype:?<class &#39;__main__.MyClass&#39;>

EX2類屬性

如果通過(guò)類直接訪問(wèn)屬性x,那么obj接直接為None,這還是比較好理解,因?yàn)椴淮嬖贛yClass的實(shí)例。

>>>?MyClass.x
self?in?RevealAccess:?<__main__.RevealAccess object at 0x7f53651070f0>
self:?<__main__.RevealAccess object at 0x7f53651070f0>
obj:?None
objtype:?<class &#39;__main__.MyClass&#39;>

描述符的原理

描述符觸發(fā)

上面這個(gè)例子中,我們分別從實(shí)例屬性和類屬性的角度列舉了描述符的用法,下面我們來(lái)仔細(xì)分析一下內(nèi)部的原理:

  • 如果是對(duì)實(shí)例屬性進(jìn)行訪問(wèn),相當(dāng)于調(diào)用了object.__getattribute__(),它將obj.d轉(zhuǎn)譯成了type(obj).__dict__['d'].__get__(obj, type(obj))。

  • 如果是對(duì)類屬性進(jìn)行訪問(wèn),相當(dāng)于調(diào)用了type.__getattribute__(),它將cls.d轉(zhuǎn)譯成了cls.__dict__['d'].__get__(None, cls),轉(zhuǎn)換成Python代碼就是:

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

簡(jiǎn)單講一下__getattribute__魔術(shù)方法,這個(gè)方法在我們?cè)L問(wèn)一個(gè)對(duì)象的屬性的時(shí)候會(huì)被無(wú)條件調(diào)用,詳細(xì)的細(xì)節(jié)比如和__getattr, __getitem__的區(qū)別我會(huì)在文章的末尾做一個(gè)額外的補(bǔ)充,我們暫時(shí)并不深究。

描述符優(yōu)先級(jí)

首先,描述符分為兩種:

  • 如果一個(gè)對(duì)象同時(shí)定義了__get__()和__set__()方法,則這個(gè)描述符被稱為data descriptor。

  • 如果一個(gè)對(duì)象只定義了__get__()方法,則這個(gè)描述符被稱為non-data descriptor。

我們對(duì)屬性進(jìn)行訪問(wèn)的時(shí)候存在下面四種情況:

  • data descriptor

  • instance dict

  • non-data descriptor

  • __getattr__()

它們的優(yōu)先級(jí)大小是:

data?descriptor?>?instance?dict?>?non-data?descriptor?>?__getattr__()

這是什么意思呢?就是說(shuō)如果實(shí)例對(duì)象obj中出現(xiàn)了同名的data descriptor->d 和 instance attribute->d,obj.d對(duì)屬性d進(jìn)行訪問(wèn)的時(shí)候,由于data descriptor具有更高的優(yōu)先級(jí),Python便會(huì)調(diào)用type(obj).__dict__['d'].__get__(obj, type(obj))而不是調(diào)用obj.__dict__['d']。但是如果描述符是個(gè)non-data descriptor,Python則會(huì)調(diào)用obj.__dict__['d']。

Property

每次使用描述符的時(shí)候都定義一個(gè)描述符類,這樣看起來(lái)非常繁瑣。Python提供了一種簡(jiǎn)潔的方式用來(lái)向?qū)傩蕴砑訑?shù)據(jù)描述符。

property(fget=None,?fset=None,?fdel=None,?doc=None)?->?property?attribute

fget、fset和fdel分別是類的getter、setter和deleter方法。我們通過(guò)下面的一個(gè)示例來(lái)說(shuō)明如何使用Property:

class?Account(object):

????def?__init__(self):
????????self._acct_num?=?None

????def?get_acct_num(self):
????????return?self._acct_num

????def?set_acct_num(self,?value):
????????self._acct_num?=?value

????def?del_acct_num(self):
????????del?self._acct_num

????acct_num?=?property(get_acct_num,?set_acct_num,?del_acct_num,?'_acct_num?property.')

如果acct是Account的一個(gè)實(shí)例,acct.acct_num將會(huì)調(diào)用getter,acct.acct_num = value將調(diào)用setter,del acct_num.acct_num將調(diào)用deleter。

>>>?acct?=?Account()
>>>?acct.acct_num?=?1000
>>>?acct.acct_num
1000

Python也提供了@property裝飾器,對(duì)于簡(jiǎn)單的應(yīng)用場(chǎng)景可以使用它來(lái)創(chuàng)建屬性。一個(gè)屬性對(duì)象擁有g(shù)etter,setter和deleter裝飾器方法,可以使用它們通過(guò)對(duì)應(yīng)的被裝飾函數(shù)的accessor函數(shù)創(chuàng)建屬性的拷貝。

class?Account(object):

????def?__init__(self):
????????self._acct_num?=?None

????@property
?????#?the?_acct_num?property.?the?decorator?creates?a?read-only?property
????def?acct_num(self):
????????return?self._acct_num

????@acct_num.setter
????#?the?_acct_num?property?setter?makes?the?property?writeable
????def?set_acct_num(self,?value):
????????self._acct_num?=?value

????@acct_num.deleter
????def?del_acct_num(self):
????????del?self._acct_num

如果想讓屬性只讀,只需要去掉setter方法。

在運(yùn)行時(shí)創(chuàng)建描述符

我們可以在運(yùn)行時(shí)添加property屬性:

class?Person(object):

????def?addProperty(self,?attribute):
????????#?create?local?setter?and?getter?with?a?particular?attribute?name
????????getter?=?lambda?self:?self._getProperty(attribute)
????????setter?=?lambda?self,?value:?self._setProperty(attribute,?value)

????????#?construct?property?attribute?and?add?it?to?the?class
????????setattr(self.__class__,?attribute,?property(fget=getter,?\
????????????????????????????????????????????????????fset=setter,?\
????????????????????????????????????????????????????doc="Auto-generated?method"))

????def?_setProperty(self,?attribute,?value):
????????print("Setting:?{}?=?{}".format(attribute,?value))
????????setattr(self,?'_'?+?attribute,?value.title())

????def?_getProperty(self,?attribute):
????????print("Getting:?{}".format(attribute))
????????return?getattr(self,?'_'?+?attribute)
>>>?user?=?Person()
>>>?user.addProperty('name')
>>>?user.addProperty('phone')
>>>?user.name?=?'john?smith'
Setting:?name?=?john?smith
>>>?user.phone?=?'12345'
Setting:?phone?=?12345
>>>?user.name
Getting:?name
'John?Smith'
>>>?user.__dict__
{'_phone':?'12345',?'_name':?'John?Smith'}

靜態(tài)方法和類方法

我們可以使用描述符來(lái)模擬Python中的@staticmethod和@classmethod的實(shí)現(xiàn)。我們首先來(lái)瀏覽一下下面這張表:

Transformation Called from an Object Called from a Class
function f(obj, *args) f(*args)
staticmethod f(*args) f(*args)
classmethod f(type(obj), *args) f(klass, *args)

靜態(tài)方法

對(duì)于靜態(tài)方法f。c.f和C.f是等價(jià)的,都是直接查詢object.__getattribute__(c, ‘f’)或者object.__getattribute__(C, ’f‘)。靜態(tài)方法一個(gè)明顯的特征就是沒(méi)有self變量。

靜態(tài)方法有什么用呢?假設(shè)有一個(gè)處理專門數(shù)據(jù)的容器類,它提供了一些方法來(lái)求平均數(shù),中位數(shù)等統(tǒng)計(jì)數(shù)據(jù)方式,這些方法都是要依賴于相應(yīng)的數(shù)據(jù)的。但是類中可能還有一些方法,并不依賴這些數(shù)據(jù),這個(gè)時(shí)候我們可以將這些方法聲明為靜態(tài)方法,同時(shí)這也可以提高代碼的可讀性。

使用非數(shù)據(jù)描述符來(lái)模擬一下靜態(tài)方法的實(shí)現(xiàn):

class?StaticMethod(object):
????def?__init__(self,?f):
????????self.f?=?f

????def?__get__(self,?obj,?objtype=None):
????????return?self.f

我們來(lái)應(yīng)用一下:

class?MyClass(object):
????@StaticMethod
????def?get_x(x):
????????return?x

print(MyClass.get_x(100))??#?output:?100

類方法

Python的@classmethod和@staticmethod的用法有些類似,但是還是有些不同,當(dāng)某些方法只需要得到類的引用而不關(guān)心類中的相應(yīng)的數(shù)據(jù)的時(shí)候就需要使用classmethod了。

使用非數(shù)據(jù)描述符來(lái)模擬一下類方法的實(shí)現(xiàn):

class?ClassMethod(object):
????def?__init__(self,?f):
????????self.f?=?f

????def?__get__(self,?obj,?klass=None):
????????if?klass?is?None:
????????????klass?=?type(obj)
????????def?newfunc(*args):
????????????return?self.f(klass,?*args)
????????return?newfunc

其他的魔術(shù)方法

首次接觸Python魔術(shù)方法的時(shí)候,我也被__get__, __getattribute__, __getattr__, __getitem__之間的區(qū)別困擾到了,它們都是和屬性訪問(wèn)相關(guān)的魔術(shù)方法,其中重寫__getattr__,__getitem__來(lái)構(gòu)造一個(gè)自己的集合類非常的常用,下面我們就通過(guò)一些例子來(lái)看一下它們的應(yīng)用。

__getattr__

Python默認(rèn)訪問(wèn)類/實(shí)例的某個(gè)屬性都是通過(guò)__getattribute__來(lái)調(diào)用的,__getattribute__會(huì)被無(wú)條件調(diào)用,沒(méi)有找到的話就會(huì)調(diào)用__getattr__。如果我們要定制某個(gè)類,通常情況下我們不應(yīng)該重寫__getattribute__,而是應(yīng)該重寫__getattr__,很少看見(jiàn)重寫__getattribute__的情況。

從下面的輸出可以看出,當(dāng)一個(gè)屬性通過(guò)__getattribute__無(wú)法找到的時(shí)候會(huì)調(diào)用__getattr__。

In?[1]:?class?Test(object):
????...:?????def?__getattribute__(self,?item):
????...:?????????print('call?__getattribute__')
????...:?????????return?super(Test,?self).__getattribute__(item)
????...:?????def?__getattr__(self,?item):
????...:?????????return?'call?__getattr__'
????...:

In?[2]:?Test().a
call?__getattribute__
Out[2]:?'call?__getattr__'

應(yīng)用

對(duì)于默認(rèn)的字典,Python只支持以obj['foo']形式來(lái)訪問(wèn),不支持obj.foo的形式,我們可以通過(guò)重寫__getattr__讓字典也支持obj['foo']的訪問(wèn)形式,這是一個(gè)非常經(jīng)典常用的用法:

class?Storage(dict):
????"""
????A?Storage?object?is?like?a?dictionary?except?`obj.foo`?can?be?used
????in?addition?to?`obj['foo']`.
????"""

????def?__getattr__(self,?key):
????????try:
????????????return?self[key]
????????except?KeyError?as?k:
????????????raise?AttributeError(k)

????def?__setattr__(self,?key,?value):
????????self[key]?=?value

????def?__delattr__(self,?key):
????????try:
????????????del?self[key]
????????except?KeyError?as?k:
????????????raise?AttributeError(k)

????def?__repr__(self):
????????return?'<Storage &#39; + dict.__repr__(self) + &#39;>'

我們來(lái)使用一下我們自定義的加強(qiáng)版字典:

>>>?s?=?Storage(a=1)
>>>?s['a']
1
>>>?s.a
1
>>>?s.a?=?2
>>>?s['a']
2
>>>?del?s.a
>>>?s.a
...
AttributeError:?'a'

__getitem__

getitem用于通過(guò)下標(biāo)[]的形式來(lái)獲取對(duì)象中的元素,下面我們通過(guò)重寫__getitem__來(lái)實(shí)現(xiàn)一個(gè)自己的list。

class?MyList(object):
????def?__init__(self,?*args):
????????self.numbers?=?args

????def?__getitem__(self,?item):
????????return?self.numbers[item]


my_list?=?MyList(1,?2,?3,?4,?6,?5,?3)
print?my_list[2]

這個(gè)實(shí)現(xiàn)非常的簡(jiǎn)陋,不支持slice和step等功能,請(qǐng)讀者自行改進(jìn),這里我就不重復(fù)了。

應(yīng)用

下面是參考requests庫(kù)中對(duì)于__getitem__的一個(gè)使用,我們定制了一個(gè)忽略屬性大小寫的字典類。

程序有些復(fù)雜,我稍微解釋一下:由于這里比較簡(jiǎn)單,沒(méi)有使用描述符的需求,所以使用了@property裝飾器來(lái)代替,lower_keys的功能是將實(shí)例字典中的鍵全部轉(zhuǎn)換成小寫并且存儲(chǔ)在字典self._lower_keys中。重寫了__getitem__方法,以后我們?cè)L問(wèn)某個(gè)屬性首先會(huì)將鍵轉(zhuǎn)換為小寫的方式,然后并不會(huì)直接訪問(wèn)實(shí)例字典,而是會(huì)訪問(wèn)字典self._lower_keys去查找。賦值/刪除操作的時(shí)候由于實(shí)例字典會(huì)進(jìn)行變更,為了保持self._lower_keys和實(shí)例字典同步,首先清除self._lower_keys的內(nèi)容,以后我們重新查找鍵的時(shí)候再調(diào)用__getitem__的時(shí)候會(huì)重新新建一個(gè)self._lower_keys。

class?CaseInsensitiveDict(dict):

????@property
????def?lower_keys(self):
????????if?not?hasattr(self,?'_lower_keys')?or?not?self._lower_keys:
????????????self._lower_keys?=?dict((k.lower(),?k)?for?k?in?self.keys())
????????return?self._lower_keys

????def?_clear_lower_keys(self):
????????if?hasattr(self,?'_lower_keys'):
????????????self._lower_keys.clear()

????def?__contains__(self,?key):
????????return?key.lower()?in?self.lower_keys

????def?__getitem__(self,?key):
????????if?key?in?self:
????????????return?dict.__getitem__(self,?self.lower_keys[key.lower()])

????def?__setitem__(self,?key,?value):
????????dict.__setitem__(self,?key,?value)
????????self._clear_lower_keys()

????def?__delitem__(self,?key):
????????dict.__delitem__(self,?key)
????????self._lower_keys.clear()

????def?get(self,?key,?default=None):
????????if?key?in?self:
????????????return?self[key]
????????else:
????????????return?default

我們來(lái)調(diào)用一下這個(gè)類:

>>>?d?=?CaseInsensitiveDict()
>>>?d['ziwenxie']?=?'ziwenxie'
>>>?d['ZiWenXie']?=?'ZiWenXie'

>>>?print(d)
{'ZiWenXie':?'ziwenxie',?'ziwenxie':?'ziwenxie'}
>>>?print(d['ziwenxie'])
ziwenxie

#?d['ZiWenXie']?=>?d['ziwenxie']
>>>?print(d['ZiWenXie'])
ziwenxie

References

HOWTO-GUIDE
DOCUMENTATION
IBM-DEVELOPWORKS
ZHIHU
REQUESTS
WEBPY


本文為作者原創(chuàng),轉(zhuǎn)載請(qǐng)先與作者聯(lián)系。 首發(fā)于我的博客

引言

Descriptors(描述符)是Python語(yǔ)言中一個(gè)深?yuàn)W但很重要的一個(gè)黑魔法,它被廣泛應(yīng)用于Python語(yǔ)言的內(nèi)核,熟練掌握描述符將會(huì)為Python程序員的工具箱添加一個(gè)額外的技巧。本文我將講述描述符的定義以及一些常見(jiàn)的場(chǎng)景,并且在文末會(huì)補(bǔ)充一下__getattr__,__getattribute__, __getitem__這三個(gè)同樣涉及到屬性訪問(wèn)的魔術(shù)方法。

描述符的定義

descr__get__(self,?obj,?objtype=None)?-->?value

descr.__set__(self,?obj,?value)?-->?None

descr.__delete__(self,?obj)?-->?None

只要一個(gè)object attribute(對(duì)象屬性)定義了上面三個(gè)方法中的任意一個(gè),那么這個(gè)類就可以被稱為描述符類。

描述符基礎(chǔ)

下面這個(gè)例子中我們創(chuàng)建了一個(gè)RevealAcess類,并且實(shí)現(xiàn)了__get__方法,現(xiàn)在這個(gè)類可以被稱為一個(gè)描述符類。

class?RevealAccess(object):
????def?__get__(self,?obj,?objtype):
????????print('self?in?RevealAccess:?{}'.format(self))
????????print('self:?{}\nobj:?{}\nobjtype:?{}'.format(self,?obj,?objtype))


class?MyClass(object):
????x?=?RevealAccess()
????def?test(self):
????????print('self?in?MyClass:?{}'.format(self))

EX1實(shí)例屬性

接下來(lái)我們來(lái)看一下__get__方法的各個(gè)參數(shù)的含義,在下面這個(gè)例子中,self即RevealAccess類的實(shí)例x,obj即MyClass類的實(shí)例m,objtype顧名思義就是MyClass類自身。從輸出語(yǔ)句可以看出,m.x訪問(wèn)描述符x會(huì)調(diào)用__get__方法。

>>>?m?=?MyClass()
>>>?m.test()
self?in?MyClass:?<__main__.MyClass object at 0x7f19d4e42160>

>>>?m.x
self?in?RevealAccess:?<__main__.RevealAccess object at 0x7f19d4e420f0>
self:?<__main__.RevealAccess object at 0x7f19d4e420f0>
obj:?<__main__.MyClass object at 0x7f19d4e42160>
objtype:?<class &#39;__main__.MyClass&#39;>

EX2類屬性

如果通過(guò)類直接訪問(wèn)屬性x,那么obj接直接為None,這還是比較好理解,因?yàn)椴淮嬖贛yClass的實(shí)例。

>>>?MyClass.x
self?in?RevealAccess:?<__main__.RevealAccess object at 0x7f53651070f0>
self:?<__main__.RevealAccess object at 0x7f53651070f0>
obj:?None
objtype:?<class &#39;__main__.MyClass&#39;>

描述符的原理

描述符觸發(fā)

上面這個(gè)例子中,我們分別從實(shí)例屬性和類屬性的角度列舉了描述符的用法,下面我們來(lái)仔細(xì)分析一下內(nèi)部的原理:

  • 如果是對(duì)實(shí)例屬性進(jìn)行訪問(wèn),相當(dāng)于調(diào)用了object.__getattribute__(),它將obj.d轉(zhuǎn)譯成了type(obj).__dict__['d'].__get__(obj, type(obj))。

  • 如果是對(duì)類屬性進(jìn)行訪問(wèn),相當(dāng)于調(diào)用了type.__getattribute__(),它將cls.d轉(zhuǎn)譯成了cls.__dict__['d'].__get__(None, cls),轉(zhuǎn)換成Python代碼就是:

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

簡(jiǎn)單講一下__getattribute__魔術(shù)方法,這個(gè)方法在我們?cè)L問(wèn)一個(gè)對(duì)象的屬性的時(shí)候會(huì)被無(wú)條件調(diào)用,詳細(xì)的細(xì)節(jié)比如和__getattr, __getitem__的區(qū)別我會(huì)在文章的末尾做一個(gè)額外的補(bǔ)充,我們暫時(shí)并不深究。

描述符優(yōu)先級(jí)

首先,描述符分為兩種:

  • 如果一個(gè)對(duì)象同時(shí)定義了__get__()和__set__()方法,則這個(gè)描述符被稱為data descriptor。

  • 如果一個(gè)對(duì)象只定義了__get__()方法,則這個(gè)描述符被稱為non-data descriptor。

我們對(duì)屬性進(jìn)行訪問(wèn)的時(shí)候存在下面四種情況:

  • data descriptor

  • instance dict

  • non-data descriptor

  • __getattr__()

它們的優(yōu)先級(jí)大小是:

data?descriptor?>?instance?dict?>?non-data?descriptor?>?__getattr__()

這是什么意思呢?就是說(shuō)如果實(shí)例對(duì)象obj中出現(xiàn)了同名的data descriptor->d 和 instance attribute->d,obj.d對(duì)屬性d進(jìn)行訪問(wèn)的時(shí)候,由于data descriptor具有更高的優(yōu)先級(jí),Python便會(huì)調(diào)用type(obj).__dict__['d'].__get__(obj, type(obj))而不是調(diào)用obj.__dict__['d']。但是如果描述符是個(gè)non-data descriptor,Python則會(huì)調(diào)用obj.__dict__['d']。

Property

每次使用描述符的時(shí)候都定義一個(gè)描述符類,這樣看起來(lái)非常繁瑣。Python提供了一種簡(jiǎn)潔的方式用來(lái)向?qū)傩蕴砑訑?shù)據(jù)描述符。

property(fget=None,?fset=None,?fdel=None,?doc=None)?->?property?attribute

fget、fset和fdel分別是類的getter、setter和deleter方法。我們通過(guò)下面的一個(gè)示例來(lái)說(shuō)明如何使用Property:

class?Account(object):

????def?__init__(self):
????????self._acct_num?=?None

????def?get_acct_num(self):
????????return?self._acct_num

????def?set_acct_num(self,?value):
????????self._acct_num?=?value

????def?del_acct_num(self):
????????del?self._acct_num

????acct_num?=?property(get_acct_num,?set_acct_num,?del_acct_num,?'_acct_num?property.')

如果acct是Account的一個(gè)實(shí)例,acct.acct_num將會(huì)調(diào)用getter,acct.acct_num = value將調(diào)用setter,del acct_num.acct_num將調(diào)用deleter。

>>>?acct?=?Account()
>>>?acct.acct_num?=?1000
>>>?acct.acct_num
1000

Python也提供了@property裝飾器,對(duì)于簡(jiǎn)單的應(yīng)用場(chǎng)景可以使用它來(lái)創(chuàng)建屬性。一個(gè)屬性對(duì)象擁有g(shù)etter,setter和deleter裝飾器方法,可以使用它們通過(guò)對(duì)應(yīng)的被裝飾函數(shù)的accessor函數(shù)創(chuàng)建屬性的拷貝。

class?Account(object):

????def?__init__(self):
????????self._acct_num?=?None

????@property
?????#?the?_acct_num?property.?the?decorator?creates?a?read-only?property
????def?acct_num(self):
????????return?self._acct_num

????@acct_num.setter
????#?the?_acct_num?property?setter?makes?the?property?writeable
????def?set_acct_num(self,?value):
????????self._acct_num?=?value

????@acct_num.deleter
????def?del_acct_num(self):
????????del?self._acct_num

如果想讓屬性只讀,只需要去掉setter方法。

在運(yùn)行時(shí)創(chuàng)建描述符

我們可以在運(yùn)行時(shí)添加property屬性:

class?Person(object):

????def?addProperty(self,?attribute):
????????#?create?local?setter?and?getter?with?a?particular?attribute?name
????????getter?=?lambda?self:?self._getProperty(attribute)
????????setter?=?lambda?self,?value:?self._setProperty(attribute,?value)

????????#?construct?property?attribute?and?add?it?to?the?class
????????setattr(self.__class__,?attribute,?property(fget=getter,?\
????????????????????????????????????????????????????fset=setter,?\
????????????????????????????????????????????????????doc="Auto-generated?method"))

????def?_setProperty(self,?attribute,?value):
????????print("Setting:?{}?=?{}".format(attribute,?value))
????????setattr(self,?'_'?+?attribute,?value.title())

????def?_getProperty(self,?attribute):
????????print("Getting:?{}".format(attribute))
????????return?getattr(self,?'_'?+?attribute)
>>>?user?=?Person()
>>>?user.addProperty('name')
>>>?user.addProperty('phone')
>>>?user.name?=?'john?smith'
Setting:?name?=?john?smith
>>>?user.phone?=?'12345'
Setting:?phone?=?12345
>>>?user.name
Getting:?name
'John?Smith'
>>>?user.__dict__
{'_phone':?'12345',?'_name':?'John?Smith'}

靜態(tài)方法和類方法

我們可以使用描述符來(lái)模擬Python中的@staticmethod和@classmethod的實(shí)現(xiàn)。我們首先來(lái)瀏覽一下下面這張表:

Transformation Called from an Object Called from a Class
function f(obj, *args) f(*args)
staticmethod f(*args) f(*args)
classmethod f(type(obj), *args) f(klass, *args)

靜態(tài)方法

對(duì)于靜態(tài)方法f。c.f和C.f是等價(jià)的,都是直接查詢object.__getattribute__(c, ‘f’)或者object.__getattribute__(C, ’f‘)。靜態(tài)方法一個(gè)明顯的特征就是沒(méi)有self變量。

靜態(tài)方法有什么用呢?假設(shè)有一個(gè)處理專門數(shù)據(jù)的容器類,它提供了一些方法來(lái)求平均數(shù),中位數(shù)等統(tǒng)計(jì)數(shù)據(jù)方式,這些方法都是要依賴于相應(yīng)的數(shù)據(jù)的。但是類中可能還有一些方法,并不依賴這些數(shù)據(jù),這個(gè)時(shí)候我們可以將這些方法聲明為靜態(tài)方法,同時(shí)這也可以提高代碼的可讀性。

使用非數(shù)據(jù)描述符來(lái)模擬一下靜態(tài)方法的實(shí)現(xiàn):

class?StaticMethod(object):
????def?__init__(self,?f):
????????self.f?=?f

????def?__get__(self,?obj,?objtype=None):
????????return?self.f

我們來(lái)應(yīng)用一下:

class?MyClass(object):
????@StaticMethod
????def?get_x(x):
????????return?x

print(MyClass.get_x(100))??#?output:?100

類方法

Python的@classmethod和@staticmethod的用法有些類似,但是還是有些不同,當(dāng)某些方法只需要得到類的引用而不關(guān)心類中的相應(yīng)的數(shù)據(jù)的時(shí)候就需要使用classmethod了。

使用非數(shù)據(jù)描述符來(lái)模擬一下類方法的實(shí)現(xiàn):

class?ClassMethod(object):
????def?__init__(self,?f):
????????self.f?=?f

????def?__get__(self,?obj,?klass=None):
????????if?klass?is?None:
????????????klass?=?type(obj)
????????def?newfunc(*args):
????????????return?self.f(klass,?*args)
????????return?newfunc

其他的魔術(shù)方法

首次接觸Python魔術(shù)方法的時(shí)候,我也被__get__, __getattribute__, __getattr__, __getitem__之間的區(qū)別困擾到了,它們都是和屬性訪問(wèn)相關(guān)的魔術(shù)方法,其中重寫__getattr__,__getitem__來(lái)構(gòu)造一個(gè)自己的集合類非常的常用,下面我們就通過(guò)一些例子來(lái)看一下它們的應(yīng)用。

__getattr__

Python默認(rèn)訪問(wèn)類/實(shí)例的某個(gè)屬性都是通過(guò)__getattribute__來(lái)調(diào)用的,__getattribute__會(huì)被無(wú)條件調(diào)用,沒(méi)有找到的話就會(huì)調(diào)用__getattr__。如果我們要定制某個(gè)類,通常情況下我們不應(yīng)該重寫__getattribute__,而是應(yīng)該重寫__getattr__,很少看見(jiàn)重寫__getattribute__的情況。

從下面的輸出可以看出,當(dāng)一個(gè)屬性通過(guò)__getattribute__無(wú)法找到的時(shí)候會(huì)調(diào)用__getattr__。

In?[1]:?class?Test(object):
????...:?????def?__getattribute__(self,?item):
????...:?????????print('call?__getattribute__')
????...:?????????return?super(Test,?self).__getattribute__(item)
????...:?????def?__getattr__(self,?item):
????...:?????????return?'call?__getattr__'
????...:

In?[2]:?Test().a
call?__getattribute__
Out[2]:?'call?__getattr__'

應(yīng)用

對(duì)于默認(rèn)的字典,Python只支持以obj['foo']形式來(lái)訪問(wèn),不支持obj.foo的形式,我們可以通過(guò)重寫__getattr__讓字典也支持obj['foo']的訪問(wèn)形式,這是一個(gè)非常經(jīng)典常用的用法:

class?Storage(dict):
????"""
????A?Storage?object?is?like?a?dictionary?except?`obj.foo`?can?be?used
????in?addition?to?`obj['foo']`.
????"""

????def?__getattr__(self,?key):
????????try:
????????????return?self[key]
????????except?KeyError?as?k:
????????????raise?AttributeError(k)

????def?__setattr__(self,?key,?value):
????????self[key]?=?value

????def?__delattr__(self,?key):
????????try:
????????????del?self[key]
????????except?KeyError?as?k:
????????????raise?AttributeError(k)

????def?__repr__(self):
????????return?'<Storage &#39; + dict.__repr__(self) + &#39;>'

我們來(lái)使用一下我們自定義的加強(qiáng)版字典:

>>>?s?=?Storage(a=1)
>>>?s['a']
1
>>>?s.a
1
>>>?s.a?=?2
>>>?s['a']
2
>>>?del?s.a
>>>?s.a
...
AttributeError:?'a'

__getitem__

getitem用于通過(guò)下標(biāo)[]的形式來(lái)獲取對(duì)象中的元素,下面我們通過(guò)重寫__getitem__來(lái)實(shí)現(xiàn)一個(gè)自己的list。

class?MyList(object):
????def?__init__(self,?*args):
????????self.numbers?=?args

????def?__getitem__(self,?item):
????????return?self.numbers[item]


my_list?=?MyList(1,?2,?3,?4,?6,?5,?3)
print?my_list[2]

這個(gè)實(shí)現(xiàn)非常的簡(jiǎn)陋,不支持slice和step等功能,請(qǐng)讀者自行改進(jìn),這里我就不重復(fù)了。

應(yīng)用

下面是參考requests庫(kù)中對(duì)于__getitem__的一個(gè)使用,我們定制了一個(gè)忽略屬性大小寫的字典類。

程序有些復(fù)雜,我稍微解釋一下:由于這里比較簡(jiǎn)單,沒(méi)有使用描述符的需求,所以使用了@property裝飾器來(lái)代替,lower_keys的功能是將實(shí)例字典中的鍵全部轉(zhuǎn)換成小寫并且存儲(chǔ)在字典self._lower_keys中。重寫了__getitem__方法,以后我們?cè)L問(wèn)某個(gè)屬性首先會(huì)將鍵轉(zhuǎn)換為小寫的方式,然后并不會(huì)直接訪問(wèn)實(shí)例字典,而是會(huì)訪問(wèn)字典self._lower_keys去查找。賦值/刪除操作的時(shí)候由于實(shí)例字典會(huì)進(jìn)行變更,為了保持self._lower_keys和實(shí)例字典同步,首先清除self._lower_keys的內(nèi)容,以后我們重新查找鍵的時(shí)候再調(diào)用__getitem__的時(shí)候會(huì)重新新建一個(gè)self._lower_keys。

class?CaseInsensitiveDict(dict):

????@property
????def?lower_keys(self):
????????if?not?hasattr(self,?'_lower_keys')?or?not?self._lower_keys:
????????????self._lower_keys?=?dict((k.lower(),?k)?for?k?in?self.keys())
????????return?self._lower_keys

????def?_clear_lower_keys(self):
????????if?hasattr(self,?'_lower_keys'):
????????????self._lower_keys.clear()

????def?__contains__(self,?key):
????????return?key.lower()?in?self.lower_keys

????def?__getitem__(self,?key):
????????if?key?in?self:
????????????return?dict.__getitem__(self,?self.lower_keys[key.lower()])

????def?__setitem__(self,?key,?value):
????????dict.__setitem__(self,?key,?value)
????????self._clear_lower_keys()

????def?__delitem__(self,?key):
????????dict.__delitem__(self,?key)
????????self._lower_keys.clear()

????def?get(self,?key,?default=None):
????????if?key?in?self:
????????????return?self[key]
????????else:
????????????return?default

我們來(lái)調(diào)用一下這個(gè)類:

>>>?d?=?CaseInsensitiveDict()
>>>?d['ziwenxie']?=?'ziwenxie'
>>>?d['ZiWenXie']?=?'ZiWenXie'

>>>?print(d)
{'ZiWenXie':?'ziwenxie',?'ziwenxie':?'ziwenxie'}
>>>?print(d['ziwenxie'])
ziwenxie

#?d['ZiWenXie']?=>?d['ziwenxie']
>>>?print(d['ZiWenXie'])
ziwenxie

更多Python黑魔法之描述符相關(guān)文章請(qǐng)關(guān)注PHP中文網(wǎng)!

本站聲明
本文內(nèi)容由網(wǎng)友自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,本站不承擔(dān)相應(yīng)法律責(zé)任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請(qǐng)聯(lián)系admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣服圖片

Undresser.AI Undress

Undresser.AI Undress

人工智能驅(qū)動(dòng)的應(yīng)用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用于從照片中去除衣服的在線人工智能工具。

Clothoff.io

Clothoff.io

AI脫衣機(jī)

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智能換臉工具輕松在任何視頻中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的代碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

功能強(qiáng)大的PHP集成開(kāi)發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺(jué)化網(wǎng)頁(yè)開(kāi)發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)代碼編輯軟件(SublimeText3)

如何用PHP結(jié)合AI實(shí)現(xiàn)文本糾錯(cuò) PHP語(yǔ)法檢測(cè)與優(yōu)化 如何用PHP結(jié)合AI實(shí)現(xiàn)文本糾錯(cuò) PHP語(yǔ)法檢測(cè)與優(yōu)化 Jul 25, 2025 pm 08:57 PM

要實(shí)現(xiàn)PHP結(jié)合AI進(jìn)行文本糾錯(cuò)與語(yǔ)法優(yōu)化,需按以下步驟操作:1.選擇適合的AI模型或API,如百度、騰訊API或開(kāi)源NLP庫(kù);2.通過(guò)PHP的curl或Guzzle調(diào)用API并處理返回結(jié)果;3.在應(yīng)用中展示糾錯(cuò)信息并允許用戶選擇是否采納;4.使用php-l和PHP_CodeSniffer進(jìn)行語(yǔ)法檢測(cè)與代碼優(yōu)化;5.持續(xù)收集反饋并更新模型或規(guī)則以提升效果。選擇AIAPI時(shí)應(yīng)重點(diǎn)評(píng)估準(zhǔn)確率、響應(yīng)速度、價(jià)格及對(duì)PHP的支持。代碼優(yōu)化應(yīng)遵循PSR規(guī)范、合理使用緩存、避免循環(huán)查詢、定期審查代碼,并借助X

PHP調(diào)用AI智能語(yǔ)音助手 PHP語(yǔ)音交互系統(tǒng)搭建 PHP調(diào)用AI智能語(yǔ)音助手 PHP語(yǔ)音交互系統(tǒng)搭建 Jul 25, 2025 pm 08:45 PM

用戶語(yǔ)音輸入通過(guò)前端JavaScript的MediaRecorderAPI捕獲并發(fā)送至PHP后端;2.PHP將音頻保存為臨時(shí)文件后調(diào)用STTAPI(如Google或百度語(yǔ)音識(shí)別)轉(zhuǎn)換為文本;3.PHP將文本發(fā)送至AI服務(wù)(如OpenAIGPT)獲取智能回復(fù);4.PHP再調(diào)用TTSAPI(如百度或Google語(yǔ)音合成)將回復(fù)轉(zhuǎn)為語(yǔ)音文件;5.PHP將語(yǔ)音文件流式返回前端播放,完成交互。整個(gè)流程由PHP主導(dǎo)數(shù)據(jù)流轉(zhuǎn)與錯(cuò)誤處理,確保各環(huán)節(jié)無(wú)縫銜接。

成品python大片在線觀看入口 python免費(fèi)成品網(wǎng)站大全 成品python大片在線觀看入口 python免費(fèi)成品網(wǎng)站大全 Jul 23, 2025 pm 12:36 PM

本文為您精選了多個(gè)頂級(jí)的Python“成品”項(xiàng)目網(wǎng)站與高水平“大片”級(jí)學(xué)習(xí)資源入口。無(wú)論您是想尋找開(kāi)發(fā)靈感、觀摩學(xué)習(xí)大師級(jí)的源代碼,還是系統(tǒng)性地提升實(shí)戰(zhàn)能力,這些平臺(tái)都是不容錯(cuò)過(guò)的寶庫(kù),能幫助您快速成長(zhǎng)為Python高手。

如何用PHP開(kāi)發(fā)商品推薦模塊 PHP推薦算法與用戶行為分析 如何用PHP開(kāi)發(fā)商品推薦模塊 PHP推薦算法與用戶行為分析 Jul 23, 2025 pm 07:00 PM

收集用戶行為數(shù)據(jù)需通過(guò)PHP記錄瀏覽、搜索、購(gòu)買等信息至數(shù)據(jù)庫(kù),并清洗分析以挖掘興趣偏好;2.推薦算法選擇應(yīng)根據(jù)數(shù)據(jù)特征決定:基于內(nèi)容、協(xié)同過(guò)濾、規(guī)則或混合推薦;3.協(xié)同過(guò)濾在PHP中可實(shí)現(xiàn)為計(jì)算用戶余弦相似度、選K近鄰、加權(quán)預(yù)測(cè)評(píng)分并推薦高分商品;4.性能評(píng)估用準(zhǔn)確率、召回率、F1值及CTR、轉(zhuǎn)化率并通過(guò)A/B測(cè)試驗(yàn)證效果;5.冷啟動(dòng)問(wèn)題可通過(guò)商品屬性、用戶注冊(cè)信息、熱門推薦和專家評(píng)價(jià)緩解;6.性能優(yōu)化手段包括緩存推薦結(jié)果、異步處理、分布式計(jì)算與SQL查詢優(yōu)化,從而提升推薦效率與用戶體驗(yàn)。

如何用PHP開(kāi)發(fā)AI智能表單系統(tǒng) PHP智能表單設(shè)計(jì)與分析 如何用PHP開(kāi)發(fā)AI智能表單系統(tǒng) PHP智能表單設(shè)計(jì)與分析 Jul 25, 2025 pm 05:54 PM

選擇合適的PHP框架需根據(jù)項(xiàng)目需求綜合考慮:Laravel適合快速開(kāi)發(fā),提供EloquentORM和Blade模板引擎,便于數(shù)據(jù)庫(kù)操作和動(dòng)態(tài)表單渲染;Symfony更靈活,適合復(fù)雜系統(tǒng);CodeIgniter輕量,適用于對(duì)性能要求較高的簡(jiǎn)單應(yīng)用。2.確保AI模型準(zhǔn)確性需從高質(zhì)量數(shù)據(jù)訓(xùn)練、合理選擇評(píng)估指標(biāo)(如準(zhǔn)確率、召回率、F1值)、定期性能評(píng)估與模型調(diào)優(yōu)入手,并通過(guò)單元測(cè)試和集成測(cè)試保障代碼質(zhì)量,同時(shí)持續(xù)監(jiān)控輸入數(shù)據(jù)以防止數(shù)據(jù)漂移。3.保護(hù)用戶隱私需采取多項(xiàng)措施:對(duì)敏感數(shù)據(jù)進(jìn)行加密存儲(chǔ)(如AES

python seaborn關(guān)節(jié)圖示例 python seaborn關(guān)節(jié)圖示例 Jul 26, 2025 am 08:11 AM

使用Seaborn的jointplot可快速可視化兩個(gè)變量間的關(guān)系及各自分布;2.基礎(chǔ)散點(diǎn)圖通過(guò)sns.jointplot(data=tips,x="total_bill",y="tip",kind="scatter")實(shí)現(xiàn),中心為散點(diǎn)圖,上下和右側(cè)顯示直方圖;3.添加回歸線和密度信息可用kind="reg",并結(jié)合marginal_kws設(shè)置邊緣圖樣式;4.數(shù)據(jù)量大時(shí)推薦kind="hex",用

如何用PHP實(shí)現(xiàn)AI內(nèi)容推薦系統(tǒng) PHP智能內(nèi)容分發(fā)機(jī)制 如何用PHP實(shí)現(xiàn)AI內(nèi)容推薦系統(tǒng) PHP智能內(nèi)容分發(fā)機(jī)制 Jul 23, 2025 pm 06:12 PM

1.PHP在AI內(nèi)容推薦系統(tǒng)中主要承擔(dān)數(shù)據(jù)收集、API通信、業(yè)務(wù)規(guī)則處理、緩存優(yōu)化與推薦展示等角色,而非直接執(zhí)行復(fù)雜模型訓(xùn)練;2.系統(tǒng)通過(guò)PHP收集用戶行為與內(nèi)容數(shù)據(jù),調(diào)用后端AI服務(wù)(如Python模型)獲取推薦結(jié)果,并利用Redis緩存提升性能;3.基礎(chǔ)推薦算法如協(xié)同過(guò)濾或內(nèi)容相似度可在PHP中實(shí)現(xiàn)輕量級(jí)邏輯,但大規(guī)模計(jì)算仍依賴專業(yè)AI服務(wù);4.優(yōu)化需關(guān)注實(shí)時(shí)性、冷啟動(dòng)、多樣性及反饋閉環(huán),挑戰(zhàn)包括高并發(fā)性能、模型更新平穩(wěn)性、數(shù)據(jù)合規(guī)與推薦可解釋性,PHP需協(xié)同消息隊(duì)列、數(shù)據(jù)庫(kù)與前端共同構(gòu)建穩(wěn)

如何用PHP開(kāi)發(fā)基于AI的文本摘要 PHP信息快速提煉技術(shù) 如何用PHP開(kāi)發(fā)基于AI的文本摘要 PHP信息快速提煉技術(shù) Jul 25, 2025 pm 05:57 PM

PHP開(kāi)發(fā)AI文本摘要的核心是作為協(xié)調(diào)器調(diào)用外部AI服務(wù)API(如OpenAI、HuggingFace),實(shí)現(xiàn)文本預(yù)處理、API請(qǐng)求、響應(yīng)解析與結(jié)果展示;2.局限性在于計(jì)算性能弱、AI生態(tài)薄弱,應(yīng)對(duì)策略為借力API、服務(wù)解耦和異步處理;3.模型選擇需權(quán)衡摘要質(zhì)量、成本、延遲、并發(fā)、數(shù)據(jù)隱私,推薦使用GPT或BART/T5等抽象式模型;4.性能優(yōu)化包括緩存、異步隊(duì)列、批量處理和就近區(qū)域選擇,錯(cuò)誤處理需覆蓋限流重試、網(wǎng)絡(luò)超時(shí)、密鑰安全、輸入驗(yàn)證及日志記錄,以確保系統(tǒng)穩(wěn)定高效運(yùn)行。

See all articles