Day7 初识面向对象,面向对象之继承与派生

Posted 思维无界限

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day7 初识面向对象,面向对象之继承与派生相关的知识,希望对你有一定的参考价值。

一、面向对象引言

  一、面向对象的程序设计的由来

  详述见:http://www.cnblogs.com/linhaifeng/articles/6428835.html 

  二、面向对象引子

  写一个简单程序:人狗大战

角色:人、狗
人角色特性:攻击力、生命值、名字、等级
狗角色特性:攻击力、生命值、名字、品种

  1、用函数实现功能:角色和技能分别定义不同的函数

 1 def person(attack,life_value,name,level):
 2     person_dic = {\'attack\':attack,
 3                   \'life_value\':life_value,
 4                   \'name\':name,
 5                   \'level\':level}
 6     return person_dic
 7 def dog(attack,life_value,name,level):
 8     dog_dic = {\'attack\':attack,
 9                \'life_value\':life_value,
10                \'name\':name,
11                \'type\':type}
12     return dog_dic
13 
14 def attack(person_d,dog_d):
15     print(\'%s 打了 %s\' %(person_d[\'name\'],dog_d[\'name\']))
16     dog_d[\'life_value\'] -= person_d[\'attack\']
17 
18 def bite(dog_d,person_d):
19     print(\'%s 咬了 %s\' %(dog_d[\'name\'],person_d[\'name\']))
20     person_d[\'life_value\'] -= dog_d[\'attack\']
定义角色和技能
1 alex = person(100,1000,\'alex\',2)
2 egg = dog(200,2000,\'egon\',\'金毛\')
3 print(egg[\'life_value\'])
4 attack(alex,egg)
5 print(egg[\'life_value\'])
6 bite(egg,alex)
7 print(alex[\'life_value\'])
角色实例化,互相攻击

  弊端:某一角色误操作,可以调用其他角色的技能,这是明显的bug,必须修正:角色只能调用自己的技能

  2、改进,角色只能调用自己的技能

 1 def person(attack,life_value,name,level):
 2     \'\'\'
 3     将独有的特性放到内部,只能自己调用
 4     :param attack:
 5     :param life_value:
 6     :param name:
 7     :param level:
 8     :return:
 9     \'\'\'
10     def atk( dog_d):
11         print(\'%s 打了 %s\' % (name, dog_d[\'name\']))
12         dog_d[\'life_value\'] -= attack
13     person_dic = {\'attack\':attack,
14                   \'life_value\':life_value,
15                   \'name\':name,
16                   \'level\':level,
17                   \'atk\':atk}
18     return person_dic
19 def dog(attack,life_value,name,level):
20     def bite(person_d):
21         print(\'%s 咬了 %s\' % (name, person_d[\'name\']))
22         person_d[\'life_value\'] -= attack
23     dog_dic = {\'attack\':attack,
24                \'life_value\':life_value,
25                \'name\':name,
26                \'type\':type,
27                \'bite\':bite}
28     return dog_dic
定义角色和技能(技能放在角色函数内)
1 alex = person(100,1000,\'alex\',2)
2 egg = dog(200,2000,\'egon\',\'金毛\')
3 print(egg[\'life_value\'])
4 alex[\'atk\'](egg)
5 print(egg[\'life_value\'])
6 egg[\'bite\'](alex)
7 print(alex[\'life_value\'])
角色实例化,操作(技能不能互相调用了)

  三、面向对象与面向过程的区别

  1、面向对象概述、优缺点及应用场景

面向过程的程序设计的核心是过程二字(流水线式思维),过程即解决问题的步骤。流程需要精心设计,是一种机械式的思维模式。

优点:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。

缺点:一套流水线或者流程就是来解决一个问题,无法满足个性化需求,牵一发而动全身。

应用场景:适用于一旦流程确定,很少改变的场景。例如:Linux内核,git,以及shell相关管理脚本。

  2、面向对象概述、优缺点及应用场景

面向对象的程序设计的核心是对象二字。

对象是特征和技能的综合体,基于面向对象设计程序就好比在创建一个世界,你就是世界的上帝,存在的皆为对象,不存在的也可以创建出来。

与面向过程的机械式思维方式形成鲜明对比,面向对象更加注重对显示世界的模拟,是一种“上帝式”的思维模式。

优点:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中。如对游戏中一个人物参数的特征和技能修改都很容易。

缺点:1、复杂性高。编程的复杂性远高于面向过程,不了解面向对象而立即基于它设计程序,极容易出现过度设计的问题。一些扩展性要求低的场景使用面向对象会徒增编程难度,例如shell脚本。

   2、可控性差。无法像面对过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果。面向对象的程序一旦开始就由对象之间的交互解决问题,即便设计者也无法准确地预测最终结果。例如:新增一个游戏人物,在对战的过程中极容易出现阴霾的技能,一刀砍死3个人。

应用场景:需求经常变化的软件,一般需求的变化都集中在用户层:互联网应用,企业内部软件,游戏等。

  思路一:只关心某一个对象变成抽象规范了一类对象

  思路二:当多个函数都需要传递同样的多个参数的时候,考虑面向对象的思想

面想对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计知识用来解决扩展性。

二、初识面向对象

Python中一切皆为对象,(数据)类型的本质就是类。例如:字典等。

在Python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘’,对象则是这一类事物中具体的一个。

 一、类的相关知识

  1、初始类

定义函数时,函数名首字母小写。

1 def functionName(args):
2     \'\'\'
3     注释
4     :param args:
5     :return:
6     \'\'\'
7     函数体
函数定义阶段

定义类与定义函数类似。类名首字母要大写。

1 class Date:
2     \'类的文档字符串\'
3     类体
定义类

  2、类有两种作用:属性引用和实例化

属性引用方法:类名.属性

 1 class Person:
 2     rol = \'\'         #数据属性、静态属性、类属性
 3     country = \'中国\'
 4     def __init__(self,name,age,life_value): #初始化方法
 5         # self.__dict__[\'name\'] = name
 6         self.name = name       #属性、对象属性
 7         self.theage = age
 8         self.life_value = life_value
 9 
10     def attack(self):  #函数属性、动态属性、方法
11         #self只是一个形式参数,可以叫其他名字,但正常没人会这样
12         #self是水性杨花,那个对象调这个方法,self就是那个对象
13         print(\'attack方法被%s执行了\'%self.name)
14 print(Person.rol)  ##属性引用

实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征。

语法:对象名 = 类名(参数)

执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法

 1 class Person:
 2     rol = \'\'         #数据属性、静态属性、类属性
 3     country = \'中国\'
 4     def __init__(self,name,age,life_value): #初始化方法
 5         # self.__dict__[\'name\'] = name
 6         self.name = name       #属性、对象属性
 7         self.theage = age
 8         self.life_value = life_value
 9 
10     def attack(self):  #函数属性、动态属性、方法
11         #self只是一个形式参数,可以叫其他名字,但正常没人会这样
12         #self是水性杨花,那个对象调这个方法,self就是那个对象
13         print(\'attack方法被%s执行了\'%self.name)
14 print(Person.rol)
15 alex=Person(\'alex\',38,1000)
16 egg=Person(\'egon\',18,2000)

查看属性和调用方法

print(alex.name)  ##查看属性信息 对象名.属性名
print(alex.attack()) #调用方法:对象名.方法名()

self:在实例化时自动将对象/实例本身传给__init__的第一个参数。可以自定义个名字,但一般情况采用默认。

类的属性补充

 1 一:我们定义的类的属性到底存到哪里了?有两种方式查看
 2 dir(类名):查出的是一个名字列表
 3 类名.__dict__:查出的是一个字典,key为属性名,value为属性值
 4 二:特殊的类属性
 5 类名.__name__# 类的名字(字符串)
 6 类名.__doc__# 类的文档字符串
 7 类名.__base__# 类的第一个父类(在讲继承时会讲)
 8 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
 9 类名.__dict__# 类的字典属性
10 类名.__module__# 类定义所在的模块
11 类名.__class__# 实例对应的类(仅新式类中)
类属性的补充

  二、对象的相关知识

对象是关于类而上实际存在的一个例子。

对象/实例只有一种作用:属性引用

注意:

1. 站的角度不同,定义出的类是截然不同的,详见面向对象实战之需求分析

2. 现实中的类并不完全等于程序中的类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类...... 

3. 有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类

面向对象的小结

 1 class 类名:
 2     def __init__(self,参数1,参数2):
 3         self.对象的属性1 = 参数1
 4         self.对象的属性2 = 参数2
 5 
 6     def 方法名(self):pass
 7 
 8     def 方法名2(self):pass
 9 
10 对象名 = 类名(1,2)  #对象就是实例,代表一个具体的东西
11                   #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
12                   #括号里传参数,参数不需要传self,其他与init中的形参一一对应
13                   #结果返回一个对象
14 对象名.对象的属性1   #查看对象的属性,直接用 对象名.属性名 即可
15 对象名.方法名()     #调用类中的方法,直接用 对象名.方法名() 即可

 三、属性查找

 创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字为类的属性。

创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性

  1、类的属性分类和特点:

  1. 数据(静态)属性:是所有对象共享的,id都一样
  2. 函数(动态)属性:绑定给对象用的obi.method称为绑定方法,内存地址都一样

id是Python的实现机制,并不能真实反映内存地址,若有内存地址,还是以内存地址为准。

obj.name会先从obj自己的名称空间里找name,找不到则去类中找,找不到就找父类......最后都找不到就抛出异常。

  2、练习

编写一个学生类,产生一堆学生对象,要求有一个计数器(属性),统计总共有多少个对象。

对于任何数据类型的静态属性,它的修改操作尽量用类名;尤其是对于不可变数据类型,修改必须用类名

class Student:
    count = 0
    def __init__(self,name,sex,age):
        self.name = name
        self.sex = sex
        self.age = age
        Student.count += 1
alex = Student(\'alex\',\'male\',28)
egon = Student(\'egon\',\'male\',18)
print(alex.count)
print(egon.count)
计数器

  四、对象之间的交互

  1、实例:人狗大战

 1 class Dog:
 2     def __init__(self,name,type,aggr):
 3         self.name = name
 4         self.dog_type = type
 5         self.aggr = aggr
 6         self.life_value = 2000
 7 
 8     def bite(self,person_obj):  #self==egg,person_obj=alex
 9         #属性的变化
10         print(\'%s咬了%s\'%(self.name,person_obj.name))
11         person_obj.life_value -= self.aggr
12 
13 class Person:
14     rol = \'\'         #数据属性、静态属性、类属性
15     country = \'中国\'
16     def __init__(self,name,age,life_value): #初始化方法
17         self.name = name       #属性、对象属性
18         self.theage = age
19         self.life_value = life_value
20         self.aggr = 1
21 
22     def attack(self,dog_obj):#函数属性、动态属性、方法
23         print(\'%s攻击了%s\'%self.name,dog_obj)
24         dog_obj.life_value -= self.aggr
25 
26 alex = Person(\'alex\',38,500)
27 egg = Dog(\'egon\',\'二哈\',20)
28 print(alex.life_value)
29 egg.bite(alex)   #Dog.bite(egg,alex)
30 print(alex.life_value)

  2、练习

练习一:已知一个圆的半径,求圆的周长和面积。(使用return)

  能用return的返回结果的,不用print

 1 from math import pi
 2 class Circle():
 3     def __init__(self,r):
 4         self.r=r
 5     def perimeter(self):
 6         return self.r*pi*2
 7 
 8     def area(self):
 9         return pi*self.r**2
10 
11 circle1 = Circle(5)
12 print(circle1.perimeter())
13 print(circle1.area())
计算圆的周长和面积

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

小明,10岁,男,上山去砍柴
小明,10岁,男,开车去东北
小明,10岁,男,最爱大保健
老李,90岁,男,上山去砍柴
老李,90岁,男,开车去东北
老李,90岁,男,最爱大保健
打印信息
 1 class Person:
 2     def __init__(self,name,age,sex):
 3         self.name = name
 4         self.age = age
 5         self.sex = sex
 6     def shangshan(self):
 7         print(\'%s,%s,%s,上山去砍柴\' %(self.name,self.age,self.sex))
 8     def kaiche(self):
 9         print(\'%s,%s,%s,开车去东北\' %(self.name,self.age,self.sex))
10     def zuiai(self):
11         print(\'%s,%s,%s,最爱大保健\' %(self.name,self.age,self.sex))
12 
13 xiaoming = Person(\'小明\',10,\'\')
14 laoli = Person(\'老李\',90,\'\')
15 xiaoming.shangshan()
16 xiaoming.kaiche()
17 xiaoming.zuiai()
代码实现

  五、面向对象的组合用法

  1、组合简介

软件重用的重要方式,除了继承以外,还有一种方式就是组合。

组合指的是在一个类中以另外一个类的对象作为数据属性,称为类的组合。

组合描述的是类之间的所属关系。例如:人有生日等等

  2、继承与组合

组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,

1.继承的方式

通过继承建立了派生类与基类之间的关系,它是一种\'\'的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人

2.组合的方式

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3..
 1 class People:
 2     def __init__(self,name,age,sex):
 3         self.name=name
 4         self.age=age
 5         self.sex=sex
 6 
 7 class Course:
 8     def __init__(self,name,period,price):
 9         self.name=name
10         self.period=period
11         self.price=price
12     def tell_info(self):
13         print(\'<%s %s %s>\' %(self.name,self.period,self.price))
14 
15 class Teacher(People):
16     def __init__(self,name,age,sex,job_title):
17         People.__init__(self,name,age,sex)
18         self.job_title=job_title
19         self.course=[]
20         self.students=[]
21 
22 
23 class Student(People):
24     def __init__(self,name,age,sex):
25         People.__init__(self,name,age,sex)
26         self.course=[]
27 
28 
29 egon=Teacher(\'egon\',18,\'male\',\'沙河霸道金牌讲师\')
30 s1=Student(\'牛榴弹\',18,\'female\')
31 
32 python=Course(\'python\',\'3mons\',3000.0)
33 linux=Course(\'python\',\'3mons\',3000.0)
34 
35 #为老师egon和学生s1添加课程
36 egon.course.append(python)
37 egon.course.append(linux)
38 s1.course.append(python)
39 
40 #为老师egon添加学生s1
41 egon.students.append(s1)
42 
43 
44 #使用
45 for obj in egon.course:
46     obj.tell_info()
例子:继承与组合

 3、举例:

  实例一:人类和生日类组合