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

311人阅读 评论(0) 收藏 举报

stackoverflow上面的相关讨论

http://stackoverflow.com/questions/2923579/python-class-attribute

http://stackoverflow.com/questions/1944625/what-is-the-relationship-between-getattr-and-getattr


1. 类属性

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

  1. class Test:
  2. class_attribute1="attr-value"
    class Test:
        class_attribute1="attr-value"

2. 实例属性

在__init__方法中添加的属性, 在其他位置添加也是可以的, 实际是通过setattr内置函数(调用__setattr__)完成, 另外也可以直接修改__dict__属性手动添加

  1. t=Test()
  2. setattr(t,attr_name,attr_value) #等价于 t.attr_name=attr_value
  3. t.__dict__[attr_name]=attr_value
    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时被调用,通过重写该方法可以达到控制属性添加的功能
  1. class Test:
  2. def __setattr__(self,name,value):
  3. if name=="key":
  4. self.__dict__[name]=value
    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异常

  1. class Test:
  2. derived_val=1
  3. def __init__(self):
  4. pass
  5. t1=Test()
  6. print "t1.derived_val=",t1.derived_val #t1.derived_val= 1
  7. print "Test.derived_val=",Test.derived_val #Test.derived_val=1
  8. print "t1.__dict__=",t1.__dict__ #{}
  9. print "Test.__dict__=",Test.__dict__ #Test.__dict__= {'derived_val': 1,...}
  10. print "t1.__class__.__dict__=",t1.__class__.__dict__ #Test.__dict__= {'derived_val': 1,...}
  11. Test.derived_val+=1 #类属性derived_val+1=2
  12. t1.derived_val+=1 #t1并没有属性derived_val, 根据访问顺序, 将访问类属性derived_val, 结果为 t1.derived_val=Test.derived_val+1=3
  13. print "t1.derived_val=",t1.derived_val #t1.derived_val=3
  14. print "Test.derived_val=",Test.derived_val #Test.derived_val=2
  15. print "t1.__dict__=",t1.__dict__ #t1.__dict__={'derived_val':3}
  16. print "Test.__dict__=",Test.__dict__ #Test.__dict__= {'derived_val': 2, ...}
  17. print "t1.__class__.__dict__=",t1.__class__.__dict__ #Test.__dict__= {'derived_val': 2, ...}
    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__的区别

另请参考: http://stackoverflow.com/questions/4295678/understanding-the-difference-between-getattr-and-getattribute

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

  1. class A(object):
  2. def __getattr__(self, name):
  3. return "I pretend I have an attribute called '%s'" % name
  4. a = A()
  5. print a.foo # there's no attribute 'foo', but no AttributeError currs, prints "I pretend I have an attribute called 'foo'"
		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类中
( 这个逻辑是否正确, 希望指出)

  1. class TestClass(object):
  2. def __init__(self):
  3. pass
  4. def __getattr__(self,attr_name):
  5. print "attribute '%s' is not defined!(in function TestClass.__getattr__)" %attr_name
  6. return "not defined!"
  7. def __getattribute__(self,attr_name):
  8. print "in function '__getattribute__'"
  9. attr_val=super(TestClass,self).__getattribute__(attr_name)
  10. return attr_val
  11. #TestClass类定义了两个方法__getattr__ 和__getattribute__方法, 下面几个例子说明两者区别
  12. tc=TestClass() #tc!
  13. tc.a #a
  14. tc.a=1 #add attribute 'a' to tc
  15. tc.a #b
  16. tc.__getattr__('a') #c
  17. tc.__getattribute__('a') #d
		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__方法的无限循环

  1. class TestClass(object):
  2. def __init__(self):
  3. pass
  4. def __getattribute__(self,attr_name):
  5. print "in function '__getattribute__'"
  6. attr_val=self.__dict__[attr_name]
  7. return attr_val
		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__'存在无限循环的风险, 但是如果需要在一个较低层次控制属性的访问, 可以使用它
下面是一个例子

  1. import random
  2. #I think this is a good example to show the power you can get from overriding '__getattribute__'
  3. class TestClass(object):
  4. def __init__(self,stu1,*args): #at least one student!
  5. self.students=[stu1]
  6. self.students.extend(args)
  7. def __getattribute__(self,attr_name):
  8. if attr_name=="random_student":
  9. students=super(TestClass,self).__getattribute__("students")
  10. pos=random.randint(0,len(students)-1)
  11. return students[pos]
  12. else:
  13. return super(TestClass,self).__getattribute__(attr_name)
		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


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值