也说python的类--基于python3.5
在面向对象的语言中,除了方法、对象,剩下的一大重点就是类了,从意义上来讲,类就是对具有相同行为对象的归纳。当一个或多个对象有相同属性、方法等共同特征的时候,我们就可以把它归纳到同一个类当中。在使用上来讲,类的存在,就是为了方便管理对象。python中定义一个类的代码如下:
class Simple_class(object): #定义一个名为Simple_class的类,python3以后默认object做为类的基类,这里不讨论新式和旧式类的区别 height = 1.58#定义一个类变量 def __init__(self,name,age,):#重写类的初始化, #使其在初始化的时候需要传入name,age两个参数。self为类方法的默认参数 self.name = name #定义两个对象变量 self.age = age def Print_details(self):#定义一个名为Print_details的对象方法 print("Print_details方法调用:name is %s,age is %d"%(self.name,self.age)) print("未初始化访问类变量:height = ",Simple_class.height)# 访问类变量 try: print(Simple_class.name) #访问实例变量 因为会报错,为了程序代码执行 给了一个异常处理 except AttributeError as error: print("未初始化之前访问实例变量的异常信息:",error) obj = Simple_class("object",115) #生成一个类的实例对象 obj.height = 1.88 print("初始化之后赋值给实例的类变量 height = ",obj.height) print("初始化之后的原始类变量:height = ",Simple_class.height) print("初始化之后访问实例变量:name is ",obj.name)#访问实例变量 obj.Print_details()#调用对象方法 # /////输出结果如下 /////// /Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/penglong/Documents/python/s10/day4/blog.py 未初始化访问类变量:height = 1.58 未初始化之前访问实例变量的异常信息: type object 'Simple_class' has no attribute 'name' 初始化之后赋值给实例的类变量 height = 1.88 初始化之后的原始类变量:height = 1.58 初始化之后访问实例变量:name is object Print_details方法调用:name is object,age is 115
以上,我们定义了一个类,类中包括一个重写的初始化方法,一个普通类方法。一个类变量,两个实例变量。从输出内容上我们来分析一下类变量和实例变量的区别,在实例化对象之前,是可以直接访问类变量并且取出赋值给别的变量,而实例变量则是在实例化之后,只能通过实例对象访问的变量,如果在有具体的实例对象之前直接通过类去访问,则程序会报错。这是二者的区别。从使用意义上来讲,个人认为,类变量更像是实例的默认变量,他是每一个实例对象带有的默认变量且有值,这个值在实例化对象之后可以进行修改,不影响其他实例对象。而实例变量则是各个实例的特有变量,必须要进行赋值之后才能使用。再说初始化方法和我们定义的方法,初始化方法属于类方法,可以直接调用。而对象方法需要通过实例调用。
在上一段代码中有一个关于基类的概念,实际上也就是继承。在编写代码的时候,我们想对一个类进行一些额外的扩充,而又不影响它本身的状态,那就可以写一个子类来满足,这里先从单重继承说起,代码如下:
class father_class(object): #定义一个名为father_class的类,他既是子类的父类,又是子类的子类的基类 name = "父类" #定义一个类变量 def log(self): #定义一个log方法 输出类名,这里不用self.__class取,是为了在子类调用时self会指向子类自身,不方便日志分辨。 print("father_class") def public_func(self):#定义一个public_func方法,输出此方法是否被子类修改过 print("未修改") class son_class(father_class): #定义一个子类,直接继承父类 son_name = "子类"#定义一个类变量 def son_log(self): #定义自身的log print("son_class") def public_func(self):#修改父类提供的公共方法 print("子类修改父类方法") class grandson_class(son_class): #定义一个名为grandson_class的类继承自son_class由于son_calss也是别人的子类 #所以son_class的父类是本类的基类 grandson_name = "子类的子类" def grandson_log(self):#定义自身log print("grandson_class") def public_func(self):#对基类方法进行修改 print("子类修改基类方法") ########父类的实例对象########## father = father_class() #生成一个父类的实例对象 name = father.name #获取类变量的值并打印,类变量本身不用实例化也可以获取,但下文需要实例调用方法,所以就先生成了 print("父类访问自身类变量:",name) father.log()#父类调用自身的方法 father.public_func() #######子类的实例对象########### son = son_class()#实例化一个子类对象 name = son.name #子类去获取父类的类变量并这打印。输出结果无误,说明子类可以继承父类所有的类变量。 #同理也可以继承父类所有实例变量,代码未举例,但原理一样 print("子类访问父类变量:",name) son.log()#子类调用父类的方法,输出结果无误,说明子类可以继承父类所有的方法 name = son.son_name#子类访问自身类变量的值并打印,结果无误,说明子类可以增加类变量 print("子类访问自己的类变量:",name)# son.son_log()#子类调用自己的log方法,说明子类可以扩充自己的方法 son.public_func()#子类调用父类的方法,但输出结果不同 ,因为我们在子类中进行过修改,说明子类可以修改父类方法 #########子类的子类的实例对象 grandson = grandson_class()#生成一个子类的子类的实例对象 name = grandson.name#子类直接去访问父类的父类的类变量并打印, #结果无误,说明,我们除了继承父类的变量,还继承所有父类的父类,链条式。 print("子类访问基类变量:",name) grandson.log()#子类调用父类的父类的方法,同理可得,子类也继承父类的父类的所有方法 grandson.public_func()#子类可以直接修改父类的父类也就是基类的方法 ########输出结果如下############### 父类访问自身类变量: 父类 father_class 未修改 子类访问父类变量: 父类 father_class 子类访问自己的类变量: 子类 son_class 子类修改父类方法 子类访问基类变量: 父类 father_class 子类修改基类方法
以上代码是一个简单的单继承概括,从代码中,可以看到,当一个类继承自另一个类,那么它将拥有父类的一切数据,包括父类变量,父类的实例变量,以及父类方法,这个方法包括类方法和实例方法。同时,子类的作用就是对父类的扩充,也就是说它可以对父类的所有数据增加、更改。python是一个多继承语言,有时候我们想同时拥有两个类的方法,那直接继承两个类就可以。简单构造代码如下:
class A(object): def a(self): pass class B(object): def b(self): pass class C(A,B): pass c_test = C() c_test.a() c_test.b()
从上面代码中可以看出,C类继承自A,B两个类,所以它既可以调用A的方法,又可以调用B的方法。(ios只能通过协议实现。。差评!!!),多继承的特性和单继承差不多是一样的,但值得注意的是,多继承会涉及到方法重复。那么,这里衍生出一个问题。普通继承与super继承。首先来看一段代码:
class A(object):#这么简单的代码。。我就不解释了 def __init__(self): print("A") class B(object): def __init__(self): print("B") class C(A): def __init__(self): print("C") A.__init__(self) class D(A): def __init__(self): print("D") A.__init__(self) class E(B,C): def __init__(self): print("E") B.__init__(self) C.__init__(self) class F(D,E): def __init__(self): print("F") E.__init__(self) D.__init__(self) a = E() print("---------") b = F() ######看一下输出记录,为了省地儿,我合成了两行行######## # 初始化E类得到的输出结果 E B C A #--------- # 初始化F类得到的输出结果 F E B C A D A
分析一下上面的代码,A,B都直接继承于基类,分别有自己的初始化方法,在初始化的时候输出自己的类名。C,D皆继承自A,改写它们的初始化方法,使其在初始化的时候不仅输出自己类名,同时调用一下自己父类的初始化方法即同时输出自己的父类名.E,F两个类为多重继承。来查看一下初始化E类的结果,没有异常,它初始化了自己,也调用了父类的初始化方法,同时调用了父类的父类的初始化方法。再看一下F类的初始化结果。问题来了。它输出了两个A。由此可以分析出。它调用了两次A类的初始化方法,这是代码中极为不愿意看到的。让我们来分析一下代码,看这个问题是如何出现的。首先F继承自D,E两个类。而D继承自A,E继承自B,C。再往上分析,B继承自基类。而C继承自A。问题在这,每继承一个类,我们都对其进行了初始化方法调用。也就是说在F中我们调用了两次A的初始化方法,所以输出了两次。在实际运用中,我们也会遇到这个问题。那就是在多重继承中,常常由于类的关系复杂而使某些父类方法被其子类或者其子类的子类或者在子类重复调用。所以,我们要用到一个关键字super来解决这个问题,代码如下:
class A(object):#代码都差不多。。这个也不解释了。。 def __init__(self): print("A") super(A,self).__init__() class B(object): def __init__(self): print("B") super(B, self).__init__() class C(A): def __init__(self): print("C") super(C, self).__init__() class D(A): def __init__(self): print("D") super(D, self).__init__() class E(B,C): def __init__(self): print("E") super(E, self).__init__() class F(D,E): def __init__(self): print("F") super(F, self).__init__() a = E() print("---------") b = F() ######看一下输出记录,为了省地儿,我合成了两行行######## # 初始化E类得到的输出结果 E B C A #--------- # 初始化F类得到的输出结果 F D E B C A
如上,对比一下两份代码,不难看出super的作用,首先它自动查找父类的初始化方法并调用,同时保证只被调用一次,而且严格按照类继承路线走,关于最后一点可以对比一下两次代码的输出顺序,这个顺序涉及到python的多继承顺序算法,别管它了。除了初始化方法,对于其他方法,super也有同样的作用,需要注意的是,super只针对新式类。在单继承中,super大概被用于偷懒扩充父类构造方法。但是!混用super类和普通继承类比较危险,所以单继承个人很少使用。
以上,继承的问题基本谈完,最后说一下装饰器在类方法中的使用代码如下:
class test(object):#定义一个类。。不要管名字了 name = "test"#类变量 def __init__(self,age):#初始方法,传个参数 self.age = age#定义一个实例变量,把传进来的参数直接赋值给它 def say(self):# 未加装饰器的普通方法,输出个test,ps:只能实例化之后通过实例调用 print("test") @staticmethod#加装饰器@staticmethod之后的方法, #即静态方法 不无需实例化调用 ,无法传参数。当一个类只有静态方法的时候,从意义上讲就单纯是一个类的工具包 def say2(): print("say hi 2") @classmethod # 类方法 ,不需要实例化即可调用,可以使用类参数,无法使用实例参数,这里没有传个参数做例子。。抱歉 def say3(self): print("say hi 3")。 @property #加@property的方法 #可以返回数据,使用意义上来讲,就是把经过类方法处理后的数据传递出去 def say4(self): # print("this is 4") return "test"
以上是python中类的一些简单注意事项,做为面向对象的一大重点,类这一概念还有很多值得深究的东西。例如参数类型等等,这些都需要自己去慢慢探究。