# 面向对象
# 面向过程 VS 面向对象
- 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的思想就好比是精心设计好一条流水线,考虑周全什么时候处理什么东西。
- 优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
- 缺点是:一套流水线就是用来解决一个问题,代码牵一发而动全身。
- 应用场景:
- 一旦完成基本很少改变的场景,著名的例子有 Linux 內核,git,以及 Apache HTTP Server 等。
- 面向对象 OOP,是一种程序设计思想。OOP 把对象作为程序的基本单元,并且一个对象包含数据和操作数据的方法。
- 面向对象的程序设计的核心是对象,要理解对象为何物,必须把自己当成上帝(上帝式思维)。上帝眼里世间存在的万物皆为对象。
- 形象化场景设计:
- 面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐," 如来” 想了想解决这个问题需要四个人(对象):唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法)。然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。“如来” 根本不会管师徒四人按照什么流程去取,只关心最后结果是否可以实现。
- 因此面向对象的核心思想就是使用一个有一个的对象来完成某件具体是事件,且不用关心完成的具体过程!
- 面向对象的优点:面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使他人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
- 应用场景:需求经常变化的软件,如互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
# 类和实例
类,英文名字 Class,有 “类别”,“分类”,“聚类” 的意思。
必须牢记类是抽象的模板,用来描述具有相同属性和方法的对象的集合,比如 Animal 类。
而实例是根据类创建出来的一个个具体的 “对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
- Python 使用 class 关键字来定义类,其基本结构如下:
1 2 3 4 5 6 7 8 9 10 11
| class Student(): classroom = '101' address = 'beijing' def __init__(self, name, age): self.name = name self.age = age def print_age(self): print('%s: %s' % (self.name, self.age))
|
- 对象的创建
- 可以通过调用类的实例化方法(有的语言中也叫初始化方法或构造函数)来创建一个类的实例(对象)。
- Python 提供了一个
def __init__(self):
的实例化机制。任何一个类中,名字为 __init__
的方法就是类的实例化方法,具有 __init__
方法的类在实例化的时候,会自动调用该方法,并传递对应的参数。
1 2
| zhangsan = Student('zhangsan',20) lisi = Student('lisi',30)
|
# 实例变量和类变量
- 实例变量
- 实例变量指的是实例(对象)本身拥有的变量。Student 类中
__init__
方法里的 name 和 age 就是两个实例变量。
- 通过实例名加圆点的方式调用实例变量(可以通过对象名打点的方式去调用 / 访问属于对象的成员)。
1 2 3 4 5 6 7 8 9 10 11 12
| class Student(): def __init__(self,i_name,i_age): self.name = i_name self.age = i_age
s1 = Student('zhangsan',20) s2 = Student('lisi',25)
print(s1.name,s1.age) print(s2.name,s2.age)
|
- 类变量
- 定义在类中,方法之外的变量,称作类变量。类变量是所有实例公有的变量,每一个实例都可以访问类变量。
- 在 Student 类中,classroom 和 address 两个变量就是类变量。可以通过类名或者实例名加圆点的方式访问类变量,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Student(): address = 'Beijing' classroom = 167 def __init__(self,i_name,i_age): self.name = i_name self.age = i_age
s1 = Student('zhangsan',20) s2 = Student('lisi',25)
print(s1.name,s1.age) print(s2.name,s2.age)
|
- 类变量的特性:
- 所有的类变量是可以通过类名或者对象名打点的方式访问/调用的。
-
1 2 3
| print(s1.address,s1.classroom)
print(Student.address,Student.classroom)
|
- 类变量是可以被所有的对象公用的
-
1 2
| print(s1.address,s1.classroom) print(s2.address,s2.classroom)
|
# 类的方法
Python 的类中包含实例方法、静态方法和类方法三种方法。区别在于传入的参数和调用方式不同。
在类的内部,使用 def
关键字来定义一个方法。
# 实例方法
- 类的实例方法由实例调用,至少包含一个 self 参数,且为第一个参数。执行实例方法时,会自动将调用该方法的实例赋值给 self。
self
代表的是类的实例,而非类本身。 self
不是关键字,而是 Python 约定成俗的命名,你完全可以取别的名字,但不建议这么做。
- 例如,我们前面 Student 类中的 print_age () 就是实例方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Student(): classroot = 167 def __init__(self,name,age): self.name = name self.age = age
def study(self,book): print('正在学习的书籍是:',book)
s = Student('zhangsan',20)
s.study('高等数学')
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Student(): classroot = 167 def __init__(self,name,age): self.name = name self.age = age def study(self,book): self.play('足球') print('正在学习的书籍是:',book) def play(self,b): print('正在玩的项目是:',b)
s = Student('zhangsan',20) s.study('高等数学')
|
- self就是study方法的调用者(对象),self表示的就是调用该方法的对象的引用
对象之间的交互:设计人狗大战游戏,让他们真正的打一架。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| class Person(): def __init__(self,name): self.name = name self.blood = 100 self.gjl = 10
def hitDog(self,dog): dog.blood -= self.gjl def showBlood(self): print('%s,剩下的血量为:%d'%(self.name,self.blood))
class Dog(): def __init__(self,name): self.name = name self.blood = 50 self.gjl = 5
def hitPerson(self,p): p.blood -= self.gjl def showBlood(self): print('%s,剩下的血量为:%d'%(self.name,self.blood))
p1 = Person('zhangsan') p2 = Person('lisi')
d1 = Dog('doudou') d2 = Dog('huanghuang')
p2.hitDog(d1) d1.showBlood()
d2.hitPerson(p1) p1.showBlood()
|
# 静态方法
- 静态方法由类调用,无默认参数。将实例方法参数中的 self 去掉,然后在方法定义上方加上 @staticmethod,就成为静态方法。
- 静态方法属于类,和实例无关。建议只使用类名。静态方法的调用方式。(虽然也可以使用实例名。静态方法的方式调用)
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Obj(): def __init__(self): pass @staticmethod def staticFunc(name): print('我是静态方法!,我有一个普通参数:',name)
Obj.staticFunc('bobo') o = Obj() o.staticFunc('bobo')
|
# 类方法
- 类方法由类调用,采用 @classmethod 装饰,至少传入一个 cls(代指类本身,类似 self)参数。
- 执行类方法时,自动将调用该方法的类赋值给 cls。建议只使用类名。类方法的调用方式。(虽然也可以使用实例名。类方法的方式调用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Obj(): f = 'classVar' def __init__(self): pass @classmethod def classFunc(cls): print('我是类方法!必要参数cls的值为:',cls) print('类变量的值为:',cls.f)
o = Obj() o.classFunc()
Obj.classFunc()
|
- 在类方法中是否可以调用实例方法和实例变量?
- 不能,因为实例变量和实例方法只可以通过对象名访问,但是在类方法内部不存在对象名。
- 在实例方法中是否可以调用类方法和类变量?
# 面向对象的组合用法
- 组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
- 思路设计:一个学生会有一步手机,学生使用手机看电影。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Student(): def __init__(self): self.phone = Phone('霸王别姬')
class Phone(): def __init__(self,movie_name): self.movie_name = movie_name
def playMovie(self): print('手机正在播放的电影是:',self.movie_name)
s1 = Student() s1.phone.playMovie()
|
# 继承
- 继承来源于现实世界:
- 一个最简单的例子就是孩子会具有父母的一些特征,即每个孩子都会继承父亲或者母亲的某些特征,当然这只是最基本的继承关系,现实世界中还存在着更复杂的继承。
- 在 OOP 程序设计中,当我们定义一个新类的时候,新的类称为子类(Subclass),而被继承的类称为基类、父类或超类(Base class、Super class)。
- 继承最大的好处是子类获得了父类的全部变量和方法的同时,又可以根据需要进行修改、拓展。其语法结构如下:
1 2
| class Foo(superA, superB,superC....): pass
|
继承示例代码:子类可以继承到父类中所有的成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class Father(): address = 'Beijing' def __init__(self,fistName,hobby): self.firstName = fistName self.hobby = hobby def get_xxx(self): print('我是Father的实例方法') @classmethod def classFunc(cls): print('我是Father类的类方法') @staticmethod def staticFunc(): print('我是Father类的静态方法')
class Son(Father): pass
s = Son('zhang','smoke')
print(Son.address)
print(s.firstName,s.hobby)
s.get_xxx()
Son.classFunc()
Son.staticFunc()
|
# 派生
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Father(): address = 'Beijing' def __init__(self,fistName,hobby): self.firstName = fistName self.hobby = hobby def get_xxx(self): print('我是Father的实例方法') @classmethod def classFunc(cls): print('我是Father类的类方法') @staticmethod def staticFunc(): print('我是Father类的静态方法')
class Son(Father): def sing(self): print('子类的实例方法sing')
s = Son('zhang','smoke') s.sing()
|
# 方法的重写
- 重写:子类继承到父类的方法,如果满足不了子类的需求,则子类可以重写从父类中继承到的方法。重写父类方法有两种方式:1 完全重写,2 部分重写
- 1. 完全重写:完全重新将父类的方法进行的全新的定义 / 实现(毫无保留父类方法原始的功能)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Father(): def __init__(self,firstName): self.firstName = firstName
def hobby(self): print('我喜欢读书,运动和跳舞!')
class Son(Father): def hobby(self): print('我喜欢吃鸡,王者!')
s = Son('zhang') s.hobby()
|
- 2. 部分重写:在父类方法功能实现的基础上新增了其他操作 / 功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Father(): def __init__(self,firstName): self.firstName = firstName
def hobby(self): print('我喜欢读书,运动和跳舞!')
class Son(Father1): def hobby(self): super().hobby() print('我喜欢吃鸡,王者!')
s = Son('zhang') s.hobby()
|
- super () 只可以作用在类的内部,然后表示的是父类对象的引用。
- super 函数
- 如果你想强制调用父类的成员该如何实现呢?使用 super () 函数!这是一个非常重要的函数,最常见的就是通过 super 调用父类的实例化方法
__init__
!
- 语法:
super(子类名, self).方法名()
,需要传入的是子类名和 self,调用的是父类里的方法,按父类的方法需要传入参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Father(): def __init__(self,firstName): self.firstName = firstName
def hobby(self): print('我喜欢读书,运动和跳舞!')
class Son(Father): def __init__(self,firstName,classroom,score): super().__init__(firstName) self.classroom = classroom self.score = score def hobby(self): super().hobby() print('我喜欢吃鸡,王者!')
s = Son('zhang',102,100) s.hobby()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Father1(): def hobby(self): print('我是Father1,我喜欢唱歌!') class Father2(): def hobby(self): print('我是Father2,我喜欢跳舞!')
class Son(Father1,Father2): def hobby(self): super().hobby() print('我是Son,我喜欢吃鸡!') s = Son() s.hobby()
|