python---面向对象

# 面向对象

# 面向过程 VS 面向对象

  • 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的思想就好比是精心设计好一条流水线,考虑周全什么时候处理什么东西。
  • 优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
  • 缺点是:一套流水线就是用来解决一个问题,代码牵一发而动全身。
  • 应用场景:
    • 一旦完成基本很少改变的场景,著名的例子有 Linux 內核,git,以及 Apache HTTP Server 等。
  • 面向对象 OOP,是一种程序设计思想。OOP 把对象作为程序的基本单元,并且一个对象包含数据和操作数据的方法。
  • 面向对象的程序设计的核心是对象,要理解对象为何物,必须把自己当成上帝(上帝式思维)。上帝眼里世间存在的万物皆为对象。
  • 形象化场景设计:
    • 面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐," 如来” 想了想解决这个问题需要四个人(对象):唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法)。然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。“如来” 根本不会管师徒四人按照什么流程去取,只关心最后结果是否可以实现。
    • 因此面向对象的核心思想就是使用一个有一个的对象来完成某件具体是事件,且不用关心完成的具体过程!
  • 面向对象的优点:面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使他人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
  • 应用场景:需求经常变化的软件,如互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

# 类和实例

,英文名字 Class,有 “类别”,“分类”,“聚类” 的意思。

必须牢记类是抽象的模板,用来描述具有相同属性和方法的对象的集合,比如 Animal 类。

实例是根据类创建出来的一个个具体的 “对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

  • Python 使用 class 关键字来定义类,其基本结构如下:
1
2
class 类名(): #一般类名首字母是大写
pass
  • 下面是一个学生类:
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():
#init称为构造方法
def __init__(self,i_name,i_age):
#只要定义在init方法内部的变量就是【实例/对象变量】
self.name = i_name #self.name就是定义的实例变量,name是init方法的参数值
self.age = i_age #self.age就是定义的实例变量,age就是init方法的参数值

s1 = Student('zhangsan',20) #调用Student类中的init这个构造方法
s2 = Student('lisi',25)
#根据对象的引用访问对象的实例变量
print(s1.name,s1.age) #访问s1对象的name和age这两个实例变量
print(s2.name,s2.age) #访问s2对象的name和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

#init称为构造方法
def __init__(self,i_name,i_age):
#只要定义在init方法内部的变量就是【实例/对象变量】
self.name = i_name #self.name就是定义的实例变量,name是init方法的参数值
self.age = i_age #self.age就是定义的实例变量,age就是init方法的参数值

s1 = Student('zhangsan',20) #调用Student类中的init这个构造方法
s2 = Student('lisi',25)
#根据对象的引用访问对象的实例变量
print(s1.name,s1.age) #访问s1对象的name和age这两个实例变量
print(s2.name,s2.age) #访问s2对象的name和age这两个实例变量
  -  类变量的特性: 
     -  所有的类变量是可以通过类名或者对象名打点的方式访问/调用的。 
     -  
1
2
3
print(s1.address,s1.classroom) #通过对象名可以访问类变量(不推荐)
#通过类名可以访问类变量(推荐)
print(Student.address,Student.classroom)
     -  类变量是可以被所有的对象公用的 
     -  
1
2
print(s1.address,s1.classroom) #通过s1对象访问类变量
print(s2.address,s2.classroom) #通过s2对象访问类变量

# 类的方法

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

#实例方法:self不是python的关键字,实例方法的第一个参数也可以叫其他的名字,但是约定俗成叫做self。
#注意:实例方法只可以通过对象调用。
def study(self,book):#self是不需要手动给其传值
print('正在学习的书籍是:',book)

s = Student('zhangsan',20) #调用构造方法
#只给除了self其他的参数传值
s.study('高等数学')
  • 实例方法中的第一个参数 self 到底是什么鬼?
    • 想要在一个实例方法内部调用另一个实例方法?
      • 核心:实例方法只可以被对象调用
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就是study方法的调用者(对象)
#注意:在study方法内部调用play方法,如何实现?
self.play('足球') #实例方法必须使用对象调用
#self表示的就是调用该方法的对象的引用
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参数表示的是狗对象
#人打狗后,需要让狗的血量减去人的攻击力
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就是狗攻击的人那个对象
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) #lisi去攻击doudou这条狗
d1.showBlood() #查看被攻击后的狗还剩下多少血量

d2.hitPerson(p1) #huanghuang去攻击zhangsan
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): #类方法必须要有一个cls的参数,且作为第一个参数
#cls也不是python的关键字,cls也可以写作其他的形式,比如:name,self
print('我是类方法!必要参数cls的值为:',cls)
print('类变量的值为:',cls.f) #类名访问类变量
#cls表示的是当前类

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):
#将创建好的手机对象赋值给了phone这个实例变量
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
  • Python 支持多父类的继承机制。

继承示例代码:子类可以继承到父类中所有的成员

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类的静态方法')
#Son继承了Father这个类
#Son子类,Father父类
class Son(Father):
pass

#1.子类可以继承到父类的构造方法
s = Son('zhang','smoke') #调用子类的构造方法,子类是可以继承到父类的构造方法
#2.子类可以继承到父类的类变量
print(Son.address)
#3.子类可以继承到父类的实例变量
print(s.firstName,s.hobby)
#4.子类可以继承到父类的实例方法
s.get_xxx()
#5.子类可以继承到父类的类方法
Son.classFunc()
#6.子类可以继承到父类的静态方法
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):
#调用一下父类的hobby方法
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):
#调用一下父类的hobby方法
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):
#思考:现在Son有两个父类,super()表示的是哪一个父类对象?
#输出结果显示super表示的是Father1对象
super().hobby()
print('我是Son,我喜欢吃鸡!')
s = Son()
s.hobby()

python---面向对象
https://rofgd.github.io/2020/10/15/python---面向对象/
作者
ReadPond
发布于
2020年10月15日
许可协议