您的位置:首页 > 编程语言 > Python开发

python-setattr,getattr,getattribute

2014-10-06 14:55 423 查看
集合了网上几个帖子,做了测试,整理到一起,供大家参考。

原文地址:
http://www.2cto.com/kf/201209/154470.html
http://blog.sina.com.cn/s/blog_4be6d8870100dtnw.html
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386820042500060e2921830a4adf94fb31bcea8d6f5c000
1.关于getattr( 如果在类中定义__getattr__方法, 该方法会在搜寻属性失败时调用, lookup attribute成功不会调用该方法)

   不重写getattr

    >>> class A():
pass

    >>> a=A()

     >>> a.foo

Traceback (most recent call last):

  File "<pyshell#32>", line 1, in <module>

    a.foo

AttributeError: A instance has no attribute 'foo'

     重写getattr

>>> class A(object): 

            def __getattr__(self, name): 

                     return "I pretend I have an attribute called '%s'"
4000
% name      

>>> a=A()

>>> a.foo

"I pretend I have an attribute called 'foo'"

2.getattr和getattribute

2.1

>>> class TestClass(object): 

    def __init__(self): 

        pass 

    def __getattr__(self,attr_name): 

        print "attribute '%s' is not defined!(in function TestClass.__getattr__)" %attr_name 

        return "not defined!" 

    def __getattribute__(self,attr_name): 

        print "in function '__getattribute__'" 

        attr_val=super(TestClass,self).__getattribute__(attr_name) 

        return attr_val

>>> t=TestClass()

>>> t.a

in function '__getattribute__'

attribute 'a' is not defined!(in function TestClass.__getattr__)

'not defined!'


首先调用了'__getattribute__'方法,因为该方法在属性访问时一定会被调用
接着发现属性a尚未定义, 应当抛出AttributeError异常, 但是发现TestClass中定义了'__getattr__'方法, 该方法类似AttributeError的异常处理方法,
所以python内部机制调用'__getattr__'方法


2.2

>>> t.a=1

>>> 

>>> t.a

in function '__getattribute__'

1


因为'a'已经定义, 故而不再调用'__getattr__'方法, 但'__getattribute__'仍然执行


2.3

>>> t.__getattr__('a')

in function '__getattribute__'

attribute 'a' is not defined!(in function TestClass.__getattr__)

'not defined!'


'__getattr__'同样是'tc'的属性, 同样也会调用'__getattribute__'
注意, 属性'a'并不是不存在, 只不过'__getattr__'方法这样输出而已!


2.4

>>> t.__getattribute__('a')

in function '__getattribute__'

in function '__getattribute__'

1


该方法只需要了解'__getattribute__'本身也是'tc'的属性,
就一目了然了


2.5

__getattribute__方法的无限循环


>>>
class TestClass(object): 

    def __init__(self): 

        pass 

    def __getattribute__(self,attr_name): 

        print "in function '__getattribute__'" 

        attr_val=self.__dict__[attr_name] 

        return attr_val

>>> t=TestClass()

>>> t.a

in function '__getattribute__'

in function '__getattribute__'

in function '__getattribute__'

in function '__getattribute__'


.............................

.............................

根据5测试,
我们知道在访问self.__dict__时, 会调用'__getattribute__', 这样就出现了循环嵌套调用

因此显然不能够在'__getattribute__'方法中调用self的任何属性方法, 那么既然连'__dict__'都不能够访问, 该怎样得到attr_name对应的值呢?

解决方法是使用super函数显示调用父类的'__getattribute__'方法, 这里一个问题: object的__getattribute__是如何实现的?


2.6

使用'__getattribute__'存在无限循环的风险,
但是如果需要在一个较低层次控制属性的访问, 可以使用它


>>>
import random

>>> class TestClass(object): 

    def __init__(self,stu1,*args): #at least one student! 

        self.students=[stu1] 

        self.students.extend(args) 

 

    def __getattribute__(self,attr_name): 

        if attr_name=="random_student": 

            students=super(TestClass,self).__getattribute__("students") 

            pos=random.randint(0,len(students)-1) 

            return students[pos] 

        else: 

            return super(TestClass,self).__getattribute__(attr_name)

>>> t=TestClass('barry',25)

>>> t.students

['barry', 25]

>>> t.__dict__

{'students': ['barry', 25]}

>>> 

>>> t.attr_name

Traceback (most recent call last):

  File "<pyshell#103>", line 1, in <module>

    t.attr_name

  File "<pyshell#95>", line 12, in __getattribute__

    return super(TestClass,self).__getattribute__(attr_name)

AttributeError: 'TestClass' object has no attribute 'attr_name'

>>> t.random_student

'barry'


该例子中,
使用'__getattribute__'方法添加了一个TestClass本不存在的属性'random_student'
通过该属性, 可以随机的访问students中的某一个student


3.

类属性

为在类定义时直接指定的属性(不是在__init__方法中)

class Test: 

    class_attribute1="attr-value"


实例属性

>>> class test():
pass

>>> t=test()

>>> setattr(t,name,'barry')

Traceback (most recent call last):

  File "<pyshell#112>", line 1, in <module>

    setattr(t,name,'barry')

NameError: name 'name' is not defined


>>> setattr(t,'name','barry')

>>> t.__dict__

{'name': 'barry'}

>>> t.name

'barry'

>>> t.age=25

>>> setattr(t,num,2012)

Traceback (most recent call last):

  File "<pyshell#120>", line 1, in <module>

    setattr(t,num,2012)

NameError: name 'num' is not defined


属性的访问顺序

"instance.attr_name"访问实例属性时, 首先在instance.__dict__中查找, 如果找到返回对应值,否则在
instance.__class__.__dict__中查找, 也就是在类属性中查找, 如果找到, 返回对应值, 否则产生attributeError异常


>>> class test():
classdata=1
pass

>>> t=test()

>>> t.name='barry'

>>> t.age=25

>>> t.__dict__

{'age': 25, 'name': 'barry'}

>>> test.__dict__

{'classdata': 1, '__module__': '__main__', '__doc__': None}

>>> 

>>> t.__class__dict__

Traceback (most recent call last):

  File "<pyshell#140>", line 1, in <module>

    t.__class__dict__

AttributeError: test instance has no attribute '__class__dict__'

>>> t.__class__.__dict__ ###就是test.__dict__

{'classdata': 1, '__module__': '__main__', '__doc__': None}

>>> t.classdata    #实例没有classdata,就去类属性中去找

1

>>> test.classdata

1

>>> test.classdata+=1  #类属性加1

>>> test.classdata

2

>>> t.classdata     #实例没有classdata,就去类属性中去找,已经加1,就变成了2

2




4.setattr

通过重写__setattr__控制属性的添加


该方法在setattr(t,attr_name,attr_value)或者t.attr_name=attr_value时被调用,通过重写该方法可以达到控制属性添加的功能


实际上这种控制是不完整的, 因为可以通过t.__dict__[attr_name]=attr_value的方式直接添加


>>> class Test: 

    def __setattr__(self,name,value): 

        if name=="key": 

            self.__dict__[name]=value         

>>> t=Test()

>>> t.key=25

>>> t.name='barry'  #只有key属性能添加

>>> t.__dict__      #只有key属性能添加,所以只有key属性

{'key': 25}




5.重写__call__方法

作用:实现达到()调用的效果  

>>> class test():
def __call__(self,key):
return self.__dict__[key]

>>> t=test()

>>> t.name='barry'

>>> t

<__main__.test instance at 0x01CD6940>

>>> t.name

'barry'

>>> t('name')

'barry'


6.

_变量名



有些时候,你会看到以一个下划线开头的实例变量名,比如
_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
__变量名

   如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线
__
,在Python中,实例的变量名如果以
__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:


__变量名__

在Python中,变量名类似
__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用
__name__
__score__
这样的变量名。

双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问
__name
是因为Python解释器对外把
__name
变量改成了
_Student__name
,所以,仍然可以通过
_Student__name
来访问
__name
变量:
>>> bart._Student__name
'Bart Simpson'


但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把
__name
改成不同的变量名。

总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: