一.编程范式
编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 。
一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。 不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式(比如说:java),当然也有些语言可以同时支持多种编程范式( Python )。 两种最重要的编程范式分别是面向过程编程和面向对象编程。
二.面向过程编程( Procedural Programming )
Procedural programming uses a list of instructions to tell the computer what to do step-by-step.
面向过程编程依赖 - 你猜到了- procedures,一个 procedure 包含一组要被进行计算的步骤, 面向过程又被称为 top-down languages, 就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。
举个典型的面向过程的例子, 数据库备份, 分三步,连接数据库,备份数据库,测试备份文件可用性。
1 def db_conn():
2 print("connecting db...")
3
4 def db_backup(dbname):
5 print("导出数据库...",dbname)
6 print("将备份文件打包,移至相应目录...")
7
8 def db_backup_test():
9 print("将备份文件导入测试库,看导入是否成功")
10
11 def main():
12 db_conn()
13 db_backup('my_db')
14 db_backup_test()
15
16 if __name__ == '__main__':
17 main()
18
19
20 "'
21 这样做的问题也是显而易见的,就是如果你要对程序进行修改,对你修改的那部分有依赖的各个部分你都也要跟着修改, 举个例子,如果程序开头你设置了一个变量值 为1 , 但如果其它子过程依赖这个值 为1的变量才能正常运行,那如果你改了这个变量,那这个子过程你也要修改,假如又有一个其它子程序依赖这个子过程 , 那就会发生一连串的影响,随着程序越来越大, 这种编程方式的维护难度会越来越高。
22 所以我们一般认为, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护 的, 那还是用面向对象最方便了。
23 '''
24
三.面向对象编程
OOP 编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
1>.Class 类:一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性( variables ( data ))、共同的方法(其实类就是一堆函数组成的);
2>.Object 对象:一个对象即是一个类的实例化(就好像是 zabbix 模板被监控的主机引用的过程)后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同;
3>.Encapsulation 封装:在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法;
4>.Inheritance 继承:一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承;
5>.Polymorphism 多态:多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
四.面向对象编程( Object-Oriented Programming )介绍
无论用什么形式来编程,我们都要明确记住以下原则:
1>.写重复代码是非常不好的低级行为;
2>.你写的代码需要经常变更;
其实 OOP 编程的主要作用也是使你的代码修改和扩展变的更容易,那么小白要问了,既然函数都能实现这个需求了,还要 OOP 干毛线用呢? 呵呵,说这话就像,古时候,人们打仗杀人都用刀,后来出来了枪,它的主要功能跟刀一样,也是杀人,然后小白就问,既然刀能杀人了,那还要枪干毛线,哈哈,显而易见,因为枪能更好更快更容易的杀人。函数编程与 OOP 的主要区别就是 OOP 可以使程序更加容易扩展和易更改。好了 ,我就不废话了,让我们开始写我们的第一个类吧:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Man(object): #定义类名首字母需要大写哟
7 def __init__(self,name,type): #初始化对象(初始化函数又叫构造函数),即一旦调用Man这个类,就会执行以下代码
8 self.name = name #这个步骤是相当于a.name = name,为了就是方便下面的函数去调用他们
9 self.type = type #这个步骤相当于a.type = type
10 def introduce(self):
11 print("[%s] is a good boy!" % self.name)
12 def eat(self,food):
13 print("[%s] eating [%s]"%(self.name,food))
14 jie = Man("yinzhengjie","goodboy")
15 print(jie.name,jie.type)
16 jie.introduce()
17 jie.eat("apple")
18
19
20
21 #以上代码执行结果如下:
22 yinzhengjie goodboy
23 [yinzhengjie] is a good boy!
24 [yinzhengjie] eating [apple]
五.面向对象的特性
1.什么是特性 property(是用来提供接口的一种方式)
property 是一种特殊的属性,访问它时会执行函数然后返回值
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 import math
7 class Circle:
8 def __init__(self,radius): #圆的半径radius
9 self.radius=radius
10 @property #其实就是一个装饰器,作用等效于 area = property(area)
11 def area(self):
12 return math.pi * self.radius**2 #计算面积
13 @property #作用等效于 area = property(perimeter)
14 def perimeter(self):
15 return 2*math.pi*self.radius #计算周长
16
17 c=Circle(5) #传入参数半径
18 print(c.radius) #调用的时候就不用调用类里面的方法(函数),直接调用数据属性,不需要调用函数属性
19 print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
20 print(c.perimeter) #同上
21
22
23 #以上代码执行结果如下:
24 5
25 78.53981633974483
26 31.41592653589793
2 .为什么要用 property
将一个类的函数定义成特性以后,对象再去使用的时候 obj.name , 根本无法察觉自己的 name 是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则,即它是将一个方法变成静态属性。
除此之外,看下
1 '''
2 ps:面向对象的封装有三种方式:
3 【public】
4 这种其实就是不封装,是对外公开的
5 【protected】
6 这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
7 【private】
8 这种封装对谁都不公开
9 '''
Python 并没有在语法上把它们三个内建到自己的 class 机制中,在 C++ 里一般会将所有的所有的数据都设置为私有的,然后提供 set 和 get 方法(接口)去设置和获取,在 Python 中通过 property 方法可以实现
使用展示一:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Man(object):
7 name = "ddddd"
8 def __init__(self ,name):
9 self.name = name
10 @classmethod
11 def talk(self):
12 print("%s is talking" % self.name)
13 @property #把eat这个方法变成静态属性
14 def eat(self):
15 print("%s is eating"% self.name)
16 @eat.setter #赋值,
17 def eat(self,num):
18 print("changing status", num)
19 @eat.deleter #删除,
20 def eat(self):
21 print("deleting....eat")
22 d = Man("Yinzhengjie")
23 d.eat #触发 @property
24 d.eat = 1000 #触发 @eat.setter
25 del d.eat #触发 @eat.deleter
26
27
28 #以上代码执行结果如下:
29 Yinzhengjie is eating
30 changing status 1000
31 deleting....eat
使用展示二:
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Foo:
7 def __init__(self,val):
8 self.__NAME=val #将所有的数据属性都隐藏起来
9
10 @property
11 def name(self):
12 return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)
13
14 @name.setter
15 def name(self,value):
16 if not isinstance(value,str): #在设定值之前进行类型检查
17 raise TypeError('%s must be str' %value)
18 self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
19
20 @name.deleter
21 def name(self):
22 raise TypeError('Can not delete')
23
24 f=Foo('yinzhengjie')
25 print(f.name)
26 # f.name=10 #抛出异常'TypeError: 10 must be str'
27 # del f.name #抛出异常'TypeError: Can not delete'
28
29 #以上代码执行结果如下:
30 yinzhengjie
** 应用场景:**
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6
7
8 class Flight(object):
9 def __init__(self, name):
10 self.flight_name = name
11
12 def checking_status(self):
13 print("checking flight %s status " % self.flight_name)
14 return 3
15
16 @property
17 def flight_status(self):
18 status = self.checking_status()
19 if status == 0:
20 print("flight got canceled...")
21 elif status == 1:
22 print("flight is arrived...")
23 elif status == 2:
24 print("flight has departured already...")
25 else:
26 print("cannot confirm the flight status...,please check later")
27
28 @flight_status.setter # 修改
29 def flight_status(self, status):
30 status_dic = {
31 0: "canceled",
32 1: "arrived",
33 2: "departured"
34 }
35 print("\033[31;1mHas changed the flight status to \033[0m", status_dic.get(status))
36
37 @flight_status.deleter # 删除
38 def flight_status(self):
39 print("status got removed...")
40
41
42 f = Flight("CA980")
43 f.flight_status
44 f.flight_status = 2 # 触发@flight_status.setter
45 del f.flight_status # 触发@flight_status.deleter
46
47
48
49 #以上代码执行结果如下:
50 checking flight CA980 status
51 cannot confirm the flight status...,please check later
52 Has changed the flight status to departured
53 status got removed...
六.封装
封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者 self 间接获取被封装的内容
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
学习封装前要掌握的知识点:
1>.类变量:既可以作为默认公有属性也可以全局修改或增加新属性。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 '''
7 类变量的作用:
8 1>.作为默认公有属性
9 2>.全局修改或增加新属性
10 '''
11
12 class People(object):
13 nationality = "CN" #类变量
14 def __init__(self,name,age,job):
15 pass
16 p = People("yinzhengjie","25","IT")
17 p.nationality = "JP" #修改类变量
18 print(p.nationality)
19 p2 = People("alex","35","IT")
20 People.weapon = "Nepal Saber" #全局加武器装备尼泊尔军刀,这个就是类变量的应用
21 print(p.weapon)
22
23
24
25 #以上代码执行结果如下:
26 JP
27 Nepal Saber
2>.实例变量(成员属性):其特性和类变量相反,仅仅属于你自己的实例变量。一般构造函数的实例变量都是实例变量,(即每个实例。存在自己内存空间里的属性。)
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 '''
7 类变量的作用:
8 1>.作为默认公有属性
9 2>.全局修改或增加新属性
10 '''
11
12 class People(object):
13 nationality = "CN" #类变量
14 def __init__(self,name,age,job):
15 self.name = name #实例变量,换句话说只要在构造函数里面定义的变量都是实例变量!当然你也可以新增哟。
16 self.age = age
17 self.job = job
18 p = People("yinzhengjie","25","IT")
19 p.hobbie = "basketball" #添加一个实例变量
20 print(p.hobbie)
21
22
23
24 #以上代码执行结果如下:
25 basketball
3>.公有属性:对应的就是类变量的属性
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Man(object): #定义类名首字母需要大写哟
7 age = 25 #类变量,存在内的内存地址里,可以被所有实例共享引用
8 def __init__(self,name,type): #初始化函数(构造函数)
9 self.name = name #这个步骤是相当于jie.name = name,为了就是方便下面的函数去调用他们
10 self.type = type #这个步骤相当于jie.type = type
11 def introduce(self):
12 print("[%s] is a good boy! I have [%s] years old!" % (self.name,self.age)) #注意:“self.age”其实是引用Man中的类变量
13 def eat(self,food):
14 print("[%s] eating [%s]"%(self.name,food))
15 jie = Man("yinzhengjie","goodboy") #其中“jie”就是Man的实例,整个动作“jie = Man("yinzhengjie","goodboy") ”叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了
16 print(jie.name,jie.type)
17 jie.introduce()
18 jie.eat("apple")
19 jie.name = "尹正杰" #赋值
20 print(jie.name)
21
22
23 #以上代码执行结果如下:
24 yinzhengjie goodboy
25 [yinzhengjie] is a good boy! I have [25] years old!
26 [yinzhengjie] eating [apple]
27 尹正杰
4>.私有属性:表示不想被别人访问到的属性,只能在内部各函数中调用。隐藏一些功能的实现细节,只给外部暴露调用接口
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 '''
7 类变量的作用:
8 1>.作为默认公有属性
9 2>.全局修改或增加新属性
10 '''
11
12 class People(object):
13 nationality = "CN" #类变量
14 def __init__(self,name,age,sex):
15 self.name = name #实例变量,换句话说只要在构造函数里面定义的变量都是实例变量!当然你也可以新增哟。
16 self.age = age
17 self.__sex = sex #定义私有属性,其只能在内部各函数(严格来说我们应该叫“内部方法”)中调用
18 def go_to_toilet(self):
19 if self.__sex == "F": #在内部的方法中是可以成功调用的!
20 print("stand up.....")
21 else:
22 print("蹲着.....")
23 p = People("yinzhengjie","25","F")
24 p.go_to_toilet()
25 # print(p.sex) #外部调用的话会报错!因为私有属性只能内部方法调用,而不能从外部调用!
26
27
28
29 #以上代码执行结果如下:
30 stand up.....
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class People(object):
7 nationality = "CN" #类变量
8 def __init__(self,name,age,sex):
9 self.name = name #实例变量,换句话说只要在构造函数里面定义的变量都是实例变量!当然你也可以新增哟。
10 self.age = age
11 self.__sex = sex #定义私有属性,其只能在内部各函数(严格来说我们应该叫“内部方法”)中调用
12 def get_sex(self):
13 return self.__sex #将私有属性的值返回
14 p = People("yinzhengjie","25","F")
15 print(p.get_sex()) #外部只能访问,不可以修改哟,(因为不能给方法复制!)
16
17
18
19
20 #以上代码执行结果如下:
21 F
七.继承
-
继承顺序
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class A(object):
7 def test(self):
8 print('from A')
9
10 class B(A):
11 def test(self):
12 print('from B')
13
14 class C(A):
15 def test(self):
16 print('from C')
17
18 class D(B):
19 def test(self):
20 print('from D')
21
22 class E(C):
23 def test(self):
24 print('from E')
25
26 class F(D,E):
27 # def test(self):
28 # print('from F')
29 pass
30 f1=F()
31 f1.test()
32 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
33
34 '''
35 新式类继承顺序:F->D->B->E->C->A (广度优先,【不会优先找到最顶级的类】)
36 经典类继承顺序:F->D->B->A->E->C (深度优先)
37 python3中统一都是新式类
38 pyhon2中才分新式类与经典类
39 '''
40
41 #以上代码执行结果如下:
42
43 from D
44 (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
- 继承原理(python如何实现的继承)
Python 到底是如何实现继承的,对于你定义的每一个类,Python 会计算出一个方法解析顺序 (MRO) 列表,这个 MRO 列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,Python 会在 MRO 列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个 MRO 列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的 MRO 列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
- 子类中调用父类方法
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
方法一:父类名.父类方法()
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Vehicle: # 定义交通工具类
7 Country = 'China'
8
9 def __init__(self, name, speed, load, power):
10 self.name = name
11 self.speed = speed
12 self.load = load
13 self.power = power
14
15 def run(self):
16 print('开动啦...')
17
18 class Subway(Vehicle): # 地铁
19 def __init__(self, name, speed, load, power, line):
20 Vehicle.__init__(self, name, speed, load, power)
21 self.line = line
22
23 def run(self):
24 print('地铁%s号线欢迎您' % self.line)
25 Vehicle.run(self)
26
27 line15 = Subway('中国地铁', '180m/s', '1000人/箱', '电', 15)
28 line15.run()
29
30
31 #以上代码执行结果如下:
32 地铁15号线欢迎您
33 开动啦...
方法二:super()
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class Vehicle: # 定义交通工具类
7 Country = 'China'
8
9 def __init__(self, name, speed, load, power):
10 self.name = name
11 self.speed = speed
12 self.load = load
13 self.power = power
14 def run(self):
15 print('开动啦...')
16 class Subway(Vehicle): # 地铁
17 def __init__(self, name, speed, load, power, line):
18 # super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
19 super().__init__(name, speed, load, power)
20 self.line = line
21 def run(self):
22 print('地铁%s号线欢迎您' % self.line)
23 super(Subway, self).run()
24 class Mobike(Vehicle): # 摩拜单车
25 pass
26 line13 = Subway('中国地铁', '180m/s', '1000人/箱', '电', 13)
27 line13.run()
28
29 #以上代码执行结果如下:
30 地铁13号线欢迎您
31 开动啦...
不用 super 引发的惨案
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #每个类中都继承了且重写了父类的方法
7 class A:
8 def __init__(self):
9 print('A的构造方法')
10 class B(A):
11 def __init__(self):
12 print('B的构造方法')
13 A.__init__(self)
14
15
16 class C(A):
17 def __init__(self):
18 print('C的构造方法')
19 A.__init__(self)
20
21
22 class D(B,C):
23 def __init__(self):
24 print('D的构造方法')
25 B.__init__(self)
26 C.__init__(self)
27
28 pass
29 f1=D()
30
31 print(D.__mro__) #python2中没有这个属性
32
33
34
35 #以上代码执行结果如下:
36 D的构造方法
37 B的构造方法
38 A的构造方法
39 C的构造方法
40 A的构造方法
41 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
当你使用 super() 函数时,Python 会在 MRO 列表上继续搜索下一个类。只要每个重定义的方法统一使用 super() 并只调用它一次,那么控制流最终会遍历完整个 MRO 列表,每个方法也只会被调用一次(注意注意注意:使用 super 调用的所有属性,都是从 MRO 列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看 MRO 列表)
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 #每个类中都继承了且重写了父类的方法
7 class A:
8 def __init__(self):
9 print('A的构造方法')
10 class B(A):
11 def __init__(self):
12 print('B的构造方法')
13 super(B,self).__init__()
14
15
16 class C(A):
17 def __init__(self):
18 print('C的构造方法')
19 super(C,self).__init__()
20
21
22 class D(B,C):
23 def __init__(self):
24 print('D的构造方法')
25 super(D,self).__init__()
26
27 f1=D()
28
29 print(D.__mro__) #python2中没有这个属性
30
31
32 #以上代码执行结果如下:
33 D的构造方法
34 B的构造方法
35 C的构造方法
36 A的构造方法
37 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
1>.实现继承是指使用基类的属性和方法而无需额外编码的能力;
2>.接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法);
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee (员工) 是一个人,Manager(领导) 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg(推) 类却不能继承 Person(人) 类,因为腿并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法。
OOP 开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class SchoolMember(object):
7 def __init__(self,name,age,sex):
8 self.name = name
9 self.age = age
10 self.sex = sex
11 def tell(self):
12 info = """
13 --------info of %s-------------
14 name : %s
15 age : %s
16 sex : %s
17 """%(self.name,self.name,self.age,self.sex)
18
19 class Teacher(SchoolMember): #继承父类“SchoolMember”,这样的话如果父类的一些变量就不用重复再子类中定义了.
20 def __init__(self,name,age,sex,salary): #初始化函数
21 SchoolMember.__init__(self,name,age,sex) #这个步骤表示将“name,age,sex”这几个值传递给父类SchoolMember中的"__init__(self,name,age,sex):",这样就继承了父类的所有实例变量
22 self.salary = salary #表示重新构造函数
23 def teaching(self,course):
24 print("%s is teaching %s"%(self.name,course))
25
26 t = Teacher("yinzhengjie",25,"boy","1000") #调用子类的时候除了在父类中含有的功能外,还多了一个“salary”参数。
27 print(t.name)
28 print(t.sex)
29 print(t.salary)
30
31
32 #以上代码执行结果如下:
33 yinzhengjie
34 boy
35 1000
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class SchoolMember(object):
7 members = 0
8 def __init__(self,name,age,sex):
9 self.name = name
10 self.age = age
11 self.sex = sex
12 SchoolMember.members += 1 #注意着是调用的类变量,我们不能用"self.members"去自加哟!
13 print("初始化了一个新学校成员",self.name)
14 def tell(self):
15 info = """
16 --------info of %s-------------
17 name : %s
18 age : %s
19 sex : %s
20 """%(self.name,self.name,self.age,self.sex)
21 print(info)
22 class Teacher(SchoolMember): #继承父类“SchoolMember”,这样的话如果父类的一些变量就不用重复再子类中定义了.
23 def __init__(self,name,age,sex,salary): #初始化函数
24 SchoolMember.__init__(self,name,age,sex) #这个步骤表示将“name,age,sex”这几个值传递给父类SchoolMember中的"__init__(self,name,age,sex):",这样就继承了父类的所有实例变量
25 self.salary = salary #表示重新构造函数
26 def teaching(self,course):
27 print("%s is teaching %s"%(self.name,course))
28 class Student(SchoolMember):
29 def __init__(self, name, age, sex, salary): # 初始化函数
30 SchoolMember.__init__(self, name, age, sex)
31 def pay_tuition(self, amount):
32 self.paid_tuition = amount
33 print("student %s has paid tution amoint %s" % (self.name, amount))
34 t = Teacher("yinzhengjie",25,"boy","1000") #调用子类的时候除了在父类中含有的功能外,还多了一个“salary”参数。
35 s = Student("lifeng","26","M","pys600")
36 t.tell()
37 t.teaching("python")
38 s.pay_tuition(11000)
39 print("目前学校公有[%s]个学生"% SchoolMember.members)
40
41
42 #以上代码执行结果如下:
43 初始化了一个新学校成员 yinzhengjie
44 初始化了一个新学校成员 lifeng
45
46 --------info of yinzhengjie-------------
47 name : yinzhengjie
48 age : 25
49 sex : boy
50
51 yinzhengjie is teaching python
52 student lifeng has paid tution amoint 11000
53 目前学校公有[2]个学生
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 '''
7 继承的作用:
8 1>.直接调用父类方法;
9 2>.继承父类方法并重构父类方法,先重构,在重构的方法里手动调用父类方法
10 3>.可以定义子类自己的方法;
11 4>.可以析构方法"__del__"
12 '''
13 class SchoolMember(object):
14 members = 0
15 def __init__(self,name,age,sex):
16 self.name = name
17 self.age = age
18 self.sex = sex
19 SchoolMember.members += 1 #注意着是调用的类变量,我们不能用"self.members"去自加哟!
20 print("初始化了一个新学校成员",self.name)
21 def tell(self):
22 info = """
23 --------info of %s-------------
24 name : %s
25 age : %s
26 sex : %s
27 """%(self.name,self.name,self.age,self.sex)
28 print(info)
29 def __del__(self): #析构函数(析构方法)
30 print(("注意:[%s]已经被开除了!" % self.name))
31 SchoolMember.members -= 1
32 class Teacher(SchoolMember): #继承父类“SchoolMember”,这样的话如果父类的一些变量就不用重复再子类中定义了.
33 def __init__(self,name,age,sex,salary): #初始化函数
34 SchoolMember.__init__(self,name,age,sex) #这个步骤表示将“name,age,sex”这几个值传递给父类SchoolMember中的"__init__(self,name,age,sex):",这样就继承了父类的所有实例变量
35 self.salary = salary #表示重新构造函数
36 def teaching(self,course):
37 print("%s is teaching %s"%(self.name,course))
38 class Student(SchoolMember):
39 def __init__(self, name, age, sex, salary): # 初始化函数
40 SchoolMember.__init__(self, name, age, sex)
41 def pay_tuition(self, amount):
42 self.paid_tuition = amount
43 print("student %s has paid tution amoint %s" % (self.name, amount))
44 t = Teacher("yinzhengjie",25,"boy","1000") #调用子类的时候除了在父类中含有的功能外,还多了一个“salary”参数。
45 s = Student("lifeng","26","M","pys600")
46 s2 = Student("test","22","F","pys16")
47 s3 = Student("test_1","23","F","pys16")
48 del s2 #删除一个实例用del,在实例被销毁后自动执行
49 t.tell()
50 t.teaching("python")
51 s.pay_tuition(11000)
52 print("目前学校公有[%s]个学生"% SchoolMember.members)
53
54
55
56 #以上大米执行结果如下:
57 初始化了一个新学校成员 yinzhengjie
58 初始化了一个新学校成员 lifeng
59 初始化了一个新学校成员 test
60 初始化了一个新学校成员 test_1
61 注意:[test]已经被开除了!
62
63 --------info of yinzhengjie-------------
64 name : yinzhengjie
65 age : 25
66 sex : boy
67
68 yinzhengjie is teaching python
69 student lifeng has paid tution amoint 11000
70 目前学校公有[3]个学生
71 注意:[test_1]已经被开除了! #以为程序结束了,内存会被释放掉,所以在del实例后,会打印以下3行代码。
72 注意:[yinzhengjie]已经被开除了!
73 注意:[lifeng]已经被开除了!
八.组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教 Python 课程。当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。
1 #!/usr/bin/env python
2 #_*_coding:utf-8_*_
3 #@author :yinzhengjie
4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
5 #EMAIL:y1053419035@qq.com
6 class BirthDate:
7 def __init__(self,year,month,day):
8 self.year=year
9 self.month=month
10 self.day=day
11 class Couse:
12 def __init__(self,name,price,period):
13 self.name=name
14 self.price=price
15 self.period=period
16
17 class Teacher:
18 def __init__(self,name,gender):
19 self.name=name
20 self.gender=gender
21 def teach(self):
22 print('teaching')
23 class Professor(Teacher):
24 def __init__(self,name,gender,birth,course):
25 Teacher.__init__(self,name,gender) #直接将Teacher类的方法那来这个类里面用的方式就叫组合
26 self.birth=birth
27 self.course=course
28
29 p1=Professor('egon','male',BirthDate('1992','1','27'),Couse('python','28000','4 months'))
30 print(p1.birth.year,p1.birth.month,p1.birth.day)
31 print(p1.course.name,p1.course.price,p1.course.period)
32
33
34
35
36 #以上代码执行结果如下:
37 1992 1 27
38 python 28000 4 months
九.接口与归一化设计
1.什么是接口
1 =================第一部分:Java 语言中的接口很好的展现了接口的含义: IAnimal.java
2 /*
3 * Java的Interface很好的体现了我们前面分析的接口的特征:
4 * 1)是一组功能的集合,而不是一个功能
5 * 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作
6 * 3)接口只定义函数,但不涉及函数实现
7 * 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了 */
8
9 package com.oo.demo;
10 public interface IAnimal {
11 public void eat();
12 public void run();
13 public void sleep();
14 public void speak();
15 }
16
17 =================第二部分:Pig.java:猪”的类设计,实现了IAnnimal接口
18 package com.oo.demo;
19 public class Pig implements IAnimal{ //如下每个函数都需要详细实现
20 public void eat(){
21 System.out.println("Pig like to eat grass");
22 }
23
24 public void run(){
25 System.out.println("Pig run: front legs, back legs");
26 }
27
28 public void sleep(){
29 System.out.println("Pig sleep 16 hours every day");
30 }
31
32 public void speak(){
33 System.out.println("Pig can not speak"); }
34 }
35
36 =================第三部分:Person2.java
37 /*
38 *实现了IAnimal的“人”,有几点说明一下:
39 * 1)同样都实现了IAnimal的接口,但“人”和“猪”的实现不一样,为了避免太多代码导致影响阅读,这里的代码简化成一行,但输出的内容不一样,实际项目中同一接口的同一功能点,不同的类实现完全不一样
40 * 2)这里同样是“人”这个类,但和前面介绍类时给的类“Person”完全不一样,这是因为同样的逻辑概念,在不同的应用场景下,具备的属性和功能是完全不一样的 */
41
42 package com.oo.demo;
43 public class Person2 implements IAnimal {
44 public void eat(){
45 System.out.println("Person like to eat meat");
46 }
47
48 public void run(){
49 System.out.println("Person run: left leg, right leg");
50 }
51
52 public void sleep(){
53 System.out.println("Person sleep 8 hours every dat");
54 }
55
56 public void speak(){
57 System.out.println("Hellow world, I am a person");
58 }
59 }
60
61 =================第四部分:Tester03.java
62 package com.oo.demo;
63
64 public class Tester03 {
65 public static void main(String[] args) {
66 System.out.println("===This is a person===");
67 IAnimal person = new Person2();
68 person.eat();
69 person.run();
70 person.sleep();
71 person.speak();
72
73 System.out.println("\n===This is a pig===");
74 IAnimal pig = new Pig();
75 pig.eat();
76 pig.run();
77 pig.sleep();
78 pig.speak();
79 }
80 }
81
82 java中的interface
** java 中的 interface**
继承有两种用途:
1>:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
2>:声明某个子类兼容于某基类,定义一个接口类 Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。
def read(self): #定接口函数read
pass
def write(self): #定义接口函数write
pass
class Txt(Interface): #文本,具体实现read和write
def read(self):
print('文本数据的读取方法')
def write(self):
print('文本数据的读取方法')
class Sata(Interface): #磁盘,具体实现read和write
def read(self):
print('硬盘数据的读取方法')
def write(self):
print('硬盘数据的读取方法')
class Process(All_file):
def read(self):
print('进程数据的读取方法')
def write(self):
print('进程数据的读取方法')
实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象 Linux 的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
在 Python 中根本就没有一个叫做 interface 的关键字,上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,如果非要去模仿接口的概念,可以借助第三方模块:
http://pypi.python.org/pypi/zope.interface
中 twisted 的twisted\internet\interface.py 里使用 zope.interface
文档
https://zopeinterface.readthedocs.io/en/latest/
设计模式:
https://github.com/faif/python-patterns
2. 为何要用接口
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样