Python学习之路11——面向对象

一、编程范式

  编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程,一个程序是程序员为了得到一个任务结果而编写的一组指令的集合。

  正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。

   不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式。

   两种最重要的编程范式分别是面向过程编程和面向对象编程。

二、面向过程编程

  面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,即:将之前实现的代码块复制到现需功能处。

   程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。

   基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。

   这样做的问题也是显而易见的,就是如果你要对程序进行修改,对你修改的那部分有依赖的各个部分你都也要跟着修改, 随着程序越来越大, 这种编程方式的维护难度会越来越高。

   所以我们一般认为, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护的, 那还是用面向对象最方便了。

 1 #面向过程编程
 2  
 3 while True:
 4     if cpu利用率 > 90%:
 5         #发送邮件提醒
 6         连接邮箱服务器
 7         发送邮件
 8         关闭连接
 9   
10     if 硬盘使用空间 > 90%:
11         #发送邮件提醒
12         连接邮箱服务器
13         发送邮件
14         关闭连接
15   
16     if 内存占用 > 80%:
17         #发送邮件提醒
18         连接邮箱服务器
19         发送邮件
20         关闭连接

 

三、面向对象编程简介

  1、面向对象的概念:

OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述。

使用面向对象编程的原因:

一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 。

另一方面,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

2、面向对象和面向过程的区别:

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

例如五子棋,面向过程的设计思路就是首先分析问题的步骤:

1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。

把上面每个步骤用分别的函数来实现,问题就解决了。

而面向对象的设计则是从另外的思路来解决问题。

整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。

第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。

可以明显地看出,面向对象是以功能来划分问题,而不是步骤。

同样是绘制棋局,这样的行为在面向过程的设计中分散在了众多步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。

而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。

功能上的统一保证了面向对象设计的可扩展性。

比如我要加入悔棋的功能,如果要改动面向过程的设计,那么从输入到判断到显示这一连串的步骤都要改动,甚至步骤之间的循序都要进行大规模调整。

如果是面向对象的话,只用改动棋盘对象就行了,棋盘系统保存了黑白双方的棋谱,简单回溯就可以了,而显示和规则判断则不用顾及,同时整个对对象功能的调用顺序都没有变化,改动只是局部的。

再比如我要把这个五子棋游戏改为围棋游戏,如果你是面向过程设计,那么五子棋的规则就分布在了你的程序的每一个角落,要改动还不如重写。

但是如果你当初就是面向对象的设计,那么你只用改动规则对象就可以了,五子棋和围棋的区别不就是规则吗?

当然棋盘大小好像也不一样,但是你会觉得这是一个难题吗?直接在棋盘对象中进行一番小改动就可以了。而下棋的大致步骤从面向对象的角度来看没有任何变化。

3、面向对象的几个核心特性如下:

Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法。

Object 对象 
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用。

一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同。

Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法。

Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承。

Polymorphism 多态
简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。

四、面向对象编程

  1、类、对象、实例

    面向对象编程的主要作用也是使你的代码修改和扩展变的更容易,那么小白要问了,既然函数都能实现这个需求了,还要OOP干毛线用呢?

      函数编程与OOP的主要区别就是OOP可以使程序更加容易扩展和易更改。

      下面写一个cs程序来面向对象编程做进一步认识。

CS游戏

1、暂不考虑开发场地等复杂的东西,角色恐怖份子、警察

2、每个人都有生命值

3、武器

4、被打中后就会掉血的功能

5、开枪功能

6、换子弹

7、买枪

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 
 6 # 面向对象class
 7 class Role(object):  # 定义一个类, class是定义类的语法,Role是类名,(object)是新式类的写法,暂且先记住
 8     def __init__(self, name, role, weapon, life_value=100, money=15000):  # 初始化函数,在生成一个角色时要初始化的一些属性就填写在这里
 9         self.name = name  # __init__中的第一个参数self为实例名称
10         self.role = role
11         self.weapon = weapon
12         self.life_value = life_value
13         self.money = money
14         # __init__()叫做初始化方法(或构造方法),在类被调用时,这个方法(虽然它是函数形式,但在类中就不叫函数了,叫方法)会自动执行,
15         # 进行一些初始化的动作
16 
17     def shot(self):
18         # 开了枪后要减子弹数
19         print("%s is shooting......." % (self.name))
20 
21     def got_shot(self):
22         # 中枪后要减血
23         print("ah.....%s got shot...." % (self.name))
24 
25     def buy_gun(self, gun_name):
26         # 检查钱够不够,买了枪后要扣钱
27         print("%s just bought %s " % (self.name, gun_name))
28 
29 
30 # 生成一个角色 , 会自动把参数传给Role下面的__init__(...)方法,这个过程叫做类的实例化,r1叫做类的实例
31 r1 = Role('Alex', 'police', "AK47")  # 此时self 相当于 r1 ,  Role(r1,'Alex','police','AK47’)
32 r2 = Role('Jack', 'terrorist', "B22")  # 此时self 相当于 r2 ,  Role(r2,'Jack','terrorist','B22’)
33 
34 print(r1, type(r1))
35 print(r1.role)
36 r1.shot()
37 r1.got_shot()
38 r1.buy_gun("DZT100")

 

    输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/cs.py
2 <__main__.Role object at 0x012DF990> <class '__main__.Role'>
3 police
4 Alex is shooting.......
5 ah.....Alex got shot....
6 Alex just bought DZT100 
7 
8 Process finished with exit code 0

 

  2、类变量与实例变量

    类变量为大家都共有的变量,只加载在类内存当中,不会加载在每个实例里。

    举个例子:创建14亿用户,大家国籍都是中国,如果不把国籍写到类变量中,而是写到实例变量默认参数中,会占用大量内存,这就是类变量的意义。

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # 类变量和实例变量
 6 class Role(object):  # 定义一个类, class是定义类的语法
 7     n = 123  # 类变量
 8     name = "类name"
 9 
10     def __init__(self, name, role, weapon, life_value=100, money=15000):
11         self.name = name  # 实例变量
12         self.role = role
13         self.weapon = weapon
14         self.life_value = life_value
15         self.money = money
16 
17 r1 = Role('Alex', 'police', "AK47")  # 实例
18 
19 print(Role.n, r1.n)
20 print(Role.name, r1.name)

     输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/类变量与实例变量.py
2 123 123
3 类name Alex
4 
5 Process finished with exit code 0

    由上面程序可知,类变量对全局生效,输入类名可直接调用;当类变量与实例变量相同且同时存在的话,实例变量优先。

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # 实例变量
 6 class Role(object):  # 定义一个类, class是定义类的语法
 7     n = 123  # 类变量
 8     name = "类name"
 9 
10     def __init__(self, name, role, weapon, life_value=100, money=15000):
11         self.name = name  # 实例变量
12         self.role = role
13         self.weapon = weapon
14         self.life_value = life_value
15         self.money = money
16 
17 r1 = Role('Alex', 'police', "AK47")  # 实例
18 r1.name = "wupeiqi"
19 r1.bullet = True
20 
21 print(r1.name, r1.bullet)

    输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/类变量与实例变量.py
2 wupeiqi True
3 
4 Process finished with exit code 0

     类进行实例化之后,还可以对实例变量重新赋值、增加变量。

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # 类变量和实例变量
 6 class Role(object):  # 定义一个类, class是定义类的语法
 7     n = 123  # 类变量
 8     name = "类name"
 9 
10     def __init__(self, name, role, weapon, life_value=100, money=15000):
11         self.name = name  # 实例变量
12         self.role = role
13         self.weapon = weapon
14         self.life_value = life_value
15         self.money = money
16 
17 
18 r1 = Role('Alex', 'police', "AK47")  # 实例
19 r2 = Role('lzl', 'terrorist', 'B22')
20 
21 r1.n = "r1的123"
22 print(r1.name, r1.n)
23 print(r2.name, r2.n)
24 
25 
26 Role.n = "Role的123"
27 print(r1.name, r1.n)
28 print(r2.name, r2.n)

 

    输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/类变量与实例变量.py
2 Alex r1的123
3 lzl 123
4 Alex r1的123
5 lzl Role的123
6 
7 Process finished with exit code 0

    执行上面的程序发现,赋值r1.n后只是影响到实例r1,对r2.n并没有任何影响,这是因为赋值的实例变量与类变量名一样时,并不会改变类变量中的值,只影响到当前实例;

    当重新赋值类变量时,r2也跟着改变;

    当类变量是列表时,通过实例都可对类变量进行修改,那让我们对上面的程序做个升级吧!!

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # 类变量和实例变量
 6 class Role(object):  # 定义一个类, class是定义类的语法
 7     list = []  # 定义一个列表类变量
 8 
 9     def __init__(self, name, role, weapon, life_value=100, money=15000):
10         self.name = name  # 实例变量
11         self.role = role
12         self.weapon = weapon
13         self.life_value = life_value
14         self.money = money
15 
16 r1 = Role('Alex', 'police', "AK47")  # 实例
17 r2 = Role('lzl', 'terrorist', 'B22')
18 
19 print("Role.list_before:",Role.list)  # 此时list为空
20 
21 r1.list.append("from r1")
22 r2.list.append("from r2")
23 
24 print("r1.list:",r1.list)
25 print("r2.list:",r2.list)
26 
27 print("Role.list_after:",Role.list)

    输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/类变量与实例变量1.py
2 Role.list_before: []
3 r1.list: ['from r1', 'from r2']
4 r2.list: ['from r1', 'from r2']
5 Role.list_after: ['from r1', 'from r2']
6 
7 Process finished with exit code 0

 

    因为类变量都存储在类对象的内存中,当实例中的类变量发生改变时:

    如果类变量仅是变量,会在相应实例内存中新建一个同名变量赋以修改值,并以修改值优先。

    如果类变量是列表变量,实例变量的修改会指向类变量的同一个内存,因此都会修改类变量的值。

  3、析构函数

    在实例释放、销毁的时候执行,通常用于做一下收尾工作(如关闭数据连接)。

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # 析构函数
 6 class Role(object):  # 定义一个类, class是定义类的语法
 7     def __init__(self, name, role, weapon, life_value=100, money=15000):
 8         self.name = name  # 实例变量
 9         self.role = role
10         self.weapon = weapon
11         self.life_value = life_value
12         self.money = money
13 
14     def __del__(self):
15         print("%s 彻底死了" % self.name)
16 
17     def shot(self):
18         # 开了枪后要减子弹数
19         print("%s is shooting......." % (self.name))
20 
21     def got_shot(self):
22         # 中枪后要减血
23         print("ah.....%s:I got shot...." % (self.name))
24 
25 r1 = Role('Alex', 'police', "AK47")
26 r2 = Role('Wupeiqi', 'terrorist', 'B22')
27 
28 r1.got_shot()
29 del r1
30 
31 r2.got_shot()

 

    输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/析构函数.py
2 ah.....Alex:I got shot....
3 Alex 彻底死了
4 ah.....Wupeiqi:I got shot....
5 Wupeiqi 彻底死了
6 
7 Process finished with exit code 0

    程序结束、del删除实例时才会执行__del__里的内容。

  4、私有属性、私有方法

    私有的概念:只能在类里面进行调用。

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # class私有属性、私有方法
 6 class Role(object):  # 定义一个类, class是定义类的语法
 7     def __init__(self, name, role, weapon, life_value=100, money=15000):
 8         self.name = name
 9         self.role = role
10         self.weapon = weapon
11         self.__life_value = life_value  # 定义私有属性,私有属性在外部不能调用,只能在类里面使用
12         self.money = money
13 
14     def show_status(self):  # 定义函数,调用私有属性
15         print("name:%s weapon:%s life_value:%s" % (self.name, self.weapon, self.__life_value))
16 
17     def __shot(self):  # 定义私有方法
18         print("%s is shooting......." % (self.name))
19 
20     def got_shot(self):
21         self.__shot()  # 调用私有方法
22         print("ah.....%s got shot...." % (self.name))
23 
24 r1 = Role('lzl', 'police', "AK47")
25 
26 # 私有属性
27 # print(r1.__life_value)          #外部调用life_vaule 直接报错
28 # AttributeError: 'Role' object has no attribute '__life_value'
29 r1.show_status()  # 通过方法执行私有属性
30 # name:lzl weapon:AK47 life_value:100
31 
32 # 私有方法
33 # r1.__shot()                     #外部调用私有方法 直接报错
34 # AttributeError: 'Role' object has no attribute '__shot'
35 r1.got_shot()  # 通过其他方法执行私有方法
36 # lzl is shooting.......
37 # ah.....lzl:I got shot....

   5、继承

    继承:它可以使用现有类的所有功能,并在无需重新编写原有类的情况下对这些功能进行拓展。

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # 类的继承
 6 
 7 class People(object):
 8     def __init__(self, name, age):
 9         self.name = name
10         self.age = age
11 
12     def eat(self):
13         print("%s is eating..." % self.name)
14 
15     def sleep(self):
16         print("%s is sleeping...." % self.name)
17 
18 class Man(People):  # 继承类People
19     def play(self):  # 增加新功能
20         print("%s is playing...." % self.name)
21 
22     def sleep(self):  # 重构sleep功能
23         People.sleep(self)
24         print("man is sleeping....")
25 
26 class Woman(People):  # 继承类People
27     def get_birth(self):  # 增加新功能
28         print("%s is born a baby...." % self.name)
29 
30 
31 m1 = Man("jack", 22)
32 w1 = Woman("rose", 33)
33 
34 m1.eat()  # 调用People方法
35 m1.play()  # 调用Man方法
36 # lianzhilie is eating...
37 # lianzhilie is playing....
38 
39 m1.sleep()
40 # lianzhilie is sleeping....
41 # man is sleeping...
42 
43 w1.get_birth()
44 # Alex is born a boby...

    输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/继承.py
2 jack is eating...
3 jack is playing....
4 jack is sleeping....
5 man is sleeping....
6 rose is born a boby....
7 
8 Process finished with exit code 0

    类的继承可节省大量重复的代码。

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # 类的继承,子类初始化
 6 
 7 class People():
 8     def __init__(self, name, age):
 9         self.name = name
10         self.age = age
11 
12     def eat(self):
13         print("%s is eating..." % self.name)
14 
15     def sleep(self):
16         print("%s is sleeping...." % self.name)
17 
18 class Man(People):
19     def __init__(self, name, age, money):  # 重构初始化 覆盖父类
20         # People.__init__(self,name,age)      #加载父类初始化
21         super(Man, self).__init__(name, age)  # 加载父类初始化
22         self.money = money
23         print("%s 一出生就有 $%s" % (self.name, self.money))
24 
25     def play(self):  # 增加新功能
26         print("%s is playing...." % self.name)
27 
28 class Woman(People):
29     def get_birth(self):
30         print("%s is born a boby...." % self.name)
31 
32 m1 = Man("jack", 22, 1000)
33 w1 = Woman("rose", 33)
34 
35 m1.play()
36 w1.get_birth()

    输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/继承.py
2 jack 一出生就有 $1000
3 jack is playing....
4 rose is born a boby....
5 
6 Process finished with exit code 0

    子类需要重构初始化时,会把父类的初始化覆盖掉,所以在重构时需要加载父类的初始化。

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # 类的继承-多继承
 6 
 7 class People(object):  # 新式类
 8     def __init__(self, name, age):
 9         self.name = name
10         self.age = age
11 
12     def eat(self):
13         print("%s is eating..." % self.name)
14 
15     def sleep(self):
16         print("%s is sleeping...." % self.name)
17 
18 class Relation(object):
19     def make_friends(self, obj):
20         print("%s is making friends with %s" % (self.name, obj.name))
21 
22 
23 class Man(Relation, People):  # 多继承
24     def play(self):
25         print("%s is playing...." % self.name)
26 
27 class Woman(People):
28     def get_birth(self):
29         print("%s is born a boby...." % self.name)
30 
31 m1 = Man("jack", 22)
32 w1 = Woman("Alex", 33)
33 
34 m1.make_friends(w1)

     输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/多继承.py
2 jack is making friends with Alex
3 
4 Process finished with exit code 0

    多继承时需注意,在多继承中从父类继承初始化属性时,顺序从左到右开始初始化,只要初始化到属性数据就不再向后继续,所以越往前越优先;

    当父类有初始化,子类也有初始化时,执行子类的初始化,父类的不生效。

    新式类和经典类的区别:

 1 #经典类
 2  
 3 class A():
 4     def __init__(self):
 5         print("A")
 6 class B(A):
 7     pass
 8  
 9 class C(A):
10     def __init__(self):
11         print("C")
12  
13 class D(B,C):
14     pass
15  
16 obj = D()
17 #A
18  
19  
20 #新式类
21  
22 class A(object):
23     def __init__(self):
24         print("A")
25  
26 class B(A):
27     pass
28  
29 class C(A):
30     def __init__(self):
31         print("C")
32  
33 class D(B,C):
34     pass
35  
36 obj = D()
37 #C

     当类是经典类时,多继承情况下,会按照深度优先方式查找;

     当类是新式类时,多继承情况下,会按照广度优先方式查找,Python3以后都是新式类。

     具体参考下图。

 

   6、多态

    多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。

    简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

    那么,多态的作用是什么呢?

    一种接口,多种实现,根据传入东西的不同,实现不同的效果。

    我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。

    而多态则是为了实现另一个目的——接口重用!

    多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。Pyhon不直接支持多态,但可以间接实现。

 1 #!/user/bin/env ptyhon
 2 # -*- coding:utf-8 -*-
 3 # Author: VisonWong
 4 
 5 # 多态
 6 class Animal:
 7     def __init__(self, name):  # Constructor of the class
 8         self.name = name
 9 
10     @staticmethod   
11     def animal_talk(obj):
12         obj.talk()
13 
14 class Cat(Animal):
15     def talk(self):
16         print('%s:Meow!' % (self.name))
17 
18 class Dog(Animal):
19     def talk(self):
20         print('%s:Woof! Woof!' % (self.name))
21 
22 c = Cat('Missy')
23 d = Dog('Lassie')
24 
25 Animal.animal_talk(c)
26 Animal.animal_talk(d)

    输出结果:

1 E:\Python\PythonLearing\venv\Scripts\python.exe E:/Python/PythonLearing/class/多态.py
2 Missy:Meow!
3 Lassie:Woof! Woof!
4 
5 Process finished with exit code 0

 

 

 

 

 

posted @ 2018-05-22 00:22  VisonWong  阅读(272)  评论(0编辑  收藏  举报