關於Python的實例變量與類變量,先來看一段可能顛覆世界觀的例子
1 #!/usr/bin/env python 2 # -*- coding: utf_8 -*- 3 # Date: 2016年10月10日 4 # Author:蔚藍行 5 6 #首先創建一個類cls,這個類中包含一個值為1的類變量clsvar,一個值為2的實例變量insvar, 7 class cls: 8 clsvar = 1 9 def __init__(self): 10 self.insvar = 2 11 12 #創建類的實例ins1和ins2 13 ins1 = cls() 14 ins2 = cls() 15 16 #用實例1為類變量重新賦值並打印 17 print '#'*10 18 ins1.clsvar = 20 19 print cls.clsvar #輸出結果為1 20 print ins1.clsvar #輸出結果為20 21 print ins2.clsvar #輸出結果為1 22 23 #用類名為類變量重新賦值並打印 24 print '#'*10 25 cls.clsvar = 10 26 print cls.clsvar #輸出結果為10 27 print ins1.clsvar #輸出結果為20 28 print ins2.clsvar #輸出結果為10 29 30 #這次直接給實例1沒有在類中定義的變量賦值 31 print '#'*10 32 ins1.x = 11 33 print ins1.x #輸出結果為11 34 35 #然后再用類名給類中沒有定義的變量賦值 36 print '#'*10 37 cls.m = 21 38 print cls.m #輸出結果為21 39 40 #再創建一個實例ins3,然后打印一下ins3的變量 41 print '#'*10 42 ins3 = cls() 43 print ins3.insvar #輸出結果為2 44 print ins3.clsvar #輸出結果為10 45 print ins3.m #輸出結果為21 46 print ins3.x #報錯AttributeError: cls instance has no attribute 'x'
看上去怪怪的,為什么會出現這種結果呢?這就要了解python中的__dict__屬性了,__dict__是一個字典,鍵是屬性名,值為屬性值。
Python的實例有自己的__dict__,它對應的類也有自己的__dict__ (但是有些特殊的對象是沒有__dict__屬性的,這里不做討論)
如果在程序的第15行處加上兩句打印語句,打印類和實例1的__dict__屬性,將會輸出如下:
1 print cls.__dict__ 2 print ins1.__dict__
###########輸出##########
{'clsvar': 1, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x101bbc398>}
{'insvar': 2}
當打印類的__dict__屬性時,列出了類cls所包含的屬性,包括一些類內置屬性和類變量clsvar以及構造方法__init__
而實例變量則包含在實例對象ins1的__dict__屬性中,一個對象的屬性查找順序遵循首先查找實例對象自己,然后是類,接着是類的父類。
現在可以解釋開頭代碼中的神秘現象了,再強調一遍,一個對象的屬性查找順序遵循首先查找實例對象自己,然后是類,接着是類的父類。
在第18行 ins1.clsvar = 20這句后面我們打印一下實例和類的__dict__屬性
ins1.clsvar = 20 print ins1.__dict__ print cls.__dict__
###########輸出##########
{'insvar': 2, 'clsvar': 20}
{'clsvar': 1, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x10c768398>}
可以看到,ins1.clsvar = 20這句只是在實例ins1的__dict__屬性中增加了'clsvar': 20這一鍵值對,而類中的clsvar的值並沒有改變,重要的事情說三遍:一個對象的屬性查找順序遵循首先查找實例對象自己,然后是類,接着是類的父類。當ins1在自己的__dict__中查找到了clsvar,就不會再向上查找,所以輸出了值20。但是此時,cls類中的clsvar的值仍然為1。
但是當在第25行通過類名改變了類的clsvar之后,類的__dict__中的clsvar就被改變成10了,這時打印ins1的clsvar,由於之前第18行的原因,ins1在自己的__dict__中找到了clsvar,就輸出了它自己的值20,而ins2自己的__dict__中沒有clsvar,就向上查找類的__dict__,並找到了類的clsvar,值為10
第46行的ins3一直向上查找x屬性都沒有找到,就會拋出AttributeError
像32行和37行這樣給類或實例設置屬性,其實就是在他們各自的__dict__中添加了該屬性,相信現在其他的神秘現象大家也可以自己解釋了。
最后附上一個將字典轉換成對象的小技巧,如果我們有一個字典如下:
bokeyuan={"b":1, "o":2, "k":3, "e":4, "y":5, "u":6, "a":7, "n":8, }
現在想將其轉換為一個對象,通常會這樣寫:
1 class Dict2Obj: 2 def __init__(self,bokeyuan): 3 self.b = bokeyuan['b'] 4 self.o = bokeyuan['o'] 5 self.k = bokeyuan['k'] 6 self.e = bokeyuan['e'] 7 self.y = bokeyuan['y'] 8 self.u = bokeyuan['u'] 9 self.a = bokeyuan['a'] 10 self.n = bokeyuan['n']
但是在了解了__dict__屬性之后可以這樣寫:
1 class Dict2Obj: 2 def __init__(self,bokeyuan): 3 self.__dict__.update(bokeyuan)