1. 程式人生 > >python中類屬性與例項屬性總結

python中類屬性與例項屬性總結

stackoverflow上面的相關討論

1. 類屬性

    為在類定義時直接指定的屬性(不是在__init__方法中)

    class Test:
        class_attribute1="attr-value"

2. 例項屬性

    在__init__方法中新增的屬性, 在其他位置新增也是可以的, 實際是通過setattr內建函式(呼叫__setattr__)完成, 另外也可以直接修改__dict__屬性手動新增

    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時被呼叫,通過重寫該方法可以達到控制屬性新增的功能
    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異常

    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__的區別

		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類中
(這個邏輯是否正確, 希望指出)
		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__方法的無限迴圈

		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__'存在無限迴圈的風險, 但是如果需要在一個較低層次控制屬性的訪問, 可以使用它
        下面是一個例子

		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 你的反饋就是博主進步的最大動力