在python中与getattribute和setattribute混淆

时间:2013-02-09 10:50:28

标签: python python-2.7

我想知道,如果我有这样的课程

class Test(object):
    def __init__(self):
        self.a = 20
        self.b = 30

obj = Test()

当我做obj.a时,那么先叫它?

__getattr__getattr或在__dict__['a']

中查找

setattr

相同

根据Python 2.7 docs

  

对象。 __ getattr __ 自我名称

     

当属性查找未在通常位置找到属性时调用(即,它不是实例属性,也不是在类树中找到自己)。 name是属性名称。此方法应返回(计算的)属性值或引发AttributeError异常。

它说在通常的地方找不到。什么是平常的地方。我想知道什么时候叫它

object.__getattribute__(self, name)

的差异是什么

任何人都可以举例说明所有的使用方法

4 个答案:

答案 0 :(得分:38)

这有点复杂。以下是Python请求对象属性时的检查顺序。

首先,Python将检查对象的类是否具有__getattribute__方法。如果它没有定义,它将继承object.__getattribute__,它实现了查找属性值的其他方法。

下一项检查是在对象的班级__dict__中。但是,即使在那里找到了值,也可能不是属性查找的结果!如果在此处找到,则只有“数据描述符”优先。最常见的数据描述符是property对象,它是每次访问属性时将调用的函数的包装器。您可以使用装饰器创建属性:

class foo(object):
    @property
    def myAttr(self):
        return 2

在这个类中,myAttr是一个数据描述符。这只是意味着它通过同时使用__get____set__方法来实现描述符协议。 property是数据描述符。

如果该类在其__dict__中没有包含所请求名称的任何内容,object.__getattribute__将搜索其基类(在MRO之后)以查看是否继承了该类。继承的数据描述符就像对象类中的一样。

如果找到数据描述符,则调用它的__get__方法,返回值成为属性查找的值。如果找到一个不是数据描述符的对象,它会暂时保留,但暂不返回。

接下来,检查对象自己的__dict__的属性。这是找到大多数正常成员变量的地方。

如果对象的__dict__没有任何内容,但是通过类(或基类)的早期搜索发现了除数据描述符之外的其他内容,那么它将具有下一个优先级。将简单地返回一个普通的类变量,但“非数据描述符”将获得更多的处理。

非数据描述符是具有__get__方法但没有__set__方法的对象。最常见的非数据描述符类型是函数,当从对象作为非数据描述符进行访问时,它们成为绑定方法(这就是Python可以自动将对象作为第一个参数传递的方式)。将调用描述符的__get__方法,它的返回值将是属性查找的结果。

最后,如果之前的检查都没有成功,将调用__getattr__(如果存在)。

以下是一些使用稳定增加的优先级属性访问机制来覆盖其父类行为的类:

class O1(object):
    def __getattr__(self, name):
        return "__getattr__ has the lowest priority to find {}".format(name)

class O2(O1):
    var = "Class variables and non-data descriptors are low priority"
    def method(self): # functions are non-data descriptors
        return self.var

class O3(O2):
    def __init__(self):
        self.var = "instance variables have medium priority"
        self.method = lambda: self.var # doesn't recieve self as arg

class O4(O3):
    @property # this decorator makes this instancevar into a data descriptor
    def var(self):
        return "Data descriptors (such as properties) are high priority"

    @var.setter # I'll let O3's constructor set a value in __dict__
    def var(self, value):
        self.__dict__["var"]  = value # but I know it will be ignored

class O5(O4):
    def __getattribute__(self, name):
        if name in ("magic", "method", "__dict__"): # for a few names
            return super(O5, self).__getattribute__(name) # use normal access

        return "__getattribute__ has the highest priority for {}".format(name)

并且,演示了这些课程:

O1(__getattr__):

>>> o1 = O1()
>>> o1.var
'__getattr__ has the lowest priority to find var'

O2(类变量和非数据描述符):

>>> o2 = O2()
>>> o2.var
Class variables and non-data descriptors are low priority'
>>> o2.method
<bound method O2.method of <__main__.O2 object at 0x000000000338CD30>>
>>> o2.method()
'Class variables and non-data descriptors are low priority'

O3(实例变量,包括本地重写的方法):

>>> o3 = O3()
>>> o3.method
<function O3.__init__.<locals>.<lambda> at 0x00000000034AAEA0>
>>> o3.method()
'instance variables have medium priority'
>>> o3.var
'instance variables have medium priority'

O4(数据描述符,使用property装饰器):

>>> o4 = O4()
>>> o4.method()
'Data descriptors (such as properties) are high priority'
>>> o4.var
'Data descriptors (such as properties) are high priority'
>>> o4.__dict__["var"]
'instance variables have medium priority'

O5(__getattribute__):

>>> o5 = O5()
>>> o5.method
<function O3.__init__.<locals>.<lambda> at 0x0000000003428EA0>
>>> o5.method()
'__getattribute__ has the highest priority for var'
>>> o5.__dict__["var"]
'instance variables have medium priority'
>>> o5.magic
'__getattr__ has the lowest priority to find magic'

答案 1 :(得分:1)

class test():
    def __init__(self):
        self.a = 1

    def __getattribute__(self, attr):
        print 'Getattribute:',attr

    def __getattr__(self, attr):
        print 'GetAttr:',attr

    def __dict__(self, attr):
        print 'Dict:',attr

    def __call__(self, args=None):
        print 'Called:',args

    def __getitem__(self, attr):
        print 'GetItem:',attr

    def __get__(self, instance, owner):
        print 'Get:',instance,owner

    def __int__(self):
        print 'Int'



x = test()
print x.a

以上都不会被称为..

[root@faparch doxid]# python -m trace --trace test_dict.py
 --- modulename: test_dict, funcname: <module>
test_dict.py(1): class test():
 --- modulename: test_dict, funcname: test
test_dict.py(1): class test():
test_dict.py(2):    def __init__(self):
test_dict.py(5):    def __getattribute__(self, attr):
test_dict.py(8):    def __getattr__(self, attr):
test_dict.py(11):   def __dict__(self, attr):
test_dict.py(14):   def __call__(self, args=None):
test_dict.py(17):   def __getitem__(self, attr):
test_dict.py(20):   def __get__(self, instance, owner):
test_dict.py(23):   def __int__(self):
test_dict.py(28): x = test()
 --- modulename: test_dict, funcname: __init__
test_dict.py(3):        self.a = 1
test_dict.py(29): print x.a
1
 --- modulename: trace, funcname: _unsettrace
trace.py(80):         sys.settrace(None)

您可能需要查看:http://docs.python.org/2/library/numbers.html#numbers.Number 很可能你需要实现一个处理数字类函数的嵌套类,以抢夺例如示例中的调用。或者,至少这是一种做法..

整数值包含以下必须拦截的函数

['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__format__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'imag', 'numerator', 'real']

答案 2 :(得分:0)

首先调用命名空间字典(__dict__)。

来自docs

  

一个类实例有一个名称实现为字典的名称空间   搜索属性引用的第一个位置。当一个   在那里找不到属性,并且实例的类有一个   通过该名称的属性,搜索继续使用类属性   如果没有找到类属性,并且对象的类有一个   调用__getattr__()方法来满足查找。

object.__getattribute__上的文档:

  

无条件调用以实现实例的属性访问   班上的。如果该类也定义__getattr__(),后者将定义   除非__getattribute__()明确地或者明确地调用它,否则不会被调用   提出AttributeError。这个方法应该返回(计算)   属性值或引发AttributeError例外。

object.__getattr__(self, name)上的文档:

  

如果通过正常机制找到该属性,__getattr__()   不被称为。

答案 3 :(得分:0)

来自the docs

  

...例如,obj.d查找d in   对象词典如果d定义方法__get__(),那么   根据列出的优先规则调用d.__get__(obj)   下方。

     

...   对于物体,机器在object.__getattribute__()中   将b.x转换为type(b).__dict__['x'].__get__(b, type(b))。该   实现通过提供数据的优先级链来工作   描述符优先于实例变量,实例变量   优先于非数据描述符,并为其分配最低优先级   __getattr__()如果提供的话。

即,obj.a调用__getattribute__(),默认使用__dict____getattr__()被称为最后的手段。其余的描述描述符,例如property或普通方法行为。

  

d如何定义方法__get__()

import random 

class C(object):
    @property
    def d(self):
        return random.random()

c = C()
print(c.d)