您的位置:首页 > 编程语言 > Python开发

Python3学习(21)--面向对象OOP

2017-08-29 09:57 399 查看
任何一门高级语言,都离不开面向对象思想,可见其重要性。

Object Oriented Programming,OOP,面向对象程序设计 是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。OOP 达到了软件工程的三个主要目标:重用性灵活性扩展性

面向对象的三大特性:

封装

继承

多态

本篇我们就来简单的聊一聊Python的OOP面向对象编程

开讲之前,我们先来说一下什么是抽象?

抽象

抽象是从众多的事物中抽取出共同的、本质性的特征,而舍弃其非本质的特征。例如苹果、香蕉、生梨、葡萄、桃子等,它们共同的特性就是水果。得出水果概念的过程,就是一个抽象的过程。要抽象,就必须进行比较,没有比较就无法找到在本质上共同的部分。(摘自搜狗百科)

现在,我们来抽象一个汽车类Car,我们知道,Benz,Audi等品牌车都是汽车这一抽象概念体,或者说是具有汽车这一共性的实例(Instance),它们都有各自的名字,发动机,以及车身的颜色Color(我们只列举这三个属性就够了),汽车是交通工具,所以,它的另一大特性就是供人驾驶,因此,Car类具有一个共性函数,drive。

我们先来定一个Car类,补充它的元部件(名字,发动机,颜色),并赋予它driving的行为,同时我们在定义一个show函数,将汽车的各个属性打印出来:

test.py

#!/usr/bin/env Python3
#-*- encoding:UTF-8 -*-

'''@author = 'appleyk'''
'''@time   = '2017年8月29日10:23:20'''

class Car(object):
def __init__(self,name,engine,color):
self.name   = name
self.engine = engine
self.color  = color
def drive(self):
print('Car is driving....')
def show(self):
print("I'am %s,i hava a %s engine and my color is %s" %(self.name,self.engine,self.color))


我们测试一下这个Car类是否工作正常,我们实例化一个Car的对象,也就是实例,小写的car如下:

car = Car('Benz','Benz','red')
car.drive()
car.show()


我们执行一下test.py



我们思考一个问题? 假设我们的Benz(奔驰)车买回来了,过了一段时间发动机坏了,去4s店呢,人家说修不了,最后给我们换了一个Audi的发动机,我们欣然接受,我们上面的demo可以实现吗?我们开模拟一下:

car = Car('Benz','Benz','red')
car.drive()
car.show()
car.engine = 'Audi' #这里,我们更换下Benz车的发动机
car.show()


重新执行一下,看下效果:



没问题,对不对,放在程序里面,我们想一想为什么能这样呢? 原因就在于Python中的类成员属性变量默认是public的,也就是对外公开的,不仅可以访问,更可以修改。

那么,如果我们去4s店,非Benz发动机不换呢?也就是必须是原配(出厂设置),再狠一点就是,如果,你办不到,给我重新换一辆车,我们看下我们怎么来实现这一出效果:

#!/usr/bin/env Python3
#-*- encoding:UTF-8 -*-

'''@author = 'appleyk'''
'''@time = '2017年8月29日10:23:20'''

class Car(object):
def __init__(self,name,engine,color):
self.name = name
self.__engine = engine #我们在engine属性前,加一个双下划西线,表示这个发动机只支持原配
self.color = color
def drive(self):
print('Car is driving....')
def show(self):
print("I'am %s,i hava a %s engine and my color is %s" %(self.name,self.__engine,self.color))
car = Car('Benz','Benz','red') car.drive() car.show()
car.__engine = 'Audi' #4s店试图给我们换个Audi发动机,我们看下能够成功呢?
car.show()


在Python中,类成员属性变量名前有__的,比如__engine,表示是一个只有本类内部才能访问的属性,对于外部无法访问-->私有成员变量-->private

在Python中,变量名类似__xxx__的(比如 我们的 __author__变量),也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是 private 变量,所以,不能用__engine__这样的变量名。

针对上面的demo,我们做下测试:



如果客户和4s店方,达成协议,4s店和发动机原厂家进行联系,如果对方也没有Benz发动机(缺货),我们就暂时换Audi发动机(假设啊,意在模拟讲解)

封装(一种将抽象性函式接口的实作细节部分包装、隐藏起来的方法)

针对上面的假设,我们只好给Car类的属性变量__engine重新设计一个方法,向外界提供修改汽车发动机的入口,这个入口就是一个函数,我们起名 set_engine(),参数是engine;同时,我们还增加一个get_engine()函数,用来获取当前汽车实例的发动机名:

我们修改demo如下:

#!/usr/bin/env Python3
#-*- encoding:UTF-8 -*-

'''@author = 'appleyk'''
'''@time = '2017年8月29日10:23:20'''

class Car(object):
def __init__(self,name,engine,color):
self.name = name
self.__engine = engine
self.color = color
def drive(self):
print('Car is driving....')
def show(self):
print("I'am %s,i hava a %s engine and my color is %s" %(self.name,self.__engine,self.color))
def set_engine(self,engine):
self.__engine = engine
def get_engine(self):#如果你想访问类中的成员变量,这个self参数一定要带上
return self.__engine
car = Car('Benz','Benz','red') car.drive() car.show()
car.set_engine('Audi')
car.show()
print(car.get_engine())


我们执行一下,看下结果,是不是能让双方都满意:



总结:

类成员变量默认public;如果变量名(可以是属性,也可以是函数)前带有__(双下划线),则为私有private;

私有属性的修改和访问,须通过类内部定义的setXXX和getXXX函数实现(注意,不是必须的),函数名随意:



私有函数的使用,须通过类内部的public公有函数去访问

且,类中的函数第一个参数始终是self,代表函数内部,随时可以访问到自身的内容(属性变量,函数....)

继承

我们注意到,Car这个类继承自object,Python中,object是基类,其他类,都是派生自object。

我们只有一个Car类不行,因为我们发现,无论我们产生几个实例对象,调用Car类的drive函数,输出的始终是



当然,我们可以改下drive函数,传入一个汽车名称carName参数,让print打印的时候带上这个carName,但是,这样不是我们面向对象设计的初衷,你想想,汽车只是一个概念,一个抽象的存在,你却要在这个抽象体内部去具体的实现某些行为,当然这样是可行的,但是对于大型工厂项目来说,如果你这样做了,你想想,如果一个奔驰车发生了变动,是不是汽车类里面也要变动,因为,汽车Car类里面有Benz车的具体行为,二者脱离不了干系,如果有N辆车发生了变动怎么办,我们的Car类,是不是要变动N次,想想都觉得闹心啊。因此,我们需要再定义一个Benz类,去继承Car,继承的关系就相当于父子关系,亲密无间,如下:

class Benz:

class Benz(Car):
pass


别看现在Benz类内部什么也没有,但是,我们的Benz可是继承自Car类啊,因此,Benz类具有Car类的一切特性:

benz = Benz('Benz','Benz','red')
benz.show()
print('发动机: ',benz.get_engine())
benz.drive()


执行一下:



多态(同一个实体同时具有多种形为)

我们发现,Benz类依然保留了Car类的特性,因此,drive的行为依然是父类行为的drive,我们如果想有自己的行为怎么办呢?

这个时候,我们就需要重写父类的drive方法了,同时我们验证一下Benz类的实例benz是否是Car类型:

class Benz(Car):
def drive(self):#再次提醒一下,第一个参数self在定义函数的时候,一定不要漏了!
print('Benz is driving....')

benz = Benz('Benz','Benz','red')
print('benz是不是 Car类型:',isinstance(benz,Car))
benz.show()
print('发动机: ',benz.get_engine())
benz.drive()

我们执行一下:



我们再来扩展一下Benz类的drive行为,Car类没有定义最大速度speed这个属性变量,我们想在Benz中体现,同时我们还想Benz类不丢失Car类的三个成员name,engine和color,我们用super函数实现父类构造函数的同时,在Benz类中的构造函数__init__()中初始化我们的speed;为了形象的阐述多态,我们再定义一个Audi类,和Benz类一样,也有speed变量,但不同于Benz类,Audi中的speed是作为一个全局变量存在的,初始化的时候,我们不用__init__()函数帮忙,我们可以直接拿Audi的实例去访问和修改。

针对上述描述,我们修改我们的demo如下:

#!/usr/bin/env Python3
#-*- encoding:UTF-8 -*-

'''@author = 'appleyk'''
'''@time   = '2017年8月29日10:23:20'''

class Car(object):
def __init__(self,name,engine,color):
self.name   = name
self.__engine = engine
self.color  = color
def drive(self):
print('Car is driving....')
def show(self):
print("I'am %s,i hava a %s engine and my color is %s" %(self.name,self.__engine,self.color))
def set_engine(self,engine):
self.__engine = engine
def get_engine(self):#如果你想访问类中的成员变量,这个self参数一定要带上
return self.__engine

class Benz(Car):
def __init__(self,name,engine,color,speed):
super().__init__(name,engine,color)#实现父类的构造函数,初始化name,engine和color
self.speed = speed                 #Benz类亲自初始化这个speed变量
def drive(self):
print('Benz is driving....,max speed is %s km/h ' %self.speed)

class Audi(Car):
speed = 0
def drive(self):
global speed                       #这个关键字 还记得吧
print('Audi is driving....max speed is',self.speed,'km/h')


好了,重设的部分我们已经写好了,对比父类Car的drive行为,我们的子类Benz和Audi显然已经超越了父类,我们来测试一下:

benz = Benz('Benz','Benz','red',380)
print('benz是不是 Car类型:',isinstance(benz,Car))
benz.show()
print('发动机: ',benz.get_engine())
benz.drive()
print('--------------多态的实现----------------')
audi = Audi('Audi','Audi','black')
audi.speed = 320
print('audi是不是 Car类型:',isinstance(audi,Car))
audi.show()
print('发动机: ',audi.get_engine())
audi.drive()


我们执行一下



我们可以看出来,实例benz和audi都是属于Car类型的,假设我们实例化一个car对象,我们能反着推出,car也是属于Benz和Audi类型的吗? 我们试试:



显然,不能这样以为,既然car连Benz类型都不是,更别提是属于Audi类型了;

总结:在继承关系中,子类继承自父类,那子类的实例可以当做父类。但是,反过来就不行,父类的实例无法当做子类类型。

          多态的实现可以解决项目中紧耦合的问题,提高程序的可扩展性

          封装,实现数据和使用分离,使我门只关心结果,不关心过程。

讲到这,还没有讲完,我们想一下,什么东西也可以driving,但是不具有Car类的特性,比如发动机,我们想一想,发现有一个可以对号入座,那就是玩具车ToyCar(假设,足够大),我们简单的定义一下这个类:

class ToyCar(object):#这里,我们不能继承Car类,答案上面说过了
def drive():
print('ToyCar is driving....')
如果,这个类,要和Benz,Audi类一样,可以不区分是不是都属于Car类,就可以实现各自的drive行为,我们该怎么做呢?当然,共性还是有的,我们发现这三个类,都具有drive函数(行为)。

静态语言 vs 动态语言

对于静态语言,java来说,我们需要这样定义去实现我们上述的功能:

public void drive(Car car){
car.drive();//car对象可以是Benz,也可以是Audi的实例
}

public void drive(ToyCar car){
car.drive(); //car对象是ToyCar的实例
}

对于动态语言Python来说,我们不关心传过来的参数对象属于哪个类型,我们只关心传过来的对象,是否具有drive这一行为,也就是是否具有drive()函数,因此,我们可以在全局作用域里定义一个drive()函数,此drive非彼drive:

class ToyCar(object):
def drive():
print('ToyCar is driving....')
def drive(oj):
oj.drive()

我们来实现我们的上述功能:

car1 = Audi('Audi','Audi','red')
car2 = Benz('Benz','Benz','white',380)
toycar = ToyCar
drive(car1)
drive(car2)
drive(toycar)

执行一下:



这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

Python 的“file-like object“就是一种鸭子类型,这里我们不做多讲,知道意思就行。

结束语:学习使人快乐!哪怕每天进步一点点,多学一点点,也都是我们自己的,想想日积月累,我都觉得可怕,不是觉得自己坚持不下来,而是,成功,离自己越来越近,让自己感到充血,充血是什么,是兴奋!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: