欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python中类属性与实例属性总结

程序员文章站 2022-06-13 10:39:32
*上面的相关讨论 1. 类属性     为在类定义时直接指定的属性(不是在__init__方法中) [pytho...

*上面的相关讨论


1. 类属性

    为在类定义时直接指定的属性(不是在__init__方法中)
[python]
class test: 
    class_attribute1="attr-value" 

2. 实例属性
    在__init__方法中添加的属性, 在其他位置添加也是可以的, 实际是通过setattr内置函数(调用__setattr__)完成, 另外也可以直接修改__dict__属性手动添加
[python] 
t=test() 
setattr(t,attr_name,attr_value) #等价于 t.attr_name=attr_value 
t.__dict__[attr_name]=attr_value  


3. 通过重写__setattr__控制属性的添加
    该方法在setattr(t,attr_name,attr_value)或者t.attr_name=attr_value时被调用,通过重写该方法可以达到控制属性添加的功能
[python]
class test: 
    def __setattr__(self,name,value): 
        if name=="key": 
            self.__dict__[name]=value 

    这样, 在通过setattr(t,attr_name,attr_value)或者t.attr_name=attr_value添加新属性时, 就只能添加名称为key的属性了,
    实际上这种控制是不完整的, 因为可以通过t.__dict__[attr_name]=attr_value的方式直接添加!


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

[python] 
class test: 
    derived_val=1 
    def __init__(self): 
        pass 
 
t1=test() 
 
print "t1.derived_val=",t1.derived_val #t1.derived_val= 1 
print "test.derived_val=",test.derived_val #test.derived_val=1 
 
print "t1.__dict__=",t1.__dict__ #{} 
print "test.__dict__=",test.__dict__ #test.__dict__= {'derived_val': 1,...} 
print "t1.__class__.__dict__=",t1.__class__.__dict__ #test.__dict__= {'derived_val': 1,...} 
 
test.derived_val+=1 #类属性derived_val+1=2 
t1.derived_val+=1 #t1并没有属性derived_val, 根据访问顺序, 将访问类属性derived_val, 结果为 t1.derived_val=test.derived_val+1=3 
 
print "t1.derived_val=",t1.derived_val  #t1.derived_val=3 
print "test.derived_val=",test.derived_val #test.derived_val=2 
 
print "t1.__dict__=",t1.__dict__ #t1.__dict__={'derived_val':3} 
print "test.__dict__=",test.__dict__ #test.__dict__= {'derived_val': 2, ...} 
print "t1.__class__.__dict__=",t1.__class__.__dict__ #test.__dict__= {'derived_val': 2, ...} 

    实际上没有找到属性对应的行为与类的__getattr__和__getattribute__方法有关

5. __getattr__和__getattribute__的区别

     
    如果在类中定义__getattr__方法, 该方法会在搜寻属性失败时调用, lookup attribute成功不会调用该方法

[python] v
class a(object): 
            def __getattr__(self, name): 
                     return "i pretend i have an attribute called '%s'" % name 
 
a = a() 
print a.foo # there's no attribute 'foo', but no attributeerror currs, prints "i pretend i have an attribute called 'foo'" 

    如果在类中定义__getattribute__方法, 该方法在获取instance的任何属性(方法也是属性!)时调用, 因为__getattribute__需要
    调用super类(见"__getattribute__方法的无限循环"), 故只能使用在new-style类中(这个逻辑是否正确, 希望指出)
[python] 
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 
#testclass类定义了两个方法__getattr__ 和__getattribute__方法, 下面几个例子说明两者区别 
tc=testclass() #tc! 
tc.a  #a 
tc.a=1 #add attribute 'a' to tc 
tc.a #b 
tc.__getattr__('a') #c 
tc.__getattribute__('a') #d 

************************************************************分析*************************************************************
    a. 访问属性'a',得到结果为:
   
        in function '__getattribute__'
        attribute 'a' is not defined!(in function testclass.__getattr__)
        'not defined!'
       
        首先调用了'__getattribute__'方法,因为该方法在属性访问时一定会被调用
        接着发现属性a尚未定义, 应当抛出attributeerror异常, 但是发现testclass中定义了'__getattr__'方法, 该方法
        类似attributeerror的异常处理方法, 所以python内部机制调用'__getattr__'方法
    b. 这次访问属性'a', 得到结果为:
   
        in function '__getattribute__'
        1
       
        因为'a'已经定义, 故而不再调用'__getattr__'方法, 但'__getattribute__'仍然执行
    c. 显示的调用方法'__getattr__', 得到结果:
       
        in function '__getattribute__'
        attribute 'a' is not defined!(in function testclass.__getattr__)
        'not defined!'

        '__getattr__'同样是'tc'的属性, 同样也会调用'__getattribute__'
        注意, 属性'a'并不是不存在, 只不过'__getattr__'方法这样输出而已!
    d. 调用'_getattribute__', 得到结果:
       
        in function '__getattribute__'
        in function '__getattribute__'
        1
       
        该方法只需要了解'__getattribute__'本身也是'tc'的属性, 就一目了然了
************************************************************************************************************************************


6. __getattribute__方法的无限循环

[python]
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 

    调用'__getattribute__'方法会陷入循环嵌套调用
    根据5中测试, 我们知道在访问self.__dict__时, 会调用'__getattribute__', 这样就出现了循环嵌套调用
    因此显然不能够在'__getattribute__'方法中调用self的任何属性方法, 那么既然连'__dict__'都
    不能够访问, 该怎样得到attr_name对应的值那?
    解决方法是使用super函数显示调用父类的'__getattribute__'方法, 这里一个问题: object的__getattribute__是如何实现的?


7. 危险的__getattribute__的使用时机
        使用'__getattribute__'存在无限循环的风险, 但是如果需要在一个较低层次控制属性的访问, 可以使用它
        下面是一个例子

[python] 
import random 
#i think this is a good example to show the power you can get from overriding '__getattribute__' 
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) 

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