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

Jadual Kandungan
描述符的定義
描述符基礎(chǔ)
描述符的原理
描述符觸發(fā)
描述符優(yōu)先級(jí)
Property
在運(yùn)行時(shí)創(chuàng)建描述符
靜態(tài)方法和類(lèi)方法
靜態(tài)方法
類(lèi)方法
其他的魔術(shù)方法
__getattr__
應(yīng)用
__getitem__
References
引言

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è)類(lèi)就可以被稱(chēng)為描述符類(lèi)。

描述符基礎(chǔ)

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

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類(lèi)的實(shí)例x,obj即MyClass類(lèi)的實(shí)例m,objtype顧名思義就是MyClass類(lèi)自身。從輸出語(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類(lèi)屬性

如果通過(guò)類(lèi)直接訪問(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)屬性的角度列舉了描述符的用法,下面我們來(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ì)類(lèi)屬性進(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è)描述符被稱(chēng)為data descriptor。

  • 如果一個(gè)對(duì)象只定義了__get__()方法,則這個(gè)描述符被稱(chēng)為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),這樣看起來(lái)非常繁瑣。Python提供了一種簡(jiǎn)潔的方式用來(lái)向?qū)傩蕴砑訑?shù)據(jù)描述符。

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

fget、fset和fdel分別是類(lèi)的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)方法

我們可以使用描述符來(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è)處理專(zhuān)門(mén)數(shù)據(jù)的容器類(lèi),它提供了一些方法來(lái)求平均數(shù),中位數(shù)等統(tǒng)計(jì)數(shù)據(jù)方式,這些方法都是要依賴(lài)于相應(yīng)的數(shù)據(jù)的。但是類(lèi)中可能還有一些方法,并不依賴(lài)這些數(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

類(lèi)方法

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

使用非數(shù)據(jù)描述符來(lái)模擬一下類(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ù)方法,其中重寫(xiě)__getattr__,__getitem__來(lái)構(gòu)造一個(gè)自己的集合類(lèi)非常的常用,下面我們就通過(guò)一些例子來(lái)看一下它們的應(yīng)用。

__getattr__

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

程序有些復(fù)雜,我稍微解釋一下:由于這里比較簡(jiǎn)單,沒(méi)有使用描述符的需求,所以使用了@property裝飾器來(lái)代替,lower_keys的功能是將實(shí)例字典中的鍵全部轉(zhuǎn)換成小寫(xiě)并且存儲(chǔ)在字典self._lower_keys中。重寫(xiě)了__getitem__方法,以后我們?cè)L問(wèn)某個(gè)屬性首先會(huì)將鍵轉(zhuǎn)換為小寫(xiě)的方式,然后并不會(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è)類(lèi):

>>>?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è)類(lèi)就可以被稱(chēng)為描述符類(lèi)。

描述符基礎(chǔ)

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

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類(lèi)的實(shí)例x,obj即MyClass類(lèi)的實(shí)例m,objtype顧名思義就是MyClass類(lèi)自身。從輸出語(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類(lèi)屬性

如果通過(guò)類(lèi)直接訪問(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)屬性的角度列舉了描述符的用法,下面我們來(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ì)類(lèi)屬性進(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è)描述符被稱(chēng)為data descriptor。

  • 如果一個(gè)對(duì)象只定義了__get__()方法,則這個(gè)描述符被稱(chēng)為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),這樣看起來(lái)非常繁瑣。Python提供了一種簡(jiǎn)潔的方式用來(lái)向?qū)傩蕴砑訑?shù)據(jù)描述符。

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

fget、fset和fdel分別是類(lèi)的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)方法

我們可以使用描述符來(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è)處理專(zhuān)門(mén)數(shù)據(jù)的容器類(lèi),它提供了一些方法來(lái)求平均數(shù),中位數(shù)等統(tǒng)計(jì)數(shù)據(jù)方式,這些方法都是要依賴(lài)于相應(yīng)的數(shù)據(jù)的。但是類(lèi)中可能還有一些方法,并不依賴(lài)這些數(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

類(lèi)方法

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

使用非數(shù)據(jù)描述符來(lái)模擬一下類(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ù)方法,其中重寫(xiě)__getattr__,__getitem__來(lái)構(gòu)造一個(gè)自己的集合類(lèi)非常的常用,下面我們就通過(guò)一些例子來(lái)看一下它們的應(yīng)用。

__getattr__

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

程序有些復(fù)雜,我稍微解釋一下:由于這里比較簡(jiǎn)單,沒(méi)有使用描述符的需求,所以使用了@property裝飾器來(lái)代替,lower_keys的功能是將實(shí)例字典中的鍵全部轉(zhuǎn)換成小寫(xiě)并且存儲(chǔ)在字典self._lower_keys中。重寫(xiě)了__getitem__方法,以后我們?cè)L問(wèn)某個(gè)屬性首先會(huì)將鍵轉(zhuǎn)換為小寫(xiě)的方式,然后并不會(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è)類(lèi):

>>>?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)!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Cara Menggunakan PHP Digabungkan dengan AI Untuk Mencapai Ralat Pembetulan Ralat PHP Pengesanan dan Pengoptimuman Sintaks PHP Cara Menggunakan PHP Digabungkan dengan AI Untuk Mencapai Ralat Pembetulan Ralat PHP Pengesanan dan Pengoptimuman Sintaks PHP Jul 25, 2025 pm 08:57 PM

Untuk merealisasikan pembetulan ralat teks dan pengoptimuman sintaks dengan AI, anda perlu mengikuti langkah -langkah berikut: 1. Pilih model AI atau API yang sesuai, seperti Baidu, Tencent API atau perpustakaan NLP sumber terbuka; 2. Panggil API melalui curl atau Guzzle PHP dan memproses hasil pulangan; 3. Maklumat pembetulan ralat paparan dalam aplikasi dan membenarkan pengguna memilih sama ada untuk mengadopsinya; 4. Gunakan php-l dan php_codesniffer untuk pengesanan sintaks dan pengoptimuman kod; 5. Secara berterusan mengumpul maklum balas dan mengemas kini model atau peraturan untuk meningkatkan kesannya. Apabila memilih AIAPI, fokus pada menilai ketepatan, kelajuan tindak balas, harga dan sokongan untuk PHP. Pengoptimuman kod harus mengikuti spesifikasi PSR, gunakan cache yang munasabah, elakkan pertanyaan bulat, mengkaji semula kod secara berkala, dan gunakan x

PHP memanggil AI Pembantu Suara Pintar PHP Pembinaan Sistem Interaksi Suara PHP PHP memanggil AI Pembantu Suara Pintar PHP Pembinaan Sistem Interaksi Suara PHP Jul 25, 2025 pm 08:45 PM

Input suara pengguna ditangkap dan dihantar ke backend PHP melalui API Mediarecorder JavaScript front-end; 2. PHP menjimatkan audio sebagai fail sementara dan memanggil STTAPI (seperti Pengiktirafan Suara Google atau Baidu) untuk mengubahnya menjadi teks; 3. PHP menghantar teks kepada perkhidmatan AI (seperti Openaigpt) untuk mendapatkan jawapan pintar; 4. PHP kemudian memanggil TTSAPI (seperti sintesis Baidu atau Google Voice) untuk menukar balasan ke fail suara; 5. PHP mengalir fail suara kembali ke bahagian depan untuk bermain, menyelesaikan interaksi. Seluruh proses dikuasai oleh PHP untuk memastikan hubungan lancar antara semua pautan.

Siap Blockbuster Python Online Melihat Masuk Python Percuma Koleksi Laman Web Siap Siap Blockbuster Python Online Melihat Masuk Python Percuma Koleksi Laman Web Siap Jul 23, 2025 pm 12:36 PM

Artikel ini telah memilih beberapa laman web projek "selesai" Python dan portal sumber pembelajaran "blockbuster" peringkat tinggi untuk anda. Sama ada anda sedang mencari inspirasi pembangunan, mengamati dan belajar kod sumber peringkat induk, atau secara sistematik meningkatkan keupayaan praktikal anda, platform ini tidak boleh dilepaskan dan dapat membantu anda berkembang menjadi tuan python dengan cepat.

Cara Menggunakan PHP Untuk Membangunkan Algoritma Cadangan Modul Cadangan Produk PHP dan Analisis Kelakuan Pengguna Cara Menggunakan PHP Untuk Membangunkan Algoritma Cadangan Modul Cadangan Produk PHP dan Analisis Kelakuan Pengguna Jul 23, 2025 pm 07:00 PM

Untuk mengumpul data tingkah laku pengguna, anda perlu merakam pelayaran, mencari, membeli dan maklumat lain ke dalam pangkalan data melalui PHP, dan membersihkan dan menganalisisnya untuk meneroka keutamaan minat; 2. Pemilihan algoritma cadangan harus ditentukan berdasarkan ciri -ciri data: berdasarkan kandungan, penapisan kolaboratif, peraturan atau cadangan campuran; 3. Penapisan kolaboratif boleh dilaksanakan di PHP untuk mengira kesamaan kosinus pengguna, pilih K jiran terdekat, skor ramalan berwajaran dan mengesyorkan produk pemarkahan tinggi; 4. Penilaian prestasi menggunakan ketepatan, ingat, nilai F1 dan CTR, kadar penukaran dan sahkan kesan melalui ujian A/B; 5. Masalah permulaan sejuk boleh dikurangkan melalui atribut produk, maklumat pendaftaran pengguna, cadangan popular dan penilaian pakar; 6. Kaedah Pengoptimuman Prestasi termasuk hasil cadangan cache, pemprosesan tak segerak, pengkomputeran yang diedarkan dan pengoptimuman pertanyaan SQL, dengan itu meningkatkan kecekapan cadangan dan pengalaman pengguna.

Cara Membangunkan Sistem Borang Pintar AI Dengan Reka Bentuk dan Analisis Pistol PHP PHP Cara Membangunkan Sistem Borang Pintar AI Dengan Reka Bentuk dan Analisis Pistol PHP PHP Jul 25, 2025 pm 05:54 PM

Apabila memilih rangka kerja PHP yang sesuai, anda perlu mempertimbangkan secara komprehensif mengikut keperluan projek: Laravel sesuai untuk pembangunan pesat dan menyediakan enjin template eloquentorm dan bilah, yang mudah untuk operasi pangkalan data dan rendering bentuk dinamik; Symfony lebih fleksibel dan sesuai untuk sistem kompleks; Codeigniter adalah ringan dan sesuai untuk aplikasi mudah dengan keperluan prestasi tinggi. 2. Untuk memastikan ketepatan model AI, kita perlu memulakan dengan latihan data berkualiti tinggi, pemilihan penunjuk penilaian yang munasabah (seperti ketepatan, penarikan balik, nilai F1), penilaian prestasi biasa dan penalaan model, dan memastikan kualiti kod melalui ujian unit dan ujian integrasi, sambil terus memantau data input untuk mencegah data drift. 3. Banyak langkah diperlukan untuk melindungi privasi pengguna: menyulitkan dan menyimpan data sensitif (seperti AES

Contoh Bersama Seaborn Python Contoh Bersama Seaborn Python Jul 26, 2025 am 08:11 AM

Gunakan sendi Seaborn untuk dengan cepat menggambarkan hubungan dan pengedaran antara dua pembolehubah; 2. 3. Tambah garis regresi dan maklumat ketumpatan kepada jenis = "reg", dan gabungkan marginal_kws untuk menetapkan gaya plot tepi; 4. Apabila jumlah data besar, disarankan untuk menggunakan "hex"

Cara Menggunakan PHP Untuk Melaksanakan Sistem Cadangan Kandungan AI PHP Mekanisme Pengedaran Kandungan Pintar Cara Menggunakan PHP Untuk Melaksanakan Sistem Cadangan Kandungan AI PHP Mekanisme Pengedaran Kandungan Pintar Jul 23, 2025 pm 06:12 PM

1. PHP terutamanya menjalankan pengumpulan data, komunikasi API, pemprosesan peraturan perniagaan, pengoptimuman cache dan paparan cadangan dalam sistem cadangan kandungan AI, dan bukan secara langsung melaksanakan latihan model kompleks; 2. Sistem ini mengumpul tingkah laku pengguna dan data kandungan melalui PHP, memanggil perkhidmatan AI back-end (seperti model Python) untuk mendapatkan hasil cadangan, dan menggunakan Redis Cache untuk meningkatkan prestasi; 3. Algoritma cadangan asas seperti penapisan kolaboratif atau persamaan kandungan boleh melaksanakan logik ringan dalam PHP, tetapi pengkomputeran besar-besaran masih bergantung kepada perkhidmatan AI profesional; 4. Pengoptimuman perlu memberi perhatian kepada masa nyata, permulaan sejuk, kepelbagaian dan maklum balas yang ditutup gelung, dan cabaran termasuk prestasi konkurensi tinggi, kestabilan kemas kini model, pematuhan data dan tafsiran cadangan. PHP perlu bekerjasama untuk membina maklumat yang stabil, pangkalan data dan front-end.

Cara Membangunkan Ringkasan Teks Berasaskan AI Dengan Teknologi Penapisan PHP Pantas Cara Membangunkan Ringkasan Teks Berasaskan AI Dengan Teknologi Penapisan PHP Pantas Jul 25, 2025 pm 05:57 PM

Inti perkembangan PHP Ringkasan Teks AI adalah untuk memanggil API perkhidmatan AI luaran (seperti OpenAI, HuggingFace) sebagai penyelaras untuk merealisasikan pra -proses teks, permintaan API, analisis tindak balas dan paparan hasil; 2. Batasan adalah bahawa prestasi pengkomputeran lemah dan ekosistem AI lemah. Strategi tindak balas adalah untuk memanfaatkan API, decoupling perkhidmatan dan pemprosesan tak segerak; 3. Pemilihan model perlu menimbang ringkasan kualiti, kos, kelewatan, keserasian, privasi data, dan model abstrak seperti GPT atau BART/T5 adalah disyorkan; 4. Pengoptimuman prestasi termasuk cache, antrian asynchronous, pemprosesan batch dan pemilihan kawasan berdekatan. Pemprosesan ralat perlu meliputi had semasa semula, masa tamat rangkaian, keselamatan utama, pengesahan input dan pembalakan untuk memastikan operasi sistem yang stabil dan cekap.

See all articles