面向对象
一、面向对象的设计
我们设计的软件很对时候是对现实世界的一个模拟系统,比如我们要设计一个非常简单的文字游戏,如下:
一个动物园有十个房间,里面有两种动物,老虎、羊。老虎的体重是200斤,羊体重100斤。游戏开始之前,在10个房间里面,随机放入老虎和羊。游戏这的目标是要把羊和老虎喂的越重越好。
游戏开始后,系统随机给出房间号。游戏者开始必须弄清楚,每个房间是老虎还是羊,方法是敲房间门,里面的动物都会叫,老虎叫声‘wow!!!’,
羊叫声是‘mie’。动物每叫一次体重减5斤。喂老虎应该输入单词meet,喂羊应该输入单词grass。喂对了体重加10斤。喂错了。体重减少10斤
游戏者需要强记每个房间的动物是什么,以便不需要敲门就可以得知里面的动物是什么,从而该喂什么。这样不断循环。游戏2分钟结束后。看看你喂的动物总体重多少,这个游戏怎么设计?
- 我们以前做的事情距离现实世界比较远,所以设计一般是面向过程的设计,一般是一些代码语句的顺序执行,中间加上一些循环,判断语句。最多是为了可读性,把相同功能点的代码组织成函数,甚至把大块函数通过切割成小块函数来降低系统的复杂度;
- 之前,那样做就可以解决那时候的问题了,但是现在的问题,是个典型的现实世界的模拟的系统,这时候用面向过程的设计思路来设计系统也是不行。只是这个建模的思路通常会比较费解,这时候用面向对象的设计(OOD)是最自然最容易理解。
所谓面向对象的设计(OOD),简单说,就是分析要解决的现实世界的问题,抽象出各种对象,并通过设计对象之间的关系和交互行为来设计系统,因为现实世界,其实就是各种对象和对象之间的关系、交互行为。所以这种设计方式,更容易被人所理解和接受
二、类和实例
面向对象设计里面开始要做的事情,就是寻找要设计的系统里面的对象有哪些。比如上面说的游戏,很明显的对象有老虎、样、房间。对象找到了,接下来就是要确定对象的定义
- 对象的定义,就是要找到对象在这个系统里面的属性和行为。
- 在面向对象的软件设计学里面,我们把对象和他的属性和行为定义叫类定义。
- 类表示了这类对象所共有的行为和属性
2.1.首先,寻找要设计的对象
上面的案例中对象有:老虎、羊、房间、动物园、人
2.2.接下来,确定对象的定义
对象定义说明:
- 对象的定义:定义对象在这个系统里面的的属性和行为,比如老虎这类对象,在这个系统里面的共有属性,行为 属性
- 在面向对象的软件设计中把对象和它的属性和行为的定义叫 类 的定义
- 定义对象其实就是定义对象的属性和行为,类表示了这类对象所共有的属性和行为
- 类是面向对象的设计中最基本的概念,定义了类,就定义了对象的类型(包括在系统里面的属性和行为)
比如老虎这类对象,它们在这个系统里面共有的属性,我们基本上一下子就能想到有体重(缺省200斤)、名称(tiger),而行为呢有叫和吃东西,所有面向对象的语言,天然的提供了 对类的支持。Python语言当然也是面向对象的语言,它可以如下定义一个类
class ClassName(object): 'class documentation string' class_suite
对于上面的老虎 类,可以这样定义
class Tiger: 'a tiger class,which is used in this game as tiger.' classname = 'tiger' def roar(self): print('Wow!!')
其中classname是属性,表示老虎这一类动物的统称叫tiger,叫roar是行为,用一个函数定义表示。对应类的属性和行为的定义,我们下面会详细说明
2.3.实例是类的具体化存在
对象定义好了,我们真正对对象进行使用的时候,必须进行实例化。也就是创建该对象的类的一个实例。用如下方法:
tiger1 = Tiger() tiger2 = Tiger() print(tiger1.classname) print(tiger2.calssname) tiger1.roar()
老虎这个类,描述了:
- 在这个系统里面共有的属性:体重、名称
- 行为有叫、吃食物
每个具体的老虎,是老虎这个类的 实例
- 房间中老虎
类的实例的关系,有点像 模具和根据模具做的东西一样

三、面向对象的语言 vs 面向对象的设计
- 面向对象的设计:是一种设计思路
- 面向对象的语言:提供对面向对象设计的语言支持 Python java js
- 非面向对象的语言也能实现面向对象的设计思路
在任何语言中都可以采用这个面向对象的设计思想进行编码
四、属性
4.1.对象属性
我们刚说到的老虎这类对象,他们在这个系统里面共有的属性,体重和名称。可是我们在上面的类定义的例子里面只定义了名称和属性classname ='tiger',并没有定义体重这个属性,这是因为,这两个属性类型有些不一样。名称tiger是所有老虎对象共同的属性,他们的名称叫老虎(tiger),而体重则每个老虎都不一样。虽然游戏开始的时候缺省的体重是200斤,但随着游戏的进行,每个房间里面的老虎体重可能不同,而名称这个属性,不管游戏进行到什么时候都是一样的。所以,我们把某个类型所有的对象共有的属性称之为静态属性(或类属性)。而每个对象实例各自的属性称之为实例属性。静态属性的定义上面已经有了,实例属性的定义如下
class Tiger: classname = 'tiger' def _init_(self): self.weighe=200
其中,定义的_init_函数被称之为构造函数,这个函数在实例化的时候,解释器自动调用。实例属性就是在构造函数中定义的。构造函数主要是初始化用的,包括初始化实例属性的值,甚至可以调用其他的函数执行初始化操作。如果我们没有定义构造函数,那么类被实例化的时候。就没有什么初始化操作,_init_函数里面有个参数self,这个其实就是我们实例化好了的对象本身,也就是类的实例对象。
现在我们再看一下类实例化的操作
tiger1 = Tiger()
这个Tiger后面加的括号里面,没有什么参数。但是如果有参数的话,这些参数会传递给构造函数使用,所以构造函数定义的时候必须有相应的参数定义
class Tiger: classname = 'tiger' def __init__(self,weight=200) self.weighe = weight
如果构造函数这样定义,初始化的时候可以
tiger1 = Tiger(300) print(tiger1.weight)
这样显示的体重是300,注意静态属性(类属性classname),可以通过实例进行访问,也可以通过类名进行访问比如Tiger.classname,而实例属性,只能通过实例进行访问,不能通过类进行访问,比如这样就会报错
Tiger.weight
想一想为什么呢?
class Tiger(): #静态属性 也称之为 类属性 tiger_name = '老虎' #__init__下面定义实例属性实例属性。 def __init__(self, weight): #体重 由于每一个老虎都是不一样的,所以定义为实例属性 self.weight = weight if __name__ == '__main__': #根据类创建实例,类的实例化,一旦在初始化方法init中定义了一个属性,如果要传入值,则在实例化的后面括号中传入值 t1 = Tiger(200) #可以通过实例 访问属性? print(t1.weight) #访问实例属性 print(t1.tiger_name) #访问静态属性 print("*****************************") t2 = Tiger(500) print(t2.weight) print(t2.tiger_name) print("*****************************") #访问静态属性也就是类属性的时候,可以不通过实例本身访问,直接通过 类名.属性名 的方式访问 print(Tiger.tiger_name)
4.2.类属性
该类的所有实例共享的属性
4.3.实例属性
实例属性定义在 初始化方法 __init__里面,是每个实例所独有的属性,例如老虎的体重
相关概念说明:
- python里面定义类的时候使用的关键字class,
- 类名的命名规则和变量类似,只不过建议首字母大小
- 静态属性:所有的实例相同的属性,直接定义在类里面就可以
- 实例属性:每一个实例所独有的属性,实例属性要定义在初始化方法init里面,
- 初始化方法init,在类的实例化的时候就会自动调用执行
- 实例属性必须通过实例化对象本身来访问,而静态属性则既可以通过实例化对象本身来访问,也可以通过类名来访问
五、面向对象方法
5.1.对象方法
1、对象的方法就是描述对象行为的
- 通过定义在类里面的函数
- 初始化方法__init__函数
2、类的方法包括
- 实例方法:每个具体实例相关的方法
- 静态方法:共有的方法,每个具体实例都是相同的
- 类方法:接收
cls,用于访问和修改类属性,适合定义与类相关的方法,不涉及实例数据。
我们再来看一下老虎对象的行为的定义。刚才说过了,老虎对象的行为的定义是用类里面的函数定义来实现的。表示对象的行为的函数,我们通常叫做对象的方法
class Tiger: classname = 'tiger' def __init__(self,weight=200): self.weight = weight def roar(self): print('wow!!!') self.weight -=5
上个小节,我们说过对象的属性,可以分为类属性和实例属性,而同样的对象的方法,也分为实例方法、静态方法和类方法,上面定义的roar方法里面也有个self参数,这是这个类的实例方法。必须实例化后才能调用,而静态方法这样定义:
class Tiger: classname = 'tiger' def _init_(self): self.weight = 200 def roar(self): print('Wow!!!') @staticmethod def roar2(): print('w') tiger1 = Tiger()
上面的roar2就是一个静态方法。它通过一个装饰器@staticmethod来定义。静态方法里面不需要传入self这样的参数,同样的静态方法描述的是类所共有的方法,而实例方法则实例的行为可能会不同。静态方法里面无法访问实例属性和实例方法。想一想为什么?
5.2.实例方法
1、与每个具体实例相关的方法
- 如果代码中的实例方法不访问任何实例属性,一般建议实现为静态方法
2、初始化方法就是一个实例方法
3、一般实例方法都要访问self
5.3.静态方法
静态方法:每一个实例相同的方法(行为)称之为静态方法,静态方法可以访问静态属性,但是不能访问实例属性
静态方法是用 @staticmethod 装饰器定义的方法。它们不会接收 self 或 cls 参数,因此无法访问或修改实例或类的属性。静态方法更像是与类相关的普通函数,不依赖于类的实例或类本身
案例一:
class Tiger(): #静态属性 tigername = "老虎" def __init__(self, w1=200): #实例属性 self.w = w1 print("*******************") #实例方法,喂食操作 def feed(self, food): #判断食物的类型 if food == 'meet': self.w += 10 elif food == 'grass': self.w -= 10 #定义老虎的叫声 def rore(self): a = Tiger.tigername print(f'{a}wow!!!') self.w -= 5 @staticmethod def jump(): #静态方法 a = Tiger.tigername print(f"{a}跳跃高度3米") #类方法 @classmethod使用这个来修饰 @classmethod def a(cls): print("in a") #实例化出来两只老虎 t1 = Tiger() # t2 = Tiger() # #调用实例方法,实例.方法名 print(t1.rore()) # #调用实例属性 # print(t1.w) # # #调用静态方法 # print(t1.jump()) # # #通过类名 调用静态方法 # print(Tiger.jump()) #访问类方法 # print(t1.a()) # print(Tiger.a())
案例二:
class MyClass: @staticmethod def static_method(): print("Static method called") MyClass.static_method() # 输出: Static method called
特点:
- 不依赖于类实例或类本身。
- 适用于类级别的操作,而不涉及实例数据。
5.4.类方法
类方法是用 @classmethod 装饰器定义的方法。它们接收类对象(cls)作为第一个参数,而不是实例对象。类方法可以访问和修改类的属性,但不能直接访问实例的属性。
class MyClass: class_variable = "Class variable" @classmethod def class_method(cls): print(f"Class method called with class variable: {cls.class_variable}") MyClass.class_method() # 输出: Class method called with class variable: Class variable
5.5.总结
实例方式:每一个实例所独有的方法(也就是行为),带self的就是实例方法,初始化方法init就是实例方法,实例方法既可以操作实例属性也可以操作静态属性
静态方法:每一个实例相同的方法(行为)称之为静态方法,静态方法可以访问静态属性,但是不能访问实例属性
类方法:这类所独有的一些方法,类方法可以访问静态属性
注意:
- 如果方法中操作了实例属性,就定义为实例方法
- @staticmethod是一个装饰器,指明了他下面的方法是一个静态方法
- 实例方法只能通过实例化对象来调用,静态方法可以通过实例对象来调用,也可以通过类名来调用
六、对象的组合
6.1.什么是对象的组合?
现实世界里面的对象可以是大对象里面有小对象,例如大象可以有脚、眼睛、耳朵、这些对象,在面向对象里面,对象也是可以互相组合的,比如上面的例子,我们游戏里面需要一个房间对象,里面会有老虎或者羊
class Room: def __init__(self): self.num = 1 self.animal = Tiger(300)
上面的定义,房间这个对象包含了老虎对象。还包含一个对象是房间号,固定为1,大家看房间对象由老虎对象和其他对象组成(这里是房间号,一个int对象)。这就是对大象的组合,而游戏里面房间对象里可能是任何老虎或者羊,房间对象的房间号开始也是不确定的。
- 如何获取随机数
from random import randint print(randint(0,1)) print(randint(1,10))
- 如何获取当前系统时间
import time curTime = time.time()
6.2.代码实现
游戏里面房间对象里可能是任何老虎或者羊,房间对象的房间号开始也是不确定的:
class Room: def __init__(self,num,animal): self.num = num self.animal = animal class Tiger(): #静态属性 tigername = "老虎" def __init__(self, w1=200): #实例属性 self.w = w1 print("*******************") #实例方法,喂食操作 def feed(self, food): #判断食物的类型 if food == 'meet': self.w += 10 elif food == 'grass': self.w -= 10 #定义老虎的叫声 def rore(self): a = Tiger.tigername print(f'{a}wow!!!') self.w -= 5 class Sheep(): sheepname = '羊' def __init__(self, w=100): #定义实例属性 体重 self.w = w #定义动物的叫声 def rore(self): print("miemiemie") self.w -= 5 def feed(self,food): #判断喂食的食物 if food == 'grass': print("喂对了,恭喜你") self.w += 10 elif food == 'meet': print("喂错了") self.w -= 10 #房间类 class Room(): #对象的组合其实就是把其他的对象当做参数传递进来 def __init__(self, num, animal): #定义两个实例属性,房间号 、动物 self.num = num self.animal = animal #实例化出来一个羊 s1 = Sheep() #实例化房间 r1 = Room(1, s1) #r1.animall 其实就是传入的s1,既然是实例化的s1,那么就可以访问他的实例属性体重 #访问实例方法 r1.animal.rore() print(r1.animal.w) #访问房间动物的 静态属性、调用静态方法 print(r1.animal.sheepname)
七、面向对象的三大特征
7.1.对象的继承
上个小节说的对象可以由别的对象组成,这完美的比喻了现实世界的对象之间的一种包含关系,还有一种现实世界的情况,用对象组合比较难模拟。比如上面说的老虎这类对象。如果我们的游戏稍微扩展一下:
- 老虎里面还分两种,东北虎和华南虎。东北虎跳起来有3米高,华南虎跳起来2米高。其他行为和之前的设计一样
那时候,就要用到类对象的继承这种特性。继承就是现实世界中大类和小类的关系。比如桌子===》电脑桌 ,恐龙===》霸王龙。电脑桌就是桌子的一种,我们可以说一张电脑桌就是一张桌子,但是反过来,我们不能说一张桌子就是一张电脑桌:
- 组合是一种有一个的关系。A对象有一个B对象的关系
- 继承是一种是一个的关系。A对象是一个B对象的关系
在python里面类对象的继承关系语法是这样定义的:
class SubClassName(ParentClass1[ParentClass2,...]): 'optional class documentation string' class_suite
括号里面的是被继承的类,叫做父类(或者基类),SubClassName是继承类,叫做子类,注意父类可以有多个,比如中国学生这个类,它既可以有父类中国人,也可以有父类学生,它可以同时拥有两个父类所有共同的特性
来看一个具体的例子。还是那个老虎的例子
class Tiger(object): classname = 'tiger' def __init__(self, weight=200) self.weight = weight def roar(self): """ 吼叫 :return: """ print('wow!!!') self.weight -= 5 def feed(self, food): """ 喂食操作 :param food: :return: """ if food == 'meat': self.weight += 10 print('good,weight+10') else: self.weight -= 10 print('bad,weight-10') class NeTiger(Tiger): """ 东北虎,继承老虎类 继承的时候要在子类的类名后面的括号加上父类的名字,并且要在子类的初始化方法init调用父类的初始化方法init 子类继承了父类,就拥有了父类的一切属性和行为 """ color = 'yellow while' def __init__(self, weight=200): Tiger.__init__(self, weight) @staticmethod def jump(): """ 跳跃 :return: """ print('3 meters high') class ScTiger(Tiger): """ 子类华南虎,继承父类Tiger """ color = 'brown black' def __init__(self, weight=200): Tiger.__init__(self, weight) @staticmethod def jump(): print('2 meters high')
我们看到,NeTiger和ScTiger都是Tiger的子类。这里他们都拥有Tiger的一切属性和方法。同时他们还多出了自己增加的属性(color)和方法(jump)
注意:
- 子类的初始化函数_init_里面一般要调用父类的初始化函数
- 当然我们还可以根据实际的需要,设计出子类的子类,子类的子类的子类
7.2.封装
以类的形式封装完成代码的开发
7.3.多态
同一个方法的调用,在不容类型的对象上会表现出不同的特性,称之为多态。
多态通过重定义(或称为方法重写)实现,它使得子类可以提供父类方法的具体实现。这样,你可以用相同的方式调用方法,但实际执行的行为会根据对象的具体类型而有所不同:
- 子类的属性和行为跟父类不一样
- 子类实例 调用这些属性、方法 会使用新的定义
有的时候需要让子类的属性和行为和父类有些不一样。假如我们需要重新定义东北虎的声音,和它的名称。可以这样
class Netiger(Tiger): color = 'yellow white' calssname = 'northeast tiger' def __init__(self,weight=200): Tiger._init_(self,weight) def roar(self): print('wow!!! wow!!! wow!!!') self.weight -= 5 @staticmethod def jump() print('3 meters high')
大家注意,这里属性classname和方法被重新定义了。这时候该类实例调用他们,就会使用新的定义 。
7.4.抽象
往往提到面向对象,我们都会说面向对象有三大特征:继续、封装、多态,但是有时候也有人说面向对象有四大特征:继续、封装、多态、抽象
抽象(就是抽象成类的形式,这个一般很少提及)
八、游戏完整代码
#定义 类 使用 class 关键字 class Tiger(): #静态属性 classname = '老虎' #实例属性 def __init__(self,weight= 200): #self代表实例对象本身 self.weight = weight #老虎叫声,实例方法 def jiao(self): print("wow!!!") self.weight -= 5 #喂食 def eat(self,tt): if tt == 'meet': self.weight += 10 elif tt == 'grass': self.weight -= 10 class Sheep(): #静态属性 classname = '喜羊羊' #实例属性 def __init__(self,weight= 100): #self代表实例对象本身 self.weight = weight #老虎叫声,实例方法 def jiao(self): print("mie!!!") self.weight -= 5 #喂食 def eat(self,tt): if tt == 'meet': self.weight -= 10 elif tt == 'grass': self.weight += 10 class Room(): def __init__(self,room_num,animal): self.room_num = room_num self.animal = animal from random import randint rooms = [] #实例化10个房间对象 for one in range(0,10): if randint(0,1) == 1: ani = Tiger() else: ani = Sheep() #对象的组合 aaa = Room(one,ani) #实例化的10个房间,添加到列表中 rooms.append(aaa) import time ss = time.time() while True: sss = time.time() if sss - ss > 20: print("游戏结束") for idx,room in enumerate(rooms): print("房间:%s" %(idx+1),room.animal.classname,room.animal.weight) #print(f"{idx+ 1}的动物为{room.animal.classname}体重是{room.animal.wieht}") break #随机出现1到10作为列表下标 room_no = randint(1, 10) room = rooms[room_no-1] mm = input("我们来到了房间:%s,要敲门吗?[y/n]" %room_no) if mm == 'y': room.animal.jiao() yy = input("请喂食:") room.animal.eat(yy)

浙公网安备 33010602011771号