python-setattr,getattr,getattribute

集合了网上几个帖子,做了测试,整理到一起,供大家参考。

原文地址:

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'" % 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本身没有任何机制阻止你干坏事,一切全靠自觉。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值