概述
在Python的类中,有着类属性、实例属性,静态方法、类方法、实例方法的区别。到底有什么不一样呢?接下来我们就一探究竟。
类属性、实例属性来看下简单的Student类的例子
classStudent(object):#类属性school=井冈山大学def__init__(self,name):#实例属性self.name=name
其中school是Student类的类属性,name则是实例属性。
在ipython中测试一下如何访问其属性
In[5]:stu1=Student(hui)In[6]:stu2=Student(wang)In[7]:stu3=Student(zack)In[8]:stu1.name,Student.schoolOut[8]:(hui,井冈山大学)In[9]:stu2.name,Student.schoolOut[9]:(wang,井冈山大学)In[10]:stu3.name,Student.schoolOut[10]:(zack,井冈山大学)#看看实例对象能不能访问类属性,类对象能不能访问实例属性In[11]:stu1.name,stu1.schoolOut[11]:(hui,井冈山大学)In[12]:Student.name,stu1.school---------------------------------------------------------------------------AttributeErrorTraceback(mostrecentcalllast)ipython-input-12-bebinmodule----1Student.name,stu1.schoolAttributeError:typeobjectStudenthasnoattributename
经过测试可以发现实例属性需要通过实例对象来访问,类属性通过类来访问,但在测验中stu1.school实例对象也能访问类属性,为什么呢?
其实,实例对象也是间接的通过类对象进行访问的,在每一个实例对象中都有一个__class__的属性,其指向的就是创建实例对象的类对象。stu1.__class__的指向就是Student类对象。然后实例对象访问属性的规则是先访问实例属性,然后再根据实例对象的__class__来访问类属性。如果都没有找到则报错。
In[15]:dir(stu1)Out[15]:[__class__,__delattr__,__dict__,__dir__,....name,school]In[16]:stu1.__class__Out[16]:__main__.StudentIn[17]:stu1.__class__.schoolOut[17]:井冈山大学In[18]:id(Student)Out[18]:4In[19]:id(stu1.__class__)Out[19]:4
可以看出Student,stu1.__class__的id()都一样,说明其内存地址都一样。因此实例属性可以通过__class__访问类属性。
存储方式如下图
类对象派生实例对象由上图可以看出:
类属性在内存中只保存一份实例属性在每个对象中都要保存一份还是以上面的例子在ipython中对类属性的修改进行测验
In[24]:classStudent(object):...:...:#类属性...:school=井冈山大学...:...:def__init__(self,name):...:...:#实例属性...:self.name=name...:In[25]:stu1=Student(hui)In[26]:stu2=Student(jack)In[27]:stu1.name,stu1.schoolOut[27]:(hui,井冈山大学)In[28]:stu2.name,stu2.schoolOut[28]:(jack,井冈山大学)#通过类对象进行修改In[29]:Student.school=清华大学In[30]:stu2.name,stu2.schoolOut[30]:(jack,清华大学)In[31]:stu1.name,stu1.schoolOut[31]:(hui,清华大学)#通过实例对象进行修改IIn[33]:stu1.school=北京大学In[34]:stu1.name,stu1.schoolOut[34]:(hui,北京大学)In[35]:stu2.name,stu2.schoolOut[35]:(jack,清华大学)In[36]:Student.schoolOut[36]:清华大学In[37]:stu1.__class__.schoolOut[37]:清华大学In[39]:id(stu2.school)Out[39]:8In[40]:id(Student.school)Out[40]:8In[41]:id(stu1.school)Out[41]:2#通过实例对象的__class__属性修改IIn[42]:stu2.__class__.school=井冈山大学In[43]:stu1.name,stu1.schoolOut[43]:(hui,北京大学)In[44]:stu2.name,stu2.schoolOut[44]:(jack,井冈山大学)In[45]:Student.schoolOut[45]:井冈山大学
说明:实例对象.类属性=xxx并没有修改到其类属性,而是在实例对象中创建了一个与类属性同名的实例属性。因此修改类属性,应该使用类对象进行修改。再外界最好不要使用实例对象.新属性=xxx,动态创建实例属性。
使用场景到底是用类属性,还是实例属性?
如果每个实例对象需要具有相同值的属性,那么就使用类属性,用一份既可。
classProvince(object):#类属性country=中国def__init__(self,name):#实例属性self.name=namep1=Province(江西省)p2=Province(四川省)实例方法、静态方法和类方法
类中方法包括:实例方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
实例方法:由对象调用,至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self。类方法:由类调用,至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls。静态方法:由类调用,无默认参数。classFoo(object):foo=Foodef__init__(self,name):self.name=namedefinstance_func(self):print(self.name)print(self.foo)print(实例方法)
classmethoddefclass_func1(cls):print(cls.foo)print(类方法1)classmethoddefclass_func2(cls):print(cls.name)print(类方法二)staticmethoddefstatic_func():print(静态方法)其中
classmethod是装饰器,说明这是类方法,staticmethod则说明是静态方法。关于装饰器的内容这里就不在赘述了。在ipython中测验一下各方法
#实例对象调用In[71]:f=Foo(hui)In[72]:f.instance_func()huiFoo实例方法In[73]:f.class_func1()Foo类方法1In[74]:f.class_func2()---------------------------------------------------------------------------AttributeErrorTraceback(mostrecentcalllast)ipython-input-74-7de9e60ecinmodule----1f.class_func2()ipython-input-60-7fca96ainclass_func2(cls)18
classmethod19defclass_func2(cls):---20print(cls.name)21print(类方法二)22AttributeError:typeobjectFoohasnoattributenameIn[75]:f.static_func()静态方法#类对象自身调用In[76]:Foo.instance_func()---------------------------------------------------------------------------TypeErrorTraceback(mostrecentcalllast)ipython-input-76-efcbinmodule----1Foo.instance_func()TypeError:instance_func()missing1requiredpositionalargument:selfIn[77]:Foo.class_func1()Foo类方法1In[78]:Foo.static_func()静态方法可以发现实例对象三种方法都可以调用,但cls类对象不能访问实例属性。类对象不能直接调用实例方法,类、静态方法可以。
self与cls的区别self指的是类实例对象本身(注意:不是类本身)。cls指的是类对象本身self可以访问到类属性、实例属性,cls只能访问类属性。其中self,cls只是代指实例对象和类对象,因此换成其他变量也可以,但是约定成俗(为了和其他编程语言统一,减少理解难度),不要搞另类,大家会不明白的。
使用场景需要操作类属性的定义成类方法。
需要操作实例属性的定义成实例方法。
既不需要操作类属性,也不需要操作实例属性就定义成静态方法。