GVKun编程网logo

如何在不破坏默认行为的情况下覆盖 Python 中的 __getattr__?

8

本文将介绍如何在不破坏默认行为的情况下覆盖Python中的__getattr__?的详细情况,。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于Python3_

本文将介绍如何在不破坏默认行为的情况下覆盖 Python 中的 __getattr__?的详细情况,。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于Python 3 __getattr__的行为不同于Python 2?、python __getattribute__、__getattr__、__setattr__详解、Python __getattr__、python __getattr__ __setattr__的知识。

本文目录一览:

如何在不破坏默认行为的情况下覆盖 Python 中的 __getattr__?

如何在不破坏默认行为的情况下覆盖 Python 中的 __getattr__?

我想重写__getattr__类上的方法来做一些花哨的事情,但我不想破坏默认行为。

这样做的正确方法是什么?

Python 3 __getattr__的行为不同于Python 2?

Python 3 __getattr__的行为不同于Python 2?

我需要编写一个实现32位无符号整数的类,就像使用C编程语言一样。我最关心的是二进制转换,但是我通常希望我的班级能够做到:

  1. 具有相同的界面int并可以int正常使用
  2. 我的U32课程的任何操作(int + U32U32+ int等)也将返回U32
  3. 是纯Python-我不想使用NumPy,ctypes等。

如在此答案中可以找到的,我得到了一个在Python
2下工作的解决方案。最近,我尝试在Python 3下运行它,并注意到,尽管以下测试代码在旧版本的Python下可以正常工作,但Python 3引发了错误:

class U32:    """Emulates 32-bit unsigned int known from C programming language."""    def __init__(self, num=0, base=None):        """Creates the U32 object.        Args:            num: the integer/string to use as the initial state            base: the base of the integer use if the num given was a string        """        if base is None:            self.int_ = int(num) % 2**32        else:            self.int_ = int(num, base) % 2**32    def __coerce__(self, ignored):        return None    def __str__(self):        return "<U32 instance at 0x%x, int=%d>" % (id(self), self.int_)    def __getattr__(self, attribute_name):        print("getattr called, attribute_name=%s" % attribute_name)        # you might want to take a look here:        # https://stackoverflow.com/q/19611001/1091116        r = getattr(self.int_, attribute_name)        if callable(r):  # return a wrapper if integer''s function was requested            def f(*args, **kwargs):                if args and isinstance(args[0], U32):                    args = (args[0].int_, ) + args[1:]                ret = r(*args, **kwargs)                if ret is NotImplemented:                    return ret                if attribute_name in [''__str__'', ''__repr__'', ''__index__'']:                    return ret                ret %= 2**32                return U32(ret)            return f        return rprint(U32(4) / 2)print(4 / U32(2))print(U32(4) / U32(2))

这是错误:

Traceback (most recent call last):  File "u32.py", line 41, in <module>    print(U32(4) / 2)TypeError: unsupported operand type(s) for /: ''U32'' and ''int''

看来该getattr技巧在Python 3中根本没有被调用。为什么?如何使此代码在Python 2和3下都能正常工作?

答案1

小编典典

您的Python 2解决方案依赖于 旧式类的 行为。如果使类继承自Python 2,则Python 2代码将以与Python3相同的方式失败object

class U32(object):

这是因为对于新 ,将在 类型 而不是对象本身上查找特殊方法。此行为更改修复了旧模型的一些极端情况。

实际上,这意味着像这样的方法__div__将直接在U32自身上查找,而不是在的 实例
上作为属性U32,并且__getattr__不会查询该挂钩。

不幸的是,特殊的方法也查找 绕过
任何__getattr____getattribute__挂钩。请参阅有关特殊方法查找的文档:

除了出于正确性的考虑绕过任何实例属性之外,隐式特殊方法查找通常还绕过该__getattribute__()方法,甚至对象的元类也是如此:

[…]

__getattribute__()以这种方式绕过机器为解释器内的速度优化提供了很大的空间,但以牺牲一些特殊方法的灵活性为代价(特殊方法必须在类对象本身上设置,以便由解释器一致地调用)

那么,您唯一的选择是在类上动态设置所有特殊方法。一个类装饰器可以在这里做得很好:

def _build_delegate(name, attr, cls, type_):    def f(*args, **kwargs):        args = tuple(a if not isinstance(a, cls) else a.int_ for a in args)        ret = attr(*args, **kwargs)        if not isinstance(ret, type_) or name == ''__hash__'':            return ret        return cls(ret)    return fdef delegated_special_methods(type_):    def decorator(cls):        for name, value in vars(type_).items():            if (name[:2], name[-2:]) != (''__'', ''__'') or not callable(value):                continue            if hasattr(cls, name) and not name in (''__repr__'', ''__hash__''):                continue            setattr(cls, name, _build_delegate(name, value, cls, type_))        return cls    return decorator@delegated_special_methods(int)class U32(object):    def __init__(self, num=0, base=None):        """Creates the U32 object.        Args:            num: the integer/string to use as the initial state            base: the base of the integer use if the num given was a string        """        if base is None:            self.int_ = int(num) % 2**32        else:            self.int_ = int(num, base) % 2**32    def __coerce__(self, ignored):        return None    def __str__(self):        return "<U32 instance at 0x%x, int=%d>" % (id(self), self.int_)

我更新了代理功能以正确处理多个参数,并在返回时自动强制返回到您的自定义类int

python __getattribute__、__getattr__、__setattr__详解

python __getattribute__、__getattr__、__setattr__详解

__getattribute__

官方文档中描述如下:

该方法可以拦截对对象属性的所有访问企图,当属性被访问时,自动调用该方法(只适用于新式类)。因此常用于实现一些访问某属性时执行一段代码的特性。

需要注意的是,正式由于它拦截对所有属性的访问(包括对__dict__的访问),在使用中要十分小心地避开无限循环的陷阱。在__getattribute__方法中访问当前实例的属性时,唯一安全的方式是使用基类(超类) 的方法__getattribute__(使用super)。例如:

通过上图中的代码示例可以看出,一旦实现了__getattribute__方法,所有通过对象访问的属性(包括类属性)都会被拦截,而直接通过类访问类属性则不会。

注意:当访问的属性不存在并重载(覆盖基类对某方法的默认实现)了__getattribute__方法时,该方法不会主动抛出AttributeError异常。上图中捕获的AttributeError异常,是由基类__getattribute__方法实现并抛出。

常见的错误用法示例:

在实现__getattribute__方法时访问对象自身的属性,程序陷入无限循环直到崩溃。

__getattr__

官方文档描述如下:

__getattr__方法的自动执行,需要满足两个条件:一是访问对象属性;二是触发AttributeError异常。代码示例如下:

上图中,调用不存在的job属性首先调用__getattribute__方法(如果该方法未定义,会调用基类的__getattribute__方法),触发AttributeError异常并自动捕获,然后才调用__getattr__方法。

错误用法示例如下:

重载了__getattribute__方法,却没有主动抛出AttributeError异常的机制,或者抛出一个其它类型的异常,__getattr__方法都不会执行。

__setattr__

试图给属性赋值时自动调用该方法,例如:

之所以会执行三次print函数,是因为在__init__方法中,对象A初始化时给属性name和age赋值时,触发了__setattr__方法。使用该方法是同样需要十分小心避免无限循环陷阱。

错误用法示例如下:

可以看出,在__setattr__方法中,不能直接给属性赋值,而通常的做法是使用__dict__魔法属性。__dict__属性是一个字典,所有的实例属性都存储在这个字典中,而修改__dict__字典中的键值对成员不会触发__setattr__方法,这里应注意与直接修改__dict__的值的区别。

注意:如果定义__setattr__方法的同时定义了__getattribute__方法,那么在修改__dict__字典中的键值对时,由于调用了self.__dict__属性,同样会触发__getattribute__方法,使用时应格外小心。代码示例如下:

上图示例代码中,每调用一次__setattr__就会调用一次__getattribute__。

注意赋值语句与属性调用的区别:self.__dict__ = {}是赋值语句,不会触发__getattribute__方法,但触发__setattr__方法;self.__dict__[name] = value语句,先调用self.__dict__属性,得到dict对象后再修改其成员,因此会触发__getattribute__方法。 

以上。

Python __getattr__

Python __getattr__

Python Code:

######################################

class D(object):
    def __init__(self):
        self.default = 0.0
        self.test = 20
        self.test2 = 21
    def __getattribute__(self, name):
        try:
            if name == ''test'':
                print ("__getattribute__ name = %s" % name)
                return self.test        #This will default call same function "__getattribute__", can fix by object
            else:
                print ("__getattribute__ name = %s" % name)
                return object.__getattribute__(self, name)
        except AttributeError:
            print "getattribute error"
            raise
    def __getattr__(self, name):
        print ("__getattr__ name = %s" % name)
        print ("self.default=%s" % self.default)
        return self.default
if __name__ == "__main__":
    d = D()
    #print d.test       #This will caused recurison error because it call the same function, your getattribute
    print d.test2
    print d.ts           #When "__getattribute__" raise error, it will call in "__getattr__".

######################################

输出:
>>> 
__getattribute__ name = test2
21
__getattribute__ name = ts
getattribute error
__getattr__ name = ts
__getattribute__ name = default
self.default=0.0
__getattribute__ name = default
0.0

######################################

1. 任何调用类的属性行为都将从"__getattribute__"开始调用。

2. 在"__getattribute__"中调用该函数自身属性,将会导致无限循环。应该借用object对象再调用其本身。

3. "__getattr__"将会在寻找不到合适的函数或者属性时作为默认被调用到。

Python doc:

__getattr__( self, name)

Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.
Note that if the attribute is found through the normal mechanism, __getattr__() is not called. (This is an intentional asymmetry between __getattr__() and __setattr__().) This is done both for efficiency reasons and because otherwise __setattr__() would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the __getattribute__() method below for a way to actually get total control in new-style classes.
     object.__getattribute__(self, name)? 

Called unconditionally to implement attribute accesses for instances of the class. If the class also   defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError. This method should return the (computed) attribute value or raise an AttributeError exception. In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).

4. 通常来说,如果脚本即包含"__getattribute__" ,又包含"__getattr__"的时候,是不会调用到"__getattr__"的,但从上面的例子可以看出,如果"__getattribute__"报错,同样会自动调用到"__getattr__"。

python __getattr__ __setattr__

python __getattr__ __setattr__

class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0

    def __setattr__(self, key, value):
        if  key == ''size'':
            self.width, self.height = value
        else:
            self.__dict__[key] = value

    def __getattr__(self, item):
        if item == ''size'':
            return self.width, self.height
        else:
            return self.__dict__[item]

tR = Rectangle()
tR.__setattr__(''size'',(1,3))
print(tR.__getattr__(''size''))

resule:

(1, 3)

 

我们今天的关于如何在不破坏默认行为的情况下覆盖 Python 中的 __getattr__?的分享就到这里,谢谢您的阅读,如果想了解更多关于Python 3 __getattr__的行为不同于Python 2?、python __getattribute__、__getattr__、__setattr__详解、Python __getattr__、python __getattr__ __setattr__的相关信息,可以在本站进行搜索。

本文标签: