关于Python的实例变量与类变量,先来看一段可能颠覆世界观的例子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__是一个字典,键是属性名,值为属性值。spa
Python的实例有本身的__dict__,它对应的类也有本身的__dict__ (可是有些特殊的对象是没有__dict__属性的,这里不作讨论)code
若是在程序的第15行处加上两句打印语句,打印类和实例1的__dict__属性,将会输出以下:对象
1 print cls.__dict__ 2 print ins1.__dict__
###########输出##########
{'clsvar': 1, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x101bbc398>}blog
{'insvar': 2}it
当打印类的__dict__属性时,列出了类cls所包含的属性,包括一些类内置属性和类变量clsvar以及构造方法__init__io
而实例变量则包含在实例对象ins1的__dict__属性中,一个对象的属性查找顺序遵循首先查找实例对象本身,而后是类,接着是类的父类。function
如今能够解释开头代码中的神秘现象了,再强调一遍,一个对象的属性查找顺序遵循首先查找实例对象本身,而后是类,接着是类的父类。class
在第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)