ÒýÑÔ
Descriptors(ÃèÊö·û)ÊÇPythonÓïÑÔÖÐÒ»¸öÉî°Âµ«ºÜÖØÒªµÄÒ»¸öºÚħ·¨£¬Ëü±»¹ã·ºÓ¦ÓÃÓÚPythonÓïÑÔµÄÄںˣ¬ÊìÁ·ÕÆÎÕÃèÊö·û½«»áΪPython³ÌÐòÔ±µÄ¹¤¾ßÏäÌí¼ÓÒ»¸ö¶îÍâµÄ¼¼ÇÉ¡£±¾ÎÄÎÒ½«½²ÊöÃèÊö·ûµÄ¶¨ÒåÒÔ¼°Ò»Ð©³£¼ûµÄ³¡¾°£¬²¢ÇÒÔÚÎÄÄ©»á²¹³äÒ»ÏÂ__getattr£¬__getattribute__,
__getitem__ÕâÈý¸öͬÑùÉæ¼°µ½ÊôÐÔ·ÃÎʵÄħÊõ·½·¨¡£

ÃèÊö·ûµÄ¶¨Òå
descr__get__(self, obj, objtype=None) --> value descr.__set__(self, obj, value) --> None descr.__delete__(self, obj) --> None |
Ö»ÒªÒ»¸öobject attribute(¶ÔÏóÊôÐÔ)¶¨ÒåÁËÉÏÃæÈý¸ö·½·¨ÖеÄÈÎÒâÒ»¸ö£¬ÄÇôÕâ¸öÀà¾Í¿ÉÒÔ±»³ÆÎªÃèÊö·ûÀà¡£
ÃèÊö·û»ù´¡
ÏÂÃæÕâ¸öÀý×ÓÖÐÎÒÃÇ´´½¨ÁËÒ»¸öRevealAcessÀ࣬²¢ÇÒʵÏÖÁË__get__·½·¨£¬ÏÖÔÚÕâ¸öÀà¿ÉÒÔ±»³ÆÎªÒ»¸öÃèÊö·ûÀà¡£
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ʵÀýÊôÐÔ
½ÓÏÂÀ´ÎÒÃÇÀ´¿´Ò»ÏÂ__get__·½·¨µÄ¸÷¸ö²ÎÊýµÄº¬Ò壬ÔÚÏÂÃæÕâ¸öÀý×ÓÖУ¬self¼´RevealAccessÀàµÄʵÀýx£¬obj¼´MyClassÀàµÄʵÀým£¬objtype¹ËÃû˼Òå¾ÍÊÇMyClassÀà×ÔÉí¡£´ÓÊä³öÓï¾ä¿ÉÒÔ¿´³ö£¬m.x·ÃÎÊÃèÊö·ûx»áµ÷ÓÃ__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 '__main__.MyClass'> |
EX2ÀàÊôÐÔ
Èç¹ûͨ¹ýÀàÖ±½Ó·ÃÎÊÊôÐÔx£¬ÄÇôobj½ÓÖ±½ÓΪNone£¬Õ⻹ÊDZȽϺÃÀí½â£¬ÒòΪ²»´æÔÚMyClassµÄʵÀý¡£
>>> MyClass.x self in RevealAccess: <__main__.RevealAccess object at 0x7f53651070f0> self: <__main__.RevealAccess object at 0x7f53651070f0> obj: None objtype: <class '__main__.MyClass'> |
ÃèÊö·ûµÄÔÀí
ÃèÊö·û´¥·¢
ÉÏÃæÕâ¸öÀý×ÓÖУ¬ÎÒÃÇ·Ö±ð´ÓʵÀýÊôÐÔºÍÀàÊôÐԵĽǶÈÁоÙÁËÃèÊö·ûµÄÓ÷¨£¬ÏÂÃæÎÒÃÇÀ´×Ðϸ·ÖÎöÒ»ÏÂÄÚ²¿µÄÔÀí£º
Èç¹ûÊǶÔʵÀýÊôÐÔ½øÐзÃÎÊ£¬Êµ¼ÊÉϵ÷ÓÃÁË»ùÀàobjectµÄ__getattribute__·½·¨£¬ÔÚÕâ¸ö·½·¨Öн«obj.dתÒë³ÉÁËtype(obj).__dict__['d'].__get__(obj,
type(obj))¡£
Èç¹ûÊǶÔÀàÊôÐÔ½øÐзÃÎÊ£¬Ï൱ÓÚµ÷ÓÃÁËÔªÀàtypeµÄ__getattribute__·½·¨£¬Ëü½«cls.dתÒë³Écls.__dict__['d'].__get__(None,
cls)£¬ÕâÀï__get__()µÄobjΪµÄNone£¬ÒòΪ²»´æÔÚʵÀý¡£
¼òµ¥½²Ò»ÏÂ__getattribute__ħÊõ·½·¨£¬Õâ¸ö·½·¨ÔÚÎÒÃÇ·ÃÎÊÒ»¸ö¶ÔÏóµÄÊôÐÔµÄʱºò»á±»ÎÞÌõ¼þµ÷Óã¬ÏêϸµÄϸ½Ú±ÈÈçºÍ__getattr,
__getitem__µÄÇø±ðÎÒ»áÔÚÎÄÕµÄĩβ×öÒ»¸ö¶îÍâµÄ²¹³ä£¬ÎÒÃÇÔÝʱ²¢²»É¡£
ÃèÊö·ûÓÅÏȼ¶
Ê×ÏÈ£¬ÃèÊö·û·ÖΪÁ½ÖÖ:
Èç¹ûÒ»¸ö¶ÔÏóͬʱ¶¨ÒåÁË__get__()ºÍ__set__()·½·¨£¬ÔòÕâ¸öÃèÊö·û±»³ÆÎªdata descriptor¡£
Èç¹ûÒ»¸ö¶ÔÏóÖ»¶¨ÒåÁË__get__()·½·¨£¬ÔòÕâ¸öÃèÊö·û±»³ÆÎªnon-data descriptor¡£
ÎÒÃǶÔÊôÐÔ½øÐзÃÎʵÄʱºò´æÔÚÏÂÃæËÄÖÖÇé¿ö£º
data descriptor
instance dict
non-data descriptor
__getattr__()
ËüÃǵÄÓÅÏȼ¶´óСÊÇ£º
data descriptor > instance dict > non-data descriptor > __getattr__() |
ÕâÊÇʲôÒâË¼ÄØ£¿¾ÍÊÇ˵Èç¹ûʵÀý¶ÔÏóobjÖгöÏÖÁËͬÃûµÄdata descriptor->d ºÍ instance
attribute->d£¬obj.d¶ÔÊôÐÔd½øÐзÃÎʵÄʱºò£¬ÓÉÓÚdata descriptor¾ßÓиü¸ßµÄÓÅÏȼ¶£¬Python±ã»áµ÷ÓÃtype(obj).__dict__['d'].__get__(obj,
type(obj))¶ø²»Êǵ÷ÓÃobj.__dict__[¡®d¡¯]¡£µ«ÊÇÈç¹ûÃèÊö·ûÊǸönon-data descriptor£¬PythonÔò»áµ÷ÓÃobj.__dict__['d']¡£
Property
ÿ´ÎʹÓÃÃèÊö·ûµÄʱºò¶¼¶¨ÒåÒ»¸öÃèÊö·ûÀ࣬ÕâÑù¿´ÆðÀ´·Ç³£·±Ëö¡£PythonÌṩÁËÒ»ÖÖ¼ò½àµÄ·½Ê½ÓÃÀ´ÏòÊôÐÔÌí¼ÓÊý¾ÝÃèÊö·û¡£
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute |
fget¡¢fsetºÍfdel·Ö±ðÊÇÀàµÄgetter¡¢setterºÍdeleter·½·¨¡£ÎÒÃÇͨ¹ýÏÂÃæµÄÒ»¸öʾÀýÀ´ËµÃ÷ÈçºÎʹÓÃ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µÄÒ»¸öʵÀý£¬acct.acct_num½«»áµ÷ÓÃgetter£¬acct.acct_num
= value½«µ÷ÓÃsetter£¬del acct_num.acct_num½«µ÷ÓÃdeleter¡£
>>> acct = Account() >>> acct.acct_num = 1000 >>> acct.acct_num 1000 |
PythonÒ²ÌṩÁË@property×°ÊÎÆ÷£¬¶ÔÓÚ¼òµ¥µÄÓ¦Óó¡¾°¿ÉÒÔʹÓÃËüÀ´´´½¨ÊôÐÔ¡£Ò»¸öÊôÐÔ¶ÔÏóÓµÓÐgetter,setterºÍdeleter×°ÊÎÆ÷·½·¨£¬¿ÉÒÔʹÓÃËüÃÇͨ¹ý¶ÔÓ¦µÄ±»×°Êκ¯ÊýµÄaccessorº¯Êý´´½¨ÊôÐԵĿ½±´¡£
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·½·¨¡£
ÔÚÔËÐÐʱ´´½¨ÃèÊö·û
ÎÒÃÇ¿ÉÒÔÔÚÔËÐÐʱÌí¼Ó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'} |
¾²Ì¬·½·¨ºÍÀà·½·¨
ÎÒÃÇ¿ÉÒÔʹÓÃÃèÊö·ûÀ´Ä£ÄâPythonÖеÄ@staticmethodºÍ@classmethodµÄʵÏÖ¡£ÎÒÃÇÊ×ÏÈÀ´ä¯ÀÀÒ»ÏÂÏÂÃæÕâÕÅ±í£º

¾²Ì¬·½·¨
¶ÔÓÚ¾²Ì¬·½·¨f¡£c.fºÍC.fÊǵȼ۵쬶¼ÊÇÖ±½Ó²éѯobject.__getattribute__(c,
¡®f¡¯)»òÕßobject.__getattribute__(C, ¡¯f¡®)¡£¾²Ì¬·½·¨Ò»¸öÃ÷ÏÔµÄÌØÕ÷¾ÍÊÇûÓÐself±äÁ¿¡£
¾²Ì¬·½·¨ÓÐʲôÓÃÄØ£¿¼ÙÉèÓÐÒ»¸ö´¦ÀíרÃÅÊý¾ÝµÄÈÝÆ÷À࣬ËüÌṩÁËһЩ·½·¨À´Ç󯽾ùÊý£¬ÖÐλÊýµÈͳ¼ÆÊý¾Ý·½Ê½£¬ÕâЩ·½·¨¶¼ÊÇÒªÒÀÀµÓÚÏàÓ¦µÄÊý¾ÝµÄ¡£µ«ÊÇÀàÖпÉÄÜ»¹ÓÐһЩ·½·¨£¬²¢²»ÒÀÀµÕâЩÊý¾Ý£¬Õâ¸öʱºòÎÒÃÇ¿ÉÒÔ½«ÕâЩ·½·¨ÉùÃ÷Ϊ¾²Ì¬·½·¨£¬Í¬Ê±ÕâÒ²¿ÉÒÔÌá¸ß´úÂëµÄ¿É¶ÁÐÔ¡£
ʹÓ÷ÇÊý¾ÝÃèÊö·ûÀ´Ä£ÄâһϾ²Ì¬·½·¨µÄʵÏÖ£º
class StaticMethod(object): def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): return self.f |
ÎÒÃÇÀ´Ó¦ÓÃһϣº
class MyClass(object): @StaticMethod def get_x(x): return x print(MyClass.get_x(100)) # output: 100 |
Àà·½·¨
PythonµÄ@classmethodºÍ@staticmethodµÄÓ÷¨ÓÐЩÀàËÆ£¬µ«ÊÇ»¹ÊÇÓÐЩ²»Í¬£¬µ±Ä³Ð©·½·¨Ö»ÐèÒªµÃµ½ÀàµÄÒýÓöø²»¹ØÐÄÀàÖеÄÏàÓ¦µÄÊý¾ÝµÄʱºò¾ÍÐèҪʹÓÃclassmethodÁË¡£
ʹÓ÷ÇÊý¾ÝÃèÊö·ûÀ´Ä£ÄâÒ»ÏÂÀà·½·¨µÄʵÏÖ£º
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 |
ÆäËûµÄħÊõ·½·¨
Ê״νӴ¥PythonħÊõ·½·¨µÄʱºò£¬ÎÒÒ²±»__get__, __getattribute__, __getattr__,
__getitem__Ö®¼äµÄÇø±ðÀ§Èŵ½ÁË£¬ËüÃǶ¼ÊǺÍÊôÐÔ·ÃÎÊÏà¹ØµÄħÊõ·½·¨£¬ÆäÖÐÖØÐ´__getattr__£¬__getitem__À´¹¹ÔìÒ»¸ö×Ô¼ºµÄ¼¯ºÏÀà·Ç³£µÄ³£Óã¬ÏÂÃæÎÒÃǾÍͨ¹ýһЩÀý×ÓÀ´¿´Ò»ÏÂËüÃǵÄÓ¦Óá£
__getattr__
PythonĬÈÏ·ÃÎÊÀà/ʵÀýµÄij¸öÊôÐÔ¶¼ÊÇͨ¹ý__getattribute__À´µ÷Óõģ¬__getattribute__»á±»ÎÞÌõ¼þµ÷Óã¬Ã»ÓÐÕÒµ½µÄ»°¾Í»áµ÷ÓÃ__getattr__¡£Èç¹ûÎÒÃÇÒª¶¨ÖÆÄ³¸öÀ࣬ͨ³£Çé¿öÏÂÎÒÃDz»Ó¦¸ÃÖØÐ´__getattribute__£¬¶øÊÇÓ¦¸ÃÖØÐ´__getattr__£¬ºÜÉÙ¿´¼ûÖØÐ´__getattribute__µÄÇé¿ö¡£
´ÓÏÂÃæµÄÊä³ö¿ÉÒÔ¿´³ö£¬µ±Ò»¸öÊôÐÔͨ¹ý__getattribute__ÎÞ·¨ÕÒµ½µÄʱºò»áµ÷ÓÃ__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__' |
Ó¦ÓÃ
¶ÔÓÚĬÈϵÄ×ֵ䣬PythonÖ»Ö§³ÖÒÔobj['foo']ÐÎʽÀ´·ÃÎÊ£¬²»Ö§³Öobj.fooµÄÐÎʽ£¬ÎÒÃÇ¿ÉÒÔͨ¹ýÖØÐ´__getattr__ÈÃ×ÖµäÒ²Ö§³Öobj['foo']µÄ·ÃÎÊÐÎʽ£¬ÕâÊÇÒ»¸ö·Ç³£¾µä³£ÓõÄÓ÷¨£º
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 ' + dict.__repr__(self) + '>'! |
ÎÒÃÇÀ´Ê¹ÓÃÒ»ÏÂÎÒÃÇ×Ô¶¨ÒåµÄ¼ÓÇ¿°æ×ֵ䣺
>>> 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ÓÃÓÚͨ¹ýϱê[]µÄÐÎʽÀ´»ñÈ¡¶ÔÏóÖеÄÔªËØ£¬ÏÂÃæÎÒÃÇͨ¹ýÖØÐ´__getitem__À´ÊµÏÖÒ»¸ö×Ô¼ºµÄ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] |
Õâ¸öʵÏַdz£µÄ¼òª£¬²»Ö§³ÖsliceºÍstepµÈ¹¦ÄÜ£¬Çë¶ÁÕß×ÔÐиĽø£¬ÕâÀïÎҾͲ»Öظ´ÁË¡£
Ó¦ÓÃ
ÏÂÃæÊDzο¼requests¿âÖжÔÓÚ__getitem__µÄÒ»¸öʹÓã¬ÎÒÃǶ¨ÖÆÁËÒ»¸öºöÂÔÊôÐÔ´óСдµÄ×ÖµäÀà¡£
³ÌÐòÓÐЩ¸´ÔÓ£¬ÎÒÉÔ΢½âÊÍһϣºÓÉÓÚÕâÀï±È½Ï¼òµ¥£¬Ã»ÓÐʹÓÃÃèÊö·ûµÄÐèÇó£¬ËùÒÔʹÓÃÁË@property×°ÊÎÆ÷À´´úÌæ£¬lower_keysµÄ¹¦ÄÜÊǽ«ÊµÀý×ÖµäÖеļüÈ«²¿×ª»»³ÉСд²¢ÇÒ´æ´¢ÔÚ×Öµäself._lower_keysÖС£ÖØÐ´ÁË__getitem__·½·¨£¬ÒÔºóÎÒÃÇ·ÃÎÊij¸öÊôÐÔÊ×ÏȻὫ¼üת»»ÎªÐ¡Ð´µÄ·½Ê½£¬È»ºó²¢²»»áÖ±½Ó·ÃÎÊʵÀý×ֵ䣬¶øÊÇ»á·ÃÎÊ×Öµäself._lower_keysÈ¥²éÕÒ¡£¸³Öµ/ɾ³ý²Ù×÷µÄʱºòÓÉÓÚʵÀý×Öµä»á½øÐбä¸ü£¬ÎªÁ˱£³Öself._lower_keysºÍʵÀý×Öµäͬ²½£¬Ê×ÏÈÇå³ýself._lower_keysµÄÄÚÈÝ£¬ÒÔºóÎÒÃÇÖØÐ²éÕÒ¼üµÄʱºòÔÙµ÷ÓÃ__getitem__µÄʱºò»áÖØÐÂн¨Ò»¸ö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 |
ÎÒÃÇÀ´µ÷ÓÃÒ»ÏÂÕâ¸öÀࣺ
>>> 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 |
|