详解Python中 __get__和__getattr__和__getattribute__的区别

引子
假设我们有个类A,其中a是A的实例
a.x时发生了什么?属性的lookup顺序如下:

  • 如果重载了__getattribute__,则调用.
  • a.__dict__, 实例中是不允许有descriptor的,所以不会遇到descriptor
  • A.__dict__, 也即a.__class__.__dict__ .如果遇到了descriptor,优先调用descriptor.
  • 沿着继承链搜索父类.搜索a.__class__.__bases__中的所有__dict__. 如果有多重继承且是菱形继承的情况,按MRO(Method Resolution Order)顺序搜索.

如果以上都搜不到,则抛AttributeError异常.

ps.从上面可以看到,dot(.)操作是昂贵的,很多的隐式调用,特别注重性能的话,在高频的循环内,可以考虑绑定给一个临时局部变量.

class C(object):
  def __setattr__(self, name, value):
    print "__setattr__ called:", name, value
    object.__setattr__(self, name, value) 

  def __getattr__(self, name):
    print "__getattr__ called:", name 

  def __getattribute__(self, name):
    print "__getattribute__ called:",name
    return object.__getattribute__(self, name) 

c = C()
c.x = "foo"
print c.__dict__['x']
print c.x


深入
1.object.__getattr__(self, name)
当一般位置找不到attribute的时候,会调用getattr,返回一个值或AttributeError异常。

2.object.__getattribute__(self, name)
无条件被调用,通过实例访问属性。如果class中定义了__getattr__(),则__getattr__()不会被调用(除非显示调用或引发AttributeError异常)

3.object.__get__(self, instance, owner)
如果class定义了它,则这个class就可以称为descriptor。owner是所有者的类,instance是访问descriptor的实例,如果不是通过实例访问,而是通过类访问的话,instance则为None。(descriptor的实例自己访问自己是不会触发__get__,而会触发__call__,只有descriptor作为其它类的属性才有意义。)(所以下文的d是作为C2的一个属性被调用)

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

输出结果是:

__getattribute__() is called
abc
__getattribute__() is called
__getattr__() is called
zzzzzzzz from getattr
__get__() is called <__main__.C2 object at 0x16d2310> <class '__main__.C2'>
__get__() is called <__main__.C2 object at 0x16d2310> <class '__main__.C2'>
__getattribute__() is called
abc

小结:

可以看出,每次通过实例访问属性,都会经过__getattribute__函数。而当属性不存在时,仍然需要访问__getattribute__,不过接着要访问__getattr__。这就好像是一个异常处理函数。
每次访问descriptor(即实现了__get__的类),都会先经过__get__函数。

需要注意的是,当使用类访问不存在的变量是,不会经过__getattr__函数。而descriptor不存在此问题,只是把instance标识为none而已。

(0)

相关推荐

  • 全面了解Python的getattr(),setattr(),delattr(),hasattr()

    1. getattr()函数是Python自省的核心函数,具体使用大体如下: class A: def __init__(self): self.name = 'zhangjing' #self.age='24' def method(self): print"method print" Instance = A() print getattr(Instance , 'name, 'not find') #如果Instance 对象中有属性name则打印self.name的值,否则打印

  • 浅析Python中的getattr(),setattr(),delattr(),hasattr()

    getattr()函数是Python自省的核心函数,具体使用大体如下: 获取对象引用getattr Getattr用于返回一个对象属性,或者方法 class A: def __init__(self): self.name = 'zhangjing' #self.age='' def method(self): print"method print" Instance = A() print getattr(Instance , 'name, 'not find') #如果Instan

  • 对比Python中__getattr__和 __getattribute__获取属性的用法

    相信大家觉得大多数时候我们并不太需要关注getattribute和getattr的一些细节(至少我自己吧:)), 一般情况下消费我们自定义的类的时候,我们对类的结构都了解,不会刻意偏离,造成一些属性访问的错误. 不过作为一个有好奇心有追求有气质的python宝宝,怎么可能不稍稍研究一下呢.好吧,其实是在github上读到一个开源项目sinaweibopy的源码才看的,代码挺有意思,正好当作一个实用的例子,来看看如何自定义实现gettattr让代码更加的动态优雅: # 例子在原来的基础上简化了一下

  • python中的内置函数getattr()介绍及示例

    在python的官方文档中:getattr()的解释如下: getattr(object, name[, default]) Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object's attributes, the result is the value of that attribute. For examp

  • Python __getattr__与__setattr__使用方法

    比如下面的例子: class Book(object):    def __setattr__(self, name, value):        if name == 'value':            object.__setattr__(self, name, value - 100)        else:            object.__setattr__(self, name, value)    def __getattr__(self, name):       

  • Python中getattr函数和hasattr函数作用详解

    hasattr(object, name) 作用:判断对象object是否包含名为name的特性(hasattr是通过调用getattr(ojbect, name)是否抛出异常来实现的). 示例: >>> hasattr(list, 'append') True >>> hasattr(list, 'add') False getattr(object,name,default): 作用:返回object的名称为name的属性的属性值,如果属性name存在,则直接返回其

  • python中getattr函数使用方法 getattr实现工厂模式

    看了下函数本身的doc 复制代码 代码如下: getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception i

  • Python __setattr__、 __getattr__、 __delattr__、__call__用法示例

    getattr `getattr`函数属于内建函数,可以通过函数名称获取 复制代码 代码如下: value = obj.attribute value = getattr(obj, "attribute") 使用`getattr`来实现工厂模式 复制代码 代码如下: #一个模块支持html.text.xml等格式的打印,根据传入的formate参数的不同,调用不同的函数实现几种格式的输出 import statsout def output(data, format="tex

  • 浅谈python中的getattr函数 hasattr函数

    hasattr(object, name) 作用:判断对象object是否包含名为name的特性(hasattr是通过调用getattr(ojbect, name)是否抛出异常来实现的). 示例: >>> hasattr(list, 'append') True >>> hasattr(list, 'add') False getattr(object,name,default): 作用:返回object的名称为name的属性的属性值,如果属性name存在,则直接返回其

  • 详解Python中 __get__和__getattr__和__getattribute__的区别

    引子 假设我们有个类A,其中a是A的实例 a.x时发生了什么?属性的lookup顺序如下: 如果重载了__getattribute__,则调用. a.__dict__, 实例中是不允许有descriptor的,所以不会遇到descriptor A.__dict__, 也即a.__class__.__dict__ .如果遇到了descriptor,优先调用descriptor. 沿着继承链搜索父类.搜索a.__class__.__bases__中的所有__dict__. 如果有多重继承且是菱形继承

  • 详解Python中sorted()和sort()的使用与区别

    目录 sort()方法是什么 如何妙用sorted() 方法 总结 在 Python 中,你可以使用 sorted() 方法或 sort() 方法对数据进行排序. 在本文中,我将提供 sorted() 和 sort() 方法的代码示例,并解释两者之间的区别. sort()方法是什么 此方法接受一个列表并对其进行排序.但,请记住此方法没有返回值,即返回None. 下面例子中,我们有一个数字列表,我们可以使用 sort() 方法按升序对列表进行排序. my_list = [67, 2, 999, 1

  • 详解Python中__new__方法的作用

    目录 前言 一.__new__方法简介 1.初始化数据加载+解析类实例 2.初始化数据加载重写new方法+解析类实例 二.单例模式 1.用new方法如何实现单例模式 2.如何控制类仅执行一次初始化方法 三.多例模式 总结 前言 Python中类的构造方法__new__方法有何作用? Python类中有些方法名.属性名的前后都添加__双下画线,这种方法.属性通常属于Python的特殊方法和特殊属性.通过重写这些方法或直接调用这些方法来实现特殊功能.今天来聊聊构造方法__new__实际程序的应用场景

  • 详解python中executemany和序列的使用方法

    详解python中executemany和序列的使用方法 一 代码 import sqlite3 persons=[ ("Jim","Green"), ("Hu","jie") ] conn=sqlite3.connect(":memory:") conn.execute("CREATE TABLE person(firstname,lastname)") conn.executeman

  • 详解python中 os._exit() 和 sys.exit(), exit(0)和exit(1) 的用法和区别

    详解python中 os._exit() 和 sys.exit(), exit(0)和exit(1) 的用法和区别 os._exit() 和 sys.exit() os._exit() vs sys.exit() 概述 Python的程序有两中退出方式:os._exit(), sys.exit().本文介绍这两种方式的区别和选择. os._exit()会直接将python程序终止,之后的所有代码都不会继续执行. sys.exit()会引发一个异常:SystemExit,如果这个异常没有被捕获,那

  • 详解 Python中LEGB和闭包及装饰器

    详解 Python中LEGB和闭包及装饰器 LEGB L>E>G?B L:local函数内部作用域 E:enclosing函数内部与内嵌函数之间 G:global全局作用域 B:build-in内置作用域 python 闭包 1.Closure:内部函数中对enclosing作用域变量的引用 2.函数实质与属性 函数是一个对象 函数执行完成后内部变量回收 函数属性 函数返回值 passline = 60 def func(val): if val >= passline: print (

  • 详解python中的文件与目录操作

    详解python中的文件与目录操作 一 获得当前路径 1.代码1 >>>import os >>>print('Current directory is ',os.getcwd()) Current directory is D:\Python36 2.代码2 如果将上面的脚本写入到文件再运行 Current directory is E:\python\work 二 获得目录的内容 Python代码 >>> os.listdir (os.getcwd

  • 详解python中的 is 操作符

    大家可以与Java中的 == 操作符相互印证一下,加深一下对引用和对象的理解.原问题: Python为什么直接运行和在命令行运行同样语句但结果却不同,他们的缓存机制不同吗? 其实,高票答案已经说得很详细了.我只是再补充一点而已. is 操作符是Python语言的一个内建的操作符.它的作用在于比较两个变量是否指向了同一个对象. 与 == 的区别 class A(): def __init__(self, v): self.value = v def __eq__(self, t): return

  • 详解python中asyncio模块

    一直对asyncio这个库比较感兴趣,毕竟这是官网也非常推荐的一个实现高并发的一个模块,python也是在python 3.4中引入了协程的概念.也通过这次整理更加深刻理解这个模块的使用 asyncio 是干什么的? 异步网络操作并发协程 python3.0时代,标准库里的异步网络模块:select(非常底层) python3.0时代,第三方异步网络库:Tornado python3.4时代,asyncio:支持TCP,子进程 现在的asyncio,有了很多的模块已经在支持:aiohttp,ai

  • 详解python中的线程

    Python中创建线程有两种方式:函数或者用类来创建线程对象. 函数式:调用 _thread 模块中的start_new_thread()函数来产生新线程. 类:创建threading.Thread的子类来包装一个线程对象. 1.线程的创建 1.1 通过thread类直接创建 import threading import time def foo(n): time.sleep(n) print("foo func:",n) def bar(n): time.sleep(n) prin

随机推荐