9、面向对象程序设计
9.1 面向对象概述
一种设计思想。是指客观世界中存在的对象,具有唯一性。也可以是抽象的一个事物,具有自己的属性和行为。面向对象技术是一种从组织结构上模拟客观世界的方法。
- 对象
表示任意存在的事物。静态部分成为属性,动态部分称为行为。
- 类
是封装对象的属性和行为的载体。是具有相同属性和行为的一类实体被称为类。对象是类的实例。
- 面向对象程序设计的特点
封装、继承 和 多态
(1)封装
是面向对象编程的核心思想,将对象的属性和行为封装起来。类通常随客户隐藏其实现细节,就是封装的思想。
(2)继承
是实现重复利用的重要手段,子类继承复用父类的属性和行为的同时,又添加了子类特有的属性和行为。
(3)多态
将父类对象应用于子类的特征就是多态。
9.2 类的定义和使用
9.2.1 定义类
class Geese:
pass
9.2.2 创建类的实例
wideGoose = Geese()
print(wideGoose)
# <__main__.Geese object at 0x02E7AF70>
9.2.3 创建__init()__
方法
类似于构造方法,__init()__
必须包含一个self
参数,必须是第一个参数。self
是一个指向实例本身的引用,用于访问类中的属性和方法。
class Geese:
def __init__(self):
print('我是大雁类!')
wideGoose = Geese()
# 我是大雁类!
class Geese:
def __init__(self, beak, wing, claw):
self.beak = beak
self.wing = wing
self.claw = claw
print('我是大雁,我有以下特征:')
print(beak)
print(wing)
print(claw)
beak_1 = '喙的特征,长度和宽度几乎相等'
wing_1 = '翅膀长而尖'
claw_1 = '爪子是蹼状的'
wideGoose = Geese(beak_1, wing_1, claw_1)
'''
我是大雁,我有以下特征:
喙的特征,长度和宽度几乎相等
翅膀长而尖
爪子是蹼状的
'''
9.2.4 创建类的成员方法
class Geese:
def __init__(self, beak, wing, claw):
self.beak = beak
self.wing = wing
self.claw = claw
print('我是大雁,我有以下特征:')
print(beak)
print(wing)
print(claw)
def fly(self, state):
self.state = state
print(state)
beak_1 = '喙的特征,长度和宽度几乎相等'
wing_1 = '翅膀长而尖'
claw_1 = '爪子是蹼状的'
wideGoose = Geese(beak_1, wing_1, claw_1)
state = '我飞的时候,一会是一字,一会是人字'
wideGoose.fly(state)
# 我飞的时候,一会是一字,一会是人字
通过类的属性统计实例个数
class Geese:
neck = '脖子较长'
wing = '振翅频率高'
leg = '腿位于身体的中心支点,行走自如'
number = 0
def __init__(self):
Geese.number += 1
print('这是我第'+str(Geese.number)+'只大雁,我属于雁类,有如下特征:')
print(Geese.neck)
print(Geese.wing)
print(Geese.leg)
list1 = []
for i in range(2):
list1.append(Geese())
print('一共有'+str(Geese.number)+'只大雁')
'''
这是我第1只大雁,我属于雁类,有如下特征:
脖子较长
振翅频率高
腿位于身体的中心支点,行走自如
这是我第2只大雁,我属于雁类,有如下特征:
脖子较长
振翅频率高
腿位于身体的中心支点,行走自如
一共有2只大雁
'''
实例属性也可以通过实例名称来修改,与类的属性不同,通过实例名称修改实例属性后不影响该类的另一个实例中相应的实例属性的值。
class Geese:
def __init__(self):
self.neck = '脖子较长'
print(self.neck)
goose1 = Geese()
goose2 = Geese()
goose1.neck = '脖子没有天鹅的长'
print('goose1的neck属性:', goose1.neck)
print('goose2的neck属性:', goose2.neck)
'''
脖子较长
脖子较长
goose1的neck属性: 脖子没有天鹅的长
goose2的neck属性: 脖子较长
'''
9.2.5 访问权限
Python并没有对属性和方法的访问权限进行限制。为了保证类的内部的某些属性或方法不被外部访问,可以在属性或方法前添加下划线(_foo
)、双下划线(__foo
)或收尾加双下划线(__foo__
)限制访问权限。
(1)_foo
:表示protected(保护)类型成员。只允许类本身和子类进行访问,不能使用from module import*
导入。
class Swan:
_neck_swan = '天鹅的脖子很长' # 定义保护属性
def __init__(self):
print('__init__():', Swan._neck_swan)
swan = Swan()
print('直接访问:', swan._neck_swan)
'''
__init__(): 天鹅的脖子很长
直接访问: 天鹅的脖子很长
'''
上面可以看到,保护属性可以通过实例名访问。
(2)__foo
:表示private(私有)类型成员,只能定义该方法的类的本身进行访问,不能通过类的实例进行访问。
class Swan:
__neck_swan = '天鹅的脖子很长' # 定义保护属性
def __init__(self):
print('__init__():', Swan.__neck_swan)
swan = Swan()
print('加入类名:', swan._Swan__neck_swan)
# __init__(): 天鹅的脖子很长
# 加入类名: 天鹅的脖子很长
上面可以看到,私有属性可以在类实例方法中访问,也可以通过“实例名.类名_XXX”方式访问,但不能直接通过实例名+属性名访问。
(3)__foo__
:表示定义特殊方法,一般是系统定义名字,如__init___()
。
9.3 属性
9.3.1 创建用于计算的属性
Python可以通过装饰器(@property)将一个方法转换为属性,从而实现用于计算的属性。
class Rect:
def __init__(self, width, height):
self.width = width
self.height = height
@property # 将方法转换为属性
def area(self): # 计算矩形面积的方法
return self.width * self.height
rect = Rect(800, 600) # 创建类的实例
print('矩形的面积是:', rect.area) # 输出属性的值
# 矩形的面积是: 480000
9.3.2 为属性添加安全保护机制
python默认创建的类的属性或实例可以在类体外进行修改,如果想要限制不能在体外修改,可以将其设为私有。设为私有后,不能在类体外获取其值。
class TVshow:
def __init__(self, show):
self.__show = show
@property
def show(self):
return self.__show
tvshow = TVshow('正在播放战狼2')
print('默认:', tvshow.show)
# 默认: 正在播放战狼2
上述属性具有只读性。
class TVshow:
list_film = ['战狼2', '红海行动', '哪吒之魔童降世', '唐人街探案']
def __init__(self, show):
self.__show = show
@property # 将方法转换为属性
def show(self): # 定义show()方法
return self.__show # 设置私有属性
@show.setter #让属性值可以修改
def show(self, value):
if value in TVshow.list_film:
self.__show = '您选择了' + value + ',稍后播放。'
else:
self.__show = '您点播的电影不存在。'
tvshow = TVshow('战狼2')
print('正在播放:' + tvshow.show)
print('您可以从', TVshow.list_film ,'中选择要播放的电影')
tvshow.show = '红海行动'
print(tvshow.show)
'''
正在播放:战狼2
您可以从 ['战狼2', '红海行动', '哪吒之魔童降世', '唐人街探案'] 中选择要播放的电影
您选择了红海行动,稍后播放。
'''
9.4 继承
9.4.1 继承的基本语法
被继承的类称为父类或基类,新的类成为子类或者是派生类。
class Fruit:
color = '绿色'
def harvest(self, color):
print('水果是:' + color + '的!')
print('水果已经收获。。。。。')
print('水果原来是:' + Fruit.color + '的!')
class Apple(Fruit):
color = '红色'
def __init__(self):
print('我是苹果')
class Orange(Fruit):
color = '橙色'
def __init__(self):
print('我是橘子')
apple = Apple()
apple.harvest(apple.color)
orange = Orange()
orange.harvest(orange.color)
'''
我是苹果
水果是:红色的!
水果已经收获。。。。。
水果原来是:绿色的!
我是橘子
水果是:橙色的!
水果已经收获。。。。。
水果原来是:绿色的!
'''
9.4.2 方法重写
基类的成员会被派生继承,当基类中的某个方法不完全适用于派生类时,需要在派生类中重写父类的这个方法。
将上述代码改为:
class Orange(Fruit):
color = '橙色'
def __init__(self):
print('我是橘子')
def harvest(self, color):
print('橘子是:' + self.color + '的!')
print('橘子已收获。。。')
print('橘子原来的颜色是:' +Fruit.color + '的!')
'''
我是橘子
橘子是:橙色的!
橘子已收获。。。
橘子原来的颜色是:绿色的!
'''
9.4.3 派生类中调用基类的__init__()
方法
派生类中的__init__()
方法不会自动调用基类的__init__()
方法。
class Fruit:
def __init__(self, color = '绿色'):
self.color = color
def harvest(self):
print('水果原来的颜色是:' + self.color + '的!')
class Apple(Fruit):
def __init__(self):
print('我是苹果')
super().__init__() # 调用基类的__init__()方法
'''
我是苹果
水果原来的颜色是:绿色的!
'''
class Fruit:
def __init__(self, color = '绿色'):
Fruit.color = color
def harvest(self, color):
print('水果是:' + self.color + '的!')
print('水果已经收获')
print('水果原来是:' + Fruit.color + '的!')
class Apple(Fruit):
color = '红色'
def __init__(self):
print('我是苹果')
super().__init__()
class Peach(Fruit):
def __init__(self, color):
print('我是桃子')
super().__init__(color)
def harvest(self, color):
print('桃子是:' + color + '的!')
print('桃子已经收获!')
print('桃子原来是' + Fruit.color + '的!')
apple = Apple()
apple.harvest(apple.color)
peach = Peach('粉色')
peach.harvest("粉白相间的")
'''
我是苹果
水果是:红色的!
水果已经收获
水果原来是:绿色的!
我是桃子
桃子是:粉白相间的的!
桃子已经收获!
桃子原来是粉色的!
'''