python学习笔记(七):面向对象编程、类
一、面向对象编程
面向对象--Object Oriented Programming,简称oop,是一种程序设计思想。在说面向对象之前,先说一下什么是编程范式,编程范式你按照什么方式来去编程,去实现一个功能。举个例子,你要做饭,可以用电磁炉,也可以用煤气灶。不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路,两种最重要的编程范式分别是面向过程编程和面向对象编程。
提到面向对象,就不得不提到另一种编程思想,面向过程;什么是面向过程呢,面向过程的思想是把一个项目、一件事情按照一定的顺序,从头到尾一步一步地做下去,先做什么,后做什么,一直到结束。这种思想比较好理解,其实这也是一个人做事的方法,我们之前编程的思想也都是使用这种思想。这种编程思想,只要前面有一个步骤变了,那么后面的就也要变,后面维护起来比较麻烦,这样的编程思想,我们在写一些简单的小程序、只执行一次的脚本时可以使用。而面向对象呢,面向对象的思想是把一个项目、一件事情分成更小的项目,或者说分成一个个更小的部分,每一部分负责什么方面的功能,最后再由这些部分组合而成为一个整体。这种思想比较适合多人的分工合作,就像一个大的机关,分各个部门,每个部门分别负责某样职能,各个部门可以充分发挥自己的特色,只要符合一定前提就行了。
举个例子:比如刚才说的一个大的机关,要做某一个项目,从面向过程的思想来说,应该是这样分析的,先怎么样,再怎么样,最后怎么样。第一样应该如何完成,第二样应该如何完成等等。等到每一步骤都完成,项目也就完成了。而面向对象的思想则应该是这样想的,这个项目是由几个部分组成的,我们就做好分工,成立一个部门来做一个部分的功能,另一个部门来做另一个部分。各个部门可以不用理解其他部门的事,只要完成自己那一部分的事情就OK了。
二、面向对象的特性
类:class
类,对比现实世界来说就是一个种类,一个模型。
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。
在类中定义了这些对象的都具备的属性(variables(data))、共同的方法。
对象:object
对象,也就是指模型造出来的具体的东西。
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同。
实例化:
初始化一个类,造了一个对象。把一个类变成一个具体的对象的过程,叫做实例化。
封装:
把一些功能的实现细节不对外暴露,类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法。
比如说造的一个人,你把他身体内部的什么心肝脾肺肾都封装起来了,其他人都看不到,你直接找这个人。
继承:
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承。比如说你继承了你父亲的姓。
python3中多继承都是广度优先,python2中经典类的多继承是深度优先,新式类的多继承是按照广度优先的。
继承是为了代码的重用
多态:
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
一种接口,多种实现。
三、面向对象的好处
对于编程语言的初学者来讲,OOP不是一个很容易理解的编程方式,大家虽然都按老师讲的都知道OOP的三大特性是继承、封装、多态,并且大家也都知道了如何定义类、方法等面向对象的常用语法,但是一到真正写程序的时候,还是很多人喜欢用函数式编程来写代码,特别是初学者,很容易陷入一个窘境就是“我知道面向对象,我也会写类,但我依然没发现在使用了面向对象后,对我们的程序开发效率或其它方面带来什么好处,因为我使用函数编程就可以减少重复代码并做到程序可扩展了,为啥子还用面向对象?”对于此,我个人觉得原因应该还是因为你没有充分了解到面向对象能带来的好处。
无论用什么形式来编程,我们都要明确记住以下原则:
写重复代码是非常不好的低级行为
你写的代码需要经常变更
开发正规的程序跟那种写个运行一次就扔了的小脚本一个很大不同就是,你的代码总是需要不断的更改,不是修改bug就是添加新功能等,所以为了日后方便程序的修改及扩展,你写的代码一定要遵循易读、易改的原则(专业数据叫可读性好、易扩展)。
如果你把一段同样的代码复制、粘贴到了程序的多个地方以实现在程序的各个地方调用,这个功能,那日后你再对这个功能进行修改时,就需要把程序里多个地方都改一遍,这种写程序的方式是有问题的,因为如果你不小心漏掉了一个地方没改,那可能会导致整个程序的运行都 出问题。 因此我们知道 在开发中一定要努力避免写重复的代码,否则就相当于给自己再挖坑。
还好,函数的出现就能帮我们轻松的解决重复代码的问题,对于需要重复调用的功能,只需要把它写成一个函数,然后在程序的各个地方直接调用这个函数名就好了,并且当需要修改这个功能时,只需改函数代码,然后整个程序就都更新了。
其实OOP编程的主要作用也是使你的代码修改和扩展变的更容易,那么小白要问了,既然函数都能实现这个需求了,还要OOP干毛线用呢? 呵呵,说这话就像,古时候,人们打仗杀人都用刀,后来出来了枪,它的主要功能跟刀一样,也是杀人,然后小白就问,既然刀能杀人了,那还要枪干毛线,哈哈,显而易见,因为枪能更好更快更容易的杀人。函数编程与OOP的主要区别就是OOP可以使程序更加容易扩展和易更改。
四、类
一些概念:
属性:属性就是类里面的一个变量,有类变量和实例变量,类变量是类在定义的时候就有的,实例变量是在实例化的时候才产生的变量。这个可以理解为,人是一个类,他的名字、年龄、性别就是它的属性。
方法:方法就是类的功能,也就是定义在类里面的函数,它实现了某个功能,比如说人有睡觉的功能。
构造函数:什么是构造函数,就是类在实例化的时候做的某些初始化操作,比如说人,你造一个汽车的时候,它得有颜色、型号等等。
析构函数:析构函数就是这个实例在销毁的时候做的一些操作。
定义类:
定义类使用class关键字,类名一般我们开发的时候首字母要大写。python中有经典类和新式类,他俩在python3中没有区别,在python2中经典类在多继承的时候是深度优先,新式类是广度优先。python3中统一是广度优先,这个后面在说继承的时候会说。
class Person(object):#定义类,这个是新式类 nationality = 'China' #类属性 def __init__(self,name,sex): #构造函数 self.name = name #实例属性 self.sex =sex def __del__(self):#析构函数 print('这个是析构函数') def info(self):#方法 print('name is %s sex is %s '%(self.name,self.sex)) class Person1:#定义类,这个是经典类 nationality = 'China' #类属性 def __init__(self,name,sex): #构造函数 self.name = name #实例属性 self.sex =sex def info(self):#方法 print('name is %s sex is %s '%(self.name,self.sex)) niuniu = Person('牛牛','男') #实例化,因为在上面的构造函数里面指定了必须传name和sex,所以在实例化的时候要把name和sex传进去 niuniu.info()#调用实例方法
继承:
class F(object): ''' 这个是父类 ''' def __init__(self,name,sex): self.name = name self.sex = sex def info():#方法 print('name is %s sex is %s '%(self.name,self.sex)) class S(F):#继承F这个类 pass s1 = S('牛牛','男')#实例化子类 s1.info()#因为继承父类,所以父类的方法他都有 那如果在父类里面有一个方法,子类里面也有的话,但是子类想重写一下父类的方法,增加新功能下面这么写: class test(): def __init__(self,name): self.name = name class test1(test): def __init__(self,name,age): # test.__init__(self,name)#修改父类的方法,这个是经典类里面的写法 super(test1,self).__init__(name)#和上面的效果一样,这个是新式类里面的写法 self.name = name self.age = age test1('name','age') 多继承,上面写的都是单继承的,python里面还支持多继承,多继承就是继承多个父类,在python3中多继承都是广度优先的,在python2中有点不同,经典类是深度优先,新式类是广度优先。 class A: def say(self): print('A') class B(A): # pass def say(self): print('B') class C(A): pass # def say(self): # print('c') class D(C,B): pass s=D() s.say()
多态:python里面是不直接支持多态的,可以通过别的方法来实现多态。通过下面的例子就可以实现一种接口,多种实现,对于下面的cry方法来说,不管你传入的是什么对象,只要有cry的方法,都会去调用它的cry方法,不用再一个个的去调用了。
class Animal(object): ''' 父类 ''' def __init__(self,name): self.name = name class Dog(Animal): ''' 狗类,有叫的方法 ''' def cry(self): print('狗 [%s] 汪汪汪'%self.name) class Cat(Animal): ''' 猫类,有叫的方法 ''' def cry(self): print('猫 [%s] 喵喵喵'%self.name) def cry(obj): ''' 定义一个函数,去调用传进来的实例的cry方法 ''' obj.cry() d1 = Dog('大黄')#实例化狗 d2 = Dog('小黄')#实例化狗 c1 = Cat('小白')#实例化猫 c2 = Cat('小黑')#实例化猫 cry(d1)#把对象d1传进来 cry(d2)#把对象d2传进来 objs = [d1,d2,c1,c2]#把上面实例化的对象都放到一个list里面 for obj in objs:#循环统一调用 cry(obj)
私有方法、私有属性:
什么是私有,私有就是只有在类里面可以访问,实例化之后不可以访问和调用,有私有方法和私有属性。私有就把变量名或者函数名前面加上"__"两个下划线,其实就是通过私有来实现封装的。
class Dog(object): __type = '狗'#私有属性 def cry(self): self.__test()#调用私有方法 print('私有属性%s'%self.__type) print('狗 [%s] 汪汪汪'%self.name) def __test(self):#私有方法 self.name = 'test'# d = D() d.cry()#正常可以调用 d.__type#报错,因为是私有属性,在外面不能访问 d.__test()#报错,因为是私有方法,在外面不能访问
静态方法和类方法:
class Stu(object): country = 'china'#类变量 def __init__(self,name): self.name = name @staticmethod #静态方法,和类本身没有什么关系了,就相当于在类里面定义了一个方法而已 def say(): print('xxx') @classmethod #静态方法,和类方法不同的是,它可以使用类变量,必须要传一个值,代表的就是这个类 def hello(cls): print(cls.country) def hi(self): #这个是实例方法 print(self.name) t = Stu('name') Stu.hello() Stu.say() t.hi() t.say() t.hello()