• Python——管理属性(2)


    __getattr__和__getattribute__
    眼下已经介绍了特性property和描写叙述符来管理特定属性【參考这里】,而__getattr__和__getattribute__操作符重载方法提供了拦截类实例的属性获取的还有一种方法。它们有更广泛的应用,但是它们的表现并不同:

    【1】__getattr__针对没有定义的属性执行——也就是说,属性没有存储在实例上,或者没有从其类之中的一个继承。
    【2】__getattribute__针对每一个属性。因此,当使用它的时候,必须小心避免通过把属性訪问传递给超类而导致递归循环。

    这两个方法是一组属性拦截方法的代表。这些方法还包含__setattr__和__delattr__,作用也一样。

    与特性和描写叙述符不同,这些方法是Python【操作符重载】协议的一部分——是类的特殊命名的方法,由子类继承。

    __getattr__和__getattribute__方法也比特性和描写叙述符通用。用于拦截差点儿全部的实例属性的获取,而不不过特定名称。因此,这两个方法适合于通用的基于托付的编码方式。

    ----------------------------------------------------------------------------------------------------------------------------------------

    基础知识
    假设一个类定义了或继承了例如以下方法,那么当一个实例用于后面的凝视所提到的情况时。它们将自己主动执行:

    def __getattr__(self,name): 		# 引用实例没有定义的属性obj.name
    def __getattribute(self,name):		# 引用全部属性obj.name
    def __setattr__(self,name,value):	# 设置不论什么属性obj.name = value
    def __delattr__(self,name,value):	# 删除不论什么属性del obj.name 
    全部这些中。self一般是主体实例对象,name是将要訪问的属性的字符串名。value是将要赋给该属性的对象。两个get方法通常返回一个属性的值,另两个方法返回None。比如,要捕获每一个属性的获取,我们能够使用上面的两个方法,要捕获属性赋值,能够使用第三个方法:

    >>> class Catcher:
    	def __getattr__(self,name):
    		print('Get:',name)
    	def __setattr__(self,name,value):
    		print('Set:',name,value)
    
    		
    >>> X = Catcher()
    >>> X.job
    Get: job
    >>> X.pay
    Get: pay
    >>> X.pay = 99
    Set: pay 99
    ----------------------------------------------------------------------------------------------------------------------------------------

    避免属性拦截方法中的循环

    使用这些方法要避免潜在的递归循环。
    比如,在一个__getattribute__方法代码内部的还有一次属性获取。将会再次触发__getattribute__,而且代码将会循环知道内存耗尽:

    def __getattribute__(self,name):
    	x = self.other
    要解决问题,把获取指向一个更高的超类,而不是跳过这个层级的版本号——object类总是一个超类,而且它在这里能够非常好的起作用:
    def __getattribute__(self,name):
    	x = object.__getattribute__(self,'other')
    对于__setattr__,情况是类似的,在这种方法内赋值不论什么属性,都会再次触发__setattr__并创建一个类似的循环:
    def __setattr__(self,name,value):
    	self.other = value
    要解决问题。把属性作为实例的__dict__命名空间字典中的一个键赋值。这样就避免了直接的属性赋值:
    def __setattr__(self,name,value):
    	self.__dict__['other'] = value
    另一种不经常使用的方法,__setattr__也能够把自己的属性赋值传递给一个更高的超类而避免循环,就像__getattribute__一样:
    def __setattr__(self,name,value):
    	object.__setattr__(self,'other',value)
    相反,我们不能使用__dict__技巧在__getattribute__中避免循环:
    def __getattribute__(self,name):
    	x = self.__dict__['other']
    由于获取__dict__属性会再次触发__getattribute__,从而导致递归循环。


    ----------------------------------------------------------------------------------------------------------------------------------------

    演示样例
    这里是与特性和描写叙述符一样的演示样例,只是是用属性操作符重载方法实现的。

    class Person:
        def __init__(self,name):
            self._name  = name
    
        def __getattr__(self,attr):
            if attr == 'name':
                print('fetch...')
                return self._name
            else:
                raise AttributeError(attr)
    
        def __setattr__(self,attr,value):
            if attr == 'name':
                print('change...')
                attr = '_name'
            self.__dict__[attr] = value
    
        def __delattr__(self,attr):
            if attr == 'name':
                print('remove...')
                attr = '_name'
            del self.__dict__[attr]
    
    bob = Person('Bob Smith')
    print(bob.name)
    bob.name = 'Robert Smith'
    print(bob.name)
    del bob.name
    
    print('-'*20)
    sue = Person('Sue Jones')
    print(sue.name)
    #print(Person.name.__doc__) #这里没有与特性等同的使用方法
    注意,__init__构造函数中的属性赋值也触发了__setattr__。这种方法捕获了每次属性赋值,即便是类自身之中的那些。

    执行这段代码,会产生相同的输出:

    fetch...
    Bob Smith
    change...
    fetch...
    Robert Smith
    remove...
    --------------------
    fetch...
    Sue Jones
    还要注意,与特性和描写叙述符不同,这里没有为属性直接声明指定的文档。

    要实现与__getattribute__同样的结果,用以下的代码替换演示样例中的__getattr__,因为它会捕获全部的属性获取,所以必须通过把新的获取传递到超类来避免循环:
    def __getattribute__(self,attr):
    	if attr == 'name':
    		print('fetch...')
    		attr = '_name'
    	return object.__getattribute__(self,attr)
    这些样例尽管与特性和描写叙述符编写的代码一致,可是它并没有强调这些工具的用处。因为它们是通用的。所以__getattr__和__getattribute__在基于托付的代码中更为经常使用。而在仅仅有单个的属性要惯例的情况下,使用特性和描写叙述符更好。

  • 相关阅读:
    poj 3667 Hotel
    codevs 1380 没有上司的舞会
    bzoj 4033 树上染色
    bzoj 1179 [APIO 2009]Atm(APIO水题)
    poj 1741 Tree
    bzoj 3600 没有人的算术
    poj 3233 Matrix Power Series
    hdu 2256 Problem of Precision -矩阵快速幂
    [矩阵十题第七题]vijos 1067 Warcraft III 守望者的烦恼 -矩阵快速幂
    main函数递归
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/7147714.html
Copyright © 2020-2023  润新知