资讯专栏INFORMATION COLUMN

Python中几种属性访问的区别

zhouzhou / 412人阅读

摘要:起步的提供一系列和属性访问有关的特殊方法。本文阐述它们的区别和用法。属性的访问机制一般情况下,属性访问的默认行为是从对象的字典中获取,并当获取不到时会沿着一定的查找链进行查找。则这个类的对象称为描述符。

起步

python的提供一系列和属性访问有关的特殊方法:__get__, __getattr__, __getattribute__, __getitem__ 。本文阐述它们的区别和用法。

属性的访问机制

一般情况下,属性访问的默认行为是从对象的字典中获取,并当获取不到时会沿着一定的查找链进行查找。例如 a.x 的查找链就是,从 a.__dict__["x"] ,然后是 type(a).__dict__["x"] ,再通过 type(a) 的基类开始查找。

若查找链都获取不到属性,则抛出 AttributeError 异常。

__getattr__ 方法

这个方法是当对象的属性不存在是调用。如果通过正常的机制能找到对象属性的话,不会调用 __getattr__ 方法。

class A:
    a = 1
    def __getattr__(self, item):
        print("__getattr__ call")
        return item

t = A()
print(t.a)
print(t.b)
# output
1
__getattr__ call
b
__getattribute__ 方法

这个方法会被无条件调用。不管属性存不存在。如果类中还定义了 __getattr__ ,则不会调用 __getattr__() 方法,除非在 __getattribute__ 方法中显示调用__getattr__() 或者抛出了 AttributeError

class A:
    a = 1
    def __getattribute__(self, item):
        print("__getattribute__ call")
        raise AttributeError

    def __getattr__(self, item):
        print("__getattr__ call")
        return item

t = A()
print(t.a)
print(t.b)

所以一般情况下,为了保留 __getattr__ 的作用,__getattribute__() 方法中一般返回父类的同名方法:

def __getattribute__(self, item):
    return object.__getattribute__(self, item)

使用基类的方法来获取属性能避免在方法中出现无限递归的情况。

__get__ 方法

这个方法比较简单说明,它与前面的关系不大。

如果一个类中定义了 __get__(), __set__()__delete__() 中的任何方法。则这个类的对象称为描述符。

class Descri(object):
    def __get__(self, obj, type=None):
        print("call get")

    def __set__(self, obj, value):
        print("call set")


class A(object):
    x = Descri()

a = A()
a.__dict__["x"] = 1  # 不会调用 __get__
a.x                  # 调用 __get__

如果查找的属性是在描述符对象中,则这个描述符会覆盖上文说的属性访问机制,体现在查找链的不同,而这个行文也会因为调用的不同而稍有不一样:

如果调用是对象实例(题目中的调用方式),a.x 则转换为调用: 。type(a).__dict__["x"].__get__(a, type(a))

如果调用的是类属性, A.x 则转换为:A.__dict__["x"].__get__(None, A)

其他情况见文末参考资料的文档

__getitem__ 方法

这个调用也属于无条件调用,这点与 __getattribute__ 一致。区别在于 __getitem__ 让类实例允许 [] 运算,可以这样理解:

__getattribute__ 适用于所有 . 运算符;

__getitem__ 适用于所有 [] 运算符。

class A(object):
    a = 1

    def __getitem__(self, item):
        print("__getitem__ call")
        return item

t = A()
print(t["a"])
print(t["b"])

如果仅仅想要对象能够通过 [] 获取对象属性可以简单的:

def __getitem(self, item):
    return object.__getattribute__(self, item)
总结

当这几个方法同时出现可能就会扰乱你了。我在网上看到一份示例还不错,稍微改了下:

class C(object):
    a = "abc"

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

    #        return "haha"
    def __getattr__(self, name):
        print("__getattr__() is called ")
        return name + " from getattr"

    def __get__(self, instance, owner):
        print("__get__() is called", instance, owner)
        return self

    def __getitem__(self, item):
        print("__getitem__ call")
        return object.__getattribute__(self, item)

    def foo(self, x):
        print(x)


class C2(object):
    d = C()


if __name__ == "__main__":
    c = C()
    c2 = C2()
    print(c.a)
    print(c.zzzzzzzz)
    c2.d
    print(c2.d.a)
    print(c["a"])

可以结合输出慢慢理解,这里还没涉及继承关系呢。总之,每个以 __ get 为前缀的方法都是获取对象内部数据的钩子,名称不一样,用途也存在较大的差异,只有在实践中理解它们,才能真正掌握它们的用法。

参考

https://docs.python.org/3/ref...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/45005.html

相关文章

  • JS几种包含for遍历方式

    摘要:区别遍历数组常用调用数组的每个元素,并将元素传递给回调函数这种循环的问题在于无法中途跳出循环,和命令不能奏效和都返回数组,返回的与原数组长度相同只返回符合条件的结果一个值和返回布尔值遍历对象所有的可枚举属性主要是为了遍历对象而设计的,不适 forEach,map,filter区别 forEach 遍历数组常用(调用数组的每个元素,并将元素传递给回调函数) let arr = [1,2...

    jcc 评论0 收藏0
  • js几种跨域方法

    摘要:四通过跨域一个页面嵌入一个外域的页面虽然两个窗体之前能获取彼此的对象,但是却拿不到上的属性和方法,例如一个页面嵌入一个的我是父窗体的方法嵌入的窗体跟的域名不同,很明显是跨域的,虽然能获取到对象,但是拿不到页面的任何方法和属性。 js跨域是指通过js在不同域之间进行相互通信或者数据传输,只要协议,域名,端口号其中有一个不同,就是跨域。下面总结一下我了解到的常用的跨域方法。 一:通过jso...

    XboxYan 评论0 收藏0
  • CSS几种常用选择器

    摘要:类和的背景颜色都为白色大于号指子代元素子代选择器类下的类元素的背景颜色为白色空格指后代元素后代选择器类下的所有类元素的背景颜色为白色与子代选择器不同,后代选择器可以包含下的所有元素,包括子孙加号指相邻元素相邻选择器紧接在类下的类元素的 类a和b的背景颜色都为白色 大于号‘ > ’指子代元素(子代选择器) .a>.b{ background-color:#fff; } 类a下的类...

    赵连江 评论0 收藏0
  • 通过面试题,让我们来了解Collection

    摘要:说一说迭代器通过集合对象获取其对应的对象判断是否存在下一个元素取出该元素并将迭代器对象指向下一个元素取出元素的方式迭代器。对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,也就是方法。 前言 欢迎关注微信公众号:Coder编程获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识!** 本章主要介绍Collection集合相关知识,结合面试中会提到...

    HelKyle 评论0 收藏0
  • 手撕面试官系列(五):Tomcat+Mysql+设计模式面试专题

    摘要:面试题答案领取方式见主页的缺省端口是多少,怎么修改有哪几种运行模式优化有几种部署方式容器是如何创建类实例用到了什么原理如何优化内存怎样调优垃圾回收怎样策略调优怎样共享处理怎样添加远程监控专业点的分析工具有哪些关于的数目怎样监视的内存使用情况 TomcatshowImg(https://segmentfault.com/img/remote/1460000019788819);(面试题+...

    GraphQuery 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<