python对象 属性访问优先级:object.name

python对象 属性访问优先级:object.name

  1. object.__getattribute__(self, name)
  2. 类 中的 数据描述符
  3. object.__dict__.get(name) 自身属性字典
  4. object.__class__.__dict__.get(name) 类属性字典 / 非数据描述符
  5. object.__getattr__(name)
  • 描述符:定义了 __get__() 的 类 #没有定义,返回描述符(普通类)实例
  • 数据(覆盖型)描述符: 定义了 __set__() 或者 __delete__() 中一个以上

类属性name如果是数据描述符,会截断 实例的属性访问和修改:

​ "self.name = value";就变成了name.__set__(self, ins, value)

​ "self.name";就变成了name.__get__(self, ins, ins.__class__ )

  • 定义 __delete__() 没定义 __set__(),在实例赋值时就会报错

  • 定义 __set__() 没定义 __delete__(),在实例删除 就会报错

试验代码:

# -*- coding: utf-8 -*-
# author: willowj
# date: 2018-01-03 21:32:50
# py2.7

class Descdata(object):

    def __init__(self, name=''):
        self.name = name
        self.value = 0
        # 常用dict,可根据不同的instance set不同的值,get 返回不同的值
        # 相当于给每一个实例绑定了一个值

    def __get__(self, instance, owner=None):

        print("Descdata:inst> %s__get__() is called---" % self.name,
              instance, owner)
        return self.value

    def __set__(self, instance, value):
        print("Descdata:inst> %s __set__() is called---" % self.name,
              instance, value)
        self.value = value

    def __delete__(self):
        print('Descdata inst %s: .__delete__---'% self.name)
        del self


class Desc_not_data(object):

    def __init__(self, name='', value='as654'):
        self.name = name
        self.value = value

    def __get__(self, instance, owner=None):
        print("Desc_not_data:inst> %s__get__() is called---" % self.name,
              instance, owner)
        return self.value


class Base(object):
    def __getattribute__(self, *args, **kwargs):
        print("Base __getattribute__() is called")
        return object.__getattribute__(self, *args, **kwargs)


class Test(Base):
    a = 'abc'
    d_d = Descdata(name='d_d') # 数据描述符,如果实例有同名属性 会拦截

    nd = Desc_not_data(name='nd') # 非数据描述符,如果实例有同名属性 不会拦截
    nd2 = Desc_not_data(name='nd2') # 非数据描述符,如果实例有同名属性 不会拦截


    def __init__(self, *args, **kwargs):
        self.d_d = 'Test_ins self d_d'
        #  if cls.d_d 是数据描述符,这里就是调用数据描述符的 __set__
        # 类属性name如果是数据描述符,会截断--实例ins: "self.name = value";
        # 要是定义了 __delete__ 没定义 __set__,在实例赋值时就会报错
        self.nd2 = 'Test_>nd2' # 优先于 非数据描述符

    def __getattribute__(self, *args, **kwargs):
        print("Test  __getattribute__() is called")
        return Base.__getattribute__(self, *args, **kwargs)

    def __getattr__(self, name):
        print("Test __getattr__() is called ")
        return name + " from __getattr__"


q = Test() # __init__ : 赋值 数据描述符
# ('Descdata:inst> d_d __set__() is called---', <__main__.Test object at 0x0000000002B69DD8>, 'Test_ins self d_d')

print('-'*30,u'visit data descriptor')

print 'q.d_d\n', q.d_d, '\n'
# ('------------------------------', u'visit data descriptor')
# q.d_d
# Test  __getattribute__() is called
# Base __getattribute__() is called
# ('Descdata:inst> d_d__get__() is called---', <__main__.Test object at 0x0000000002B69DD8>, <class '__main__.Test'>)
# Test_ins self d_d

print('-'*30,u'visit non data descriptor not overided by instance')

print 'q.nd\n', q.nd, '\n'
# ('------------------------------', u'visit non data descriptor')
# q.nd
# Test  __getattribute__() is called
# Base __getattribute__() is called
# ('Desc_not_data:inst> nd__get__() is called---', <__main__.Test object at 0x0000000002B69DD8>, <class '__main__.Test'>)
# as654
print('-'*30,u'visit non data descriptor overided by instance')
print 'q.nd2\n', q.nd2, '\n'
# ('------------------------------', u'visit non data descriptor overided by instance')
# q.nd2
# Test  __getattribute__() is called
# Base __getattribute__() is called
# Test_>nd2


print('-'*30,u"visit  attr that not exist" )
print 'q.nnnnnnnnnnnnn\n', q.nnnnnnnnnnnnn, '\n'

# ('------------------------------', u'visit  attr that not exist')
# q.nnnnnnnnnnnnn
# Test  __getattribute__() is called
# Base __getattribute__() is called
# Test __getattr__() is called
# nnnnnnnnnnnnn from __getattr__


print('\n q.__dict__')
print q.__dict__
#  q.__dict__
# Test  __getattribute__() is called
# Base __getattribute__() is called
# {'nd2': 'Test_>nd2'}
print('-'*30,'\n',' Test.__dict__')
a_dict_  = dict(Test.__dict__)
for k in a_dict_:
    print k,' '*(20-len(k)),a_dict_[k]


# ('------------------------------', '\n', ' Test.__dict__')
# a                     abc
# __module__            __main__
# nd                    <__main__.Desc_not_data object at 0x0000000002AA9DA0>
# __getattribute__      <function __getattribute__ at 0x0000000002AF0048>
# __getattr__           <function __getattr__ at 0x0000000002AF00B8>
# d_d                   <__main__.Descdata object at 0x0000000002AA9D30>
# nd2                   <__main__.Desc_not_data object at 0x0000000002AA9DD8>
# __doc__               None
# __init__              <function __init__ at 0x0000000002AB7F98>


print('-'*30)

print Test.__dict__['__getattribute__'] ,type(Test.__dict__['__getattribute__'])
# <function __getattribute__ at 0x0000000002B74048> <type 'function'>

推荐资料

https://www.cnblogs.com/Jimmy1988/p/6808237.html

https://www.zhihu.com/question/25391709/answer/30634637

https://foofish.net/what-is-descriptor-in-python.html

http://www.cnblogs.com/JohnABC/p/5685672.html

posted @ 2018-01-15 14:29  willowj  阅读(467)  评论(0编辑  收藏  举报