第6章自动化系列六之Python面向对象基础

Python自动化
时间:2016.08.27

1.面向對象基础概述

无论用什么形式来编程,我们都要明确记住以下原则:
1.写重复代码是非常低级的行为
2.你写的代码需要经常变更

1.面向对象编程方式实现监控报警(最容易被初学者接受):

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

2.随着时间的推移,开始使用了函数式编程,增强代码的重用性和可读性

  1. def 发送邮件(内容)
  2. #发送邮件提醒
  3. 连接邮箱服务器
  4. 发送邮件
  5. 关闭连接
  6. while True
  7. if cpu利用率 > 90%:
  8. 发送邮件('CPU报警')
  9. if 硬盘使用空间 > 90%:
  10. 发送邮件('硬盘报警')
  11. if 内存占用 > 80%:
  12. 发送邮件('内存报警')

3.今天我们来使用面向对象编程(OOP面向对象程序设计)

1.1创建类和对象

面向对象编程是一种编程方式,此变成方式和落地需要使用"类(class)"和"对象(object)"来实现,所以,面向对象编程其实就是对"类"和"对象"的使用。

  1. #class:关键字,表示要创建类
  2. #F1:类的名称
  3. #self:形式参数(必填)
  4. # 创建类
  5. class F1:
  6. # 创建类中的函数
  7. def bar(self):
  8. print("bar") #可以是功能模块
  9. # 根据类F1创建对象obj
  10. obj = F1() #创建对象,类名称后加括号即可

这么说可能有一些抽象,我们来看一个具体的例子:

  1. # 创建类
  2. class F1:
  3. def bar(self):
  4. print("bar")
  5. def Hello(self, name):
  6. self.name = name
  7. print ("i am %s" %self.name)
  8. # 根据类F1创建对象obj
  9. obj = F1()
  10. obj.bar() #执行bar方法
  11. obj.Hello("xuliangwei") #执行hello方法

你在这里是不是有疑问了?使用函数式编程和面向对象编程方式来执行一个“方法”时函数要比面向对象简便

面向对象:【创建对象】【通过对象执行方法】
函数编程:【执行函数】
观察上述对比答案则是肯定的,然后并非绝对,场景的不同适合其的编程方式也不同。
总结:函数式的应用场景 -->各个函数之间是独立且无共用的数据

1.2面向对象三大特征

面向对象的三大特性:封装、继承、多态。

1.2.1封装

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

1.封装

  1. # 创建类
  2. class F2:
  3. #init构造函数,在实例化时做一些类的初始化的工作
  4. def __init__(self, name, age):
  5. self.name = name
  6. self.age = age
  7. # 根据类F2创建对象
  8. # 自动执行F2类的__init__方法
  9. obj1 = F2('xulaingwei', 18)
  10. #将xuliangwei和18分别封装到obj1和self的name、age属性中
  11. # 根据类F2创建对象
  12. # 自动执行F2类的__init__方法
  13. obj2 = F2('jack', 58)
  14. #将Jack和58分别封装到obj2和self的name、age属性中

2.调用封装的内容时,有两种调用情况:

  1. # 创建类
  2. class F2:
  3. def __init__(self, name, age):
  4. self.name = name
  5. self.age = age
  6. obj1 = F2('xulaingwei', 18)
  7. print(obj1.name,obj1.age) #直接调用obj1对象的name age属性
  8. obj2 = F2('jack', 58)
  9. print(obj2.name,obj2.age) #直接调用obj2对象的name age属性
  1. # 创建类
  2. class F2():
  3. def __init__(self,name,age):
  4. self.name = name
  5. self.age = age
  6. def detail(self):
  7. print (self.name)
  8. print (self.age)
  9. obj1 = F2('xuliangwei',18)
  10. obj1.detail() # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 xuliangwei ;self.age 是 18
  11. obj2 = F2('jack',58)
  12. obj2.detail() # Python默认会将obj2传给self参数,即:obj2.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 jack ;self.age 是 58

综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容。

练习题:在终端输出如下信息

  1. 小明,10岁,男,上山去砍柴
  2. 小明,10岁,男,开车去东北
  3. 小明,10岁,男,最爱大保健
  4. 老李,90岁,男,上山去砍柴
  5. 老李,90岁,男,开车去东北
  6. 老李,90岁,男,最爱大保健

函数式编程方法:

  1. def kanchai (name, age, gender):
  2. print ("%s,%s岁,%s,上山砍柴" %(name,age,gender))
  3. def qudongbei(name, age, gender):
  4. print ("%s,%s岁,%s,开车去东北" %(name, age, gender))
  5. def dabaojian(name, age, gender):
  6. print ("%s,%s岁,%s,最爱大保健" %(name, age, gender))
  7. kanchai('小明', 10, '男')
  8. qudongbei('小明', 10, '男')
  9. dabaojian('小明', 10, '男')
  10. kanchai('老李', 90, '男')
  11. qudongbei('老李', 90, '男')
  12. dabaojian('老李', 90, '男')

面向对象编程方法:

  1. #!/usr/bin/env python
  2. # Author:xuliangwei
  3. class F1:
  4. #init构造函数
  5. #在实例化时做一些类的初始化的工作
  6. def __init__(self,name,age,gender):
  7. self.name = name
  8. self.age = age
  9. self.gender = gender
  10. def kanchai(self):
  11. print("%s,%s岁,%s,上山砍柴" %(self.name, self.age, self.gender))
  12. def qudongbei(self):
  13. print("%s,%s岁,%s,开车去东北" %(self.name,self.age,self.gender))
  14. def dabaojian(self):
  15. print("%s,%s岁,%s,最爱大保健" %(self.name, self.age, self.gender))
  16. obj1 = F1('小明', 10, '男')
  17. obj1.kanchai()
  18. obj1.qudongbei()
  19. obj1.dabaojian()
  20. obj2 = F1('老李', 90, '男')
  21. obj2.kanchai()
  22. obj2.qudongbei()
  23. obj2.dabaojian()
  24. #上述对比可以看出,如果使用函数式编程,需要在每次执行函数时传入相同的参数,如果参数多的话,又需要粘贴复制了... ;而对于面向对象只需要在创建对象时,将所有需要的参数封装到当前对象中,之后再次使用时,通过self间接去当前对象中取值即可。

1.2.2继承

一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承。
例如:
  猫可以:喵喵叫、吃、喝、拉、撒
  狗可以:汪汪叫、吃、喝、拉、撒

  1. class mao:
  2. def 喵喵叫(self):
  3. print ("喵喵叫")
  4. def 吃(self):
  5. print ("吃")
  6. def 喝(self):
  7. print ("喝")
  8. def 拉(self):
  9. print ("拉")
  10. def 撒(self):
  11. print ("撒")
  12. class gou:
  13. def 汪汪叫(self):
  14. print ("汪汪叫")
  15. def 吃(self):
  16. print ("吃")
  17. def 喝(self):
  18. print ("喝")
  19. def 拉(self):
  20. print ("拉")
  21. def 撒(self):
  22. print ("撒")

上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。
如果使用”继承“的思想,如下实现:
   动物:吃、喝、拉、撒
   猫:喵喵叫(猫继承动物的功能)
   狗:汪汪叫(狗继承动物的功能)

  1. class Animal:
  2. def eat(self):
  3. print ("%s 吃 " % (self.name))
  4. def drink(self):
  5. print ("%s 喝 " % (self.name))
  6. def shit(self):
  7. print ("%s 拉 " % (self.name))
  8. def pee(self):
  9. print ("%s 撒 " % (self.name))
  10. class Cat(Animal):
  11. def __init__(self, name):
  12. self.name = name
  13. self.breed = '猫'
  14. def cry(self):
  15. print ('%s 喵喵叫-->%s' %(self.name,self.breed))
  16. class Dog(Animal):
  17. def __init__(self, name):
  18. self.name = name
  19. self.breed = '狗'
  20. def cry(self):
  21. print ('%s 汪汪叫-->%s' %(self.name,self.breed))
  22. # ######### 执行 #########
  23. c1 = Cat('小黑猫')
  24. c1.cry()
  25. c1.eat()
  26. c1.drink()
  27. d1 = Dog('小瘦狗')
  28. d1.cry()
  29. d1.eat()
  30. d1.drink()
  31. d1.pee()
  32. ##对于面向对性爱那个的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法.(避免重复造轮子)
  33. 注:除了子类和父类的称谓,你可能看到过 派生类 基类 ,他们与子类和父类只是叫法不同而已。

1.2.3多级继承

1.Python的类可以继承多个类,Java和C#中则只能继承一个类
2.Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
深度优先以及广度优先

python2经典类,按深度优先来继承的。
python2新式类,按广度有限来继承的。
python3经典类,新式类 是按广度优先来继承的。

  1. #经典类写法
  2. class A1:
  3. pass
  4. class A2(A1):
  5. pass

经典类,实践测试

  1. class D:
  2. def bar(self):
  3. print ('D.bar')
  4. class C(D):
  5. def bar(self):
  6. print ('C.bar')
  7. class B(D):
  8. def bar(self):
  9. print ('B.bar')
  10. class A(B, C):
  11. def bar(self):
  12. print ('A.bar')
  13. a = A()
  14. a.bar()
  15. # 执行bar方法时
  16. # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
  17. # 所以,查找顺序:A --> B --> D --> C
  18. # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
  1. #新式类写法
  2. class N1(object):
  3. pass
  4. class N2(N1):
  5. pass

新式类,实践测试

  1. class D(object):
  2. def bar(self):
  3. print ('D.bar')
  4. class C(D):
  5. def bar(self):
  6. print ('C.bar')
  7. class B(D):
  8. def bar(self):
  9. print ('B.bar')
  10. class A(B, C):
  11. def bar(self):
  12. print ('A.bar')
  13. a = A()
  14. a.bar()
  15. # 执行bar方法时
  16. # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
  17. # 所以,查找顺序:A --> B --> C --> D
  18. # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了

经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错

新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错

1.2.4广度优先和深度优先

下图,其深度优先遍历顺序为 1->2->4->8->5->3->6->7
广度优先
下图,其广度优先算法的遍历顺序为:1->2->3->4->5->6->7->8
深度优先

1.2.5多态

多态是面向对象的重要特性,简单点说:一个接口,多种实现。

  1. class F1:
  2. pass
  3. class S1(F1):
  4. def show(self):
  5. print ('S1.show')
  6. class S2(F1):
  7. def show(self):
  8. print ('S2.show')
  9. def Func(obj):
  10. print (obj.show())
  11. s1_obj = S1()
  12. Func(s1_obj)
  13. s2_obj = S2()
  14. Func(s2_obj)