类属性和实例属性区别
类是模板,而实例则是根据类创建的对象。
绑定在一个实例上的属性不会影响其他实例,但是,类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个。即实例属性每个实例各自拥有,互相独立,而类属性有且只有一份,所有实例共同拥有。
1 | # 定义类属性可以直接在class中定义 |
类属性和实例属性冲突
修改类属性会导致所有实例访问到的类属性全部都受影响。下面测试在实例对象上修改类属性,
1 | class Person(object): |
在设置了p1.address = 'China'后,p1访问address确实变成了'China',但是,Person.address和p2.address仍然是'Earch'。原因是p1.address = 'China'并没有改变Person的address,而是给p1这个实例绑定了实例属性address,对p1来说,它有一个实例属性address(值是'China'),而它所属的类Person也有一个类属性address,所以访问p1.address时,优先查找实例属性,返回'China';访问p2.address时,p2没有实例属性address,但是有类属性address,因此返回'Earth'。
可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。
1 | # 当p1的address实例属性删除后,p1.address就又返回类属性的值'Earth'了: |
实例方法
私有属性无法从类外部访问,但是从类的内部可以访问。除了可以定义实例的属性外,还可以定义实例的方法。
实例方法就是在类中定义的函数,它的第一个参数永远是self,指向调用该方法的实例本身,其他参数和一个普通函数是完全一样的,
1 | class Person(object): |
如上,get_name(self)就是一个实例方法,它的第一个参数是self。__init__(self, name)其实也可看做是一个特殊的实例方法。
调用实例方法必须通过实例调用,
1 | p1 = Person('Bob') |
在实例方法内部,可以访问所有实例属性,这样,如果外部需要访问私有属性,可以通过方法调用获得,这种数据封装的形式除了能保护内部数据一致性外,还可以简化外部调用的难度。
方法也是属性
在class中定义的实例方法其实也是属性,它实际上是一个函数对象,
1 | class Person(object): |
如上,p1.get_grade返回的是一个函数对象,p1.get_grade()才是方法调用。
因为方法也是一个属性,所以,它也可以动态地添加到实例上,只是需要用types.MethodType()把一个函数变为一个实例方法,
1 | import types |
给一个实例动态添加方法并不常见,直接在class中定义要更直观。
类方法
在class中定义的全部是实例方法,实例方法第一个参数self是实例本身。在class中定义类方法,
1 | class Person(object): |
通过标记一个@classmethod,该方法将绑定到Person类,而非类的实例上。类方法的第一个参数将传入类本身,通常将参数名命名为cls,上面的cls.count实际上相当于Person.count。
因为是在类上调用,而非实例上调用,因此类方法无法获得任何实例变量,只能获得类的引用。