面向对象封装案例练习
- 01 封装
- 02 小明爱跑步案例
-
- 2.1 小明爱跑步案例扩展 -- 小美也爱跑步
- 03 摆放家具案例
-
- 3.1 需求分析,被使用类的应该先开发
- 3.2 家具类以及创建家具对象
- 3.3 定义房子类
- 3.4 添加家具
- 04 士兵突击案例
-
- 4.1 开发抢类
- 4.2 开发士兵类
- 05 身份运算符
- 06 私有属性和私有方法
-
- 6.1 应用场景及定义方式
- 6.2 伪私有属性和私有方法(科普)
01 封装
- 封装 是面向对象编程的一大特点
- 面向对象编程的 第一步 —— 将 属性 和 方法 封装 到一个抽象的 类 中
- 外界 使用 类 创建 对象,然后 让对象调用方法
- 对象方法的细节 都被 封装 在 类的内部
一个对象的 属性 可以是 另外一个类创建的对象
02 小明爱跑步案例
需求
- 小明 体重 75.0 公斤
- 小明每次 跑步 会减肥0.5 公斤
- 小明每次 吃东西 体重增加 1 公斤
类 | 属性 | 方法 |
---|---|---|
Person | name weight | __ init __(self,name,weight): __ str __(self): run(self): eat(self): |
提示:在 对象的方法内部,是可以 直接访问对象的属性 的!
代码实现
class Person:
def __init__(self, name, weight):
# self.属性 = 形参
self.name = name
self.weight = weight
def __str__(self):
return "我的名字叫 %s 体重是 %.2f 公斤" % (self.name, self.weight)
def run(self):
print("%s 爱跑步,跑步锻炼身体" % self.name)
self.weight -= 0.5
def eat(self):
print("%s 是吃货,吃完这顿在减肥" % self.name)
self.weight += 1
xiaoming = Person("小明", 75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)
输出结果
小明 爱跑步,跑步锻炼身体
小明 是吃货,吃完这顿在减肥
我的名字叫 小明 体重是 75.50 公斤
2.1 小明爱跑步案例扩展 – 小美也爱跑步
需求
- 小明 和 小美 都爱跑步
- 小明 体重 75.0 公斤
- 小美 体重 45.0 公斤
- 每次 跑步 会减肥0.5 公斤
- 每次 吃东西 体重增加 1 公斤
类 | 属性 | 方法 |
---|---|---|
Person | name weight | __ init __(self,name,weight): __ str __(self): run(self): eat(self): |
提示:
- 在 对象的方法内部,是可以 直接访问对象的属性 的
- 同一个 类 创建的 多个对象 之间,属性 互不干扰
代码实现
class Person:
def __init__(self, name, weight):
# self.属性 = 形参
self.name = name
self.weight = weight
def __str__(self):
return "我的名字叫 %s 体重是 %.2f 公斤" % (self.name, self.weight)
def run(self):
print("%s 爱跑步,跑步锻炼身体" % self.name)
self.weight -= 0.5
def eat(self):
print("%s 是吃货,吃完这顿在减肥" % self.name)
self.weight += 1
xiaoming = Person("小明", 75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)
xiaomei = Person("小美", 45.0)
xiaomei.eat()
xiaomei.run()
print(xiaomei)
print(xiaoming)
输出结果
小明 爱跑步,跑步锻炼身体
小明 是吃货,吃完这顿在减肥
我的名字叫 小明 体重是 75.50 公斤
小美 是吃货,吃完这顿在减肥
小美 爱跑步,跑步锻炼身体
我的名字叫 小美 体重是 45.50 公斤
我的名字叫 小明 体重是 75.50 公斤
03 摆放家具案例
需求
- 房子(House) 有 户型、总面积 和 家具名称列表
- 新房子没有任何的家具
- 家具(HouseItem) 有 名字 和 占地面积,其中
- 席梦思(bed) 占地 4 平米
- 衣柜(chest) 占地 2 平米
- 餐桌(table) 占地 1.5 平米
- 将以上三件 家具 添加 到 房子 中
- 打印房子时,要求输出:户型、总面积、剩余面积、家具名称列表
3.1 需求分析,被使用类的应该先开发
类 | 属性 | 方法 |
---|---|---|
HouseItem | name area | __ init __(self,name,area): __ str __(self): |
House | house_type area free_area item_list | __ init __(self,house_type,area): __ str __(self): add_item(self,item): |
剩余面积
- 在创建房子对象时,定义一个 剩余面积的属性、初始值和总面积相等
-
当调用 add_item 方法,向房间 添加家具 时,让 剩余面积 -= 家具面积
思考:应该先开发哪一个类?
答案 —— 家具类
- 家具简单
- 房子要使用到家具,被使用的类,通常应该先开发
3.2 家具类以及创建家具对象
代码实现
class HouseItem:
def __init__(self, name, area):
self.name = name
self.area = area
def __str__(self):
return "[%s] 占地 %.2f" % (self.name, self.area)
# 1、创建家具
bed = HouseItem("席梦思", 4)
chest = HouseItem("衣柜", 2)
table = HouseItem("餐桌", 1.5)
print(bed)
print(chest)
print(table)
输出结果
[席梦思] 占地 4.00
[衣柜] 占地 2.00
[餐桌] 占地 1.50
小结
- 创建了一个 家具类,使用到 __ init __ 和 __ str __ 两个内置方法
- 使用 家具类 创建了三个家具对象
3.3 定义房子类
代码实现
class HouseItem:
def __init__(self, name, area):
self.name = name
self.area = area
def __str__(self):
return "[%s] 占地 %.2f" % (self.name, self.area)
class House:
def __init__(self, house_type, area):
self.house_type = house_type
self.area = area
# 剩余面积
self.free_area = area
# 家具名称列表
self.item_list = []
def __str__(self):
# Python 能够自动的将一对括号内部的代码连接在一起
return ("户型:%s\n总面积:%.2f[剩余面积:%.2f]\n家具:%s"
% (self.house_type, self.area,
self.free_area, self.item_list))
def add_item(self, item):
print("要添加 %s " % item)
# 1、创建家具
bed = HouseItem("席梦思", 4)
chest = HouseItem("衣柜", 2)
table = HouseItem("餐桌", 1.5)
print(bed)
print(chest)
print(table)
# 2.创建房子对象
my_home = House("两室一厅", 80)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)
输出结果
[席梦思] 占地 4.00
[衣柜] 占地 2.00
[餐桌] 占地 1.50
要添加 [席梦思] 占地 4.00
要添加 [衣柜] 占地 2.00
要添加 [餐桌] 占地 1.50
户型:两室一厅
总面积:80.00[剩余面积:80.00]
家具:[]
小结
- 创建了一个 房子类,使用到 __ init __ 和 __ str __ 两个内置方法
- 准备了一个 add_item 方法 准备添加家具
- 使用 房子类 创建了一个房子对象
- 让 房子对象 调用了三次 add_item 方法 方法,将 三件家具 以实参传递到 add_item 方法 内部
3.4 添加家具
需求
- 判断 家具面积 是否 超过剩余面积,如果超过,则提示不能添加这件家具
- 将 家具名称 追加到 家具名称列表 中
- 用 房子的剩余面积 - 家具面积
代码实现
class HouseItem:
def __init__(self, name, area):
self.name = name
self.area = area
def __str__(self):
return "[%s] 占地 %.2f" % (self.name, self.area)
class House:
def __init__(self, house_type, area):
self.house_type = house_type
self.area = area
# 剩余面积
self.free_area = area
# 家具名称列表
self.item_list = []
def __str__(self):
# Python 能够自动的将一对括号内部的代码连接在一起
return ("户型:%s\n总面积:%.2f[剩余面积:%.2f]\n家具:%s"
% (self.house_type, self.area,
self.free_area, self.item_list))
def add_item(self, item):
print("要添加 %s " % item)
# 1. 判断家具的面积
if item.area > self.free_area :
print("%s 的面积太大了,无法添加" % item.name)
return
# 2. 将家具的名称添加到列表中
self.item_list.append(item.name)
# 3. 计算剩余面积
self.free_area -= item.area
# 1、创建家具
bed = HouseItem("席梦思", 40)
chest = HouseItem("衣柜", 2)
table = HouseItem("餐桌", 1.5)
print(bed)
print(chest)
print(table)
# 2.创建房子对象
my_home = House("两室一厅", 80)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)
输出结果
[席梦思] 占地 40.00
[衣柜] 占地 2.00
[餐桌] 占地 1.50
要添加 [席梦思] 占地 40.00
要添加 [衣柜] 占地 2.00
要添加 [餐桌] 占地 1.50
户型:两室一厅
总面积:80.00[剩余面积:36.50]
家具:[‘席梦思’, ‘衣柜’, ‘餐桌’]
小结
- 主程序只负责创建 房子 对象 和 家具 对象
- 让 房子 对象调用 add_item 方法 方法 将家具添加到房子中
- 面积计算、剩余面积、家具列表 等处理都被 封装 到 房子类的内部
04 士兵突击案例
需求
- 士兵 许三多 有一把 AK47
- 士兵 可以 开火
- 抢 能够 发射 子弹
- 抢 能够 填装子弹 —— 增加子弹数量
类 | 属性 | 方法 |
---|---|---|
Soldier | name gun | __ init __(self,name,area): fire(self): |
Gun | model bullet_count | __ init __(self,house_type,area): add_bullet(self,count): shoot(self): |
4.1 开发抢类
代码实现
class Gun:
def __init__(self, model):
# 1. 抢的型号
self.model = model
# 2. 子弹数量
self.bullet_count = 0
def add_bullet(self, count):
self.bullet_count += count
def shoot(self):
# 1. 判断子弹数量
if self.bullet_count <= 0:
print("[%s]没有子弹了" % self.model)
return
# 2. 发射子弹,-1
self.bullet_count -= 1
# 3. 提示发射信息
print("[%s]突突突...[%d]" % (self.model, self.bullet_count))
# 1. 创建抢对象
ak47 = Gun("AK-47")
ak47.add_bullet(100)
ak47.shoot()
输出结果
[AK-47]突突突…[99]
4.2 开发士兵类
假设:每一个新兵 都 没有抢
定义没有初始值的属性
在定义属性时,如果 不知道设置什么初始值,可是设置为 None
- None 关键字 表示 什么都没有
- 表示一个 空对象,没有属性和方法,是一个特殊的常量
- 可以将None赋值给任何一个变量
fire方法需求
- 判断是否有抢,没有抢没法冲锋
- 喊一声口号
- 装填子弹
- 射击
代码实现
class Gun:
def __init__(self, model):
# 1. 枪的型号
self.model = model
# 2. 子弹数量
self.bullet_count = 0
def add_bullet(self, count):
self.bullet_count += count
def shoot(self):
# 1. 判断子弹数量
if self.bullet_count <= 0:
print("[%s]没有子弹了" % self.model)
return
# 2. 发射子弹,-1
self.bullet_count -= 1
# 3. 提示发射信息
print("[%s]突突突...[%d]" % (self.model, self.bullet_count))
class Soldier:
def __init__(self, name):
# 1.姓名
self.name = name
# 1.枪-新兵没有抢
self.gun = None
def fire(self):
# 1.判断士兵是否有抢
if self.gun is None:
print("[%s] 还没有抢..." % self.name)
return
# 2.高喊口号
print("冲啊...[%s]" % self.name)
# 3.让抢装填子弹
self.gun.add_bullet(50)
# 4.让抢发射子弹
self.gun.shoot()
# 1. 创建枪对象
ak47 = Gun("AK-47")
# 2. 创建许三多
xusanduo = Soldier("许三多")
xusanduo.gun = ak47
xusanduo.fire()
print(xusanduo.gun)
输出结果
冲啊…[许三多]
[AK-47]突突突…[49]
<main.Gun object at 0x0000022DD8586A60>
05 身份运算符
身份运算符用于 比较 两个对象的 内存地址 是否一致 – 是否是对同一个对象的引用
在Python中针对None 比较时,建议使用is判断
运算符 | 描述 | 实例 |
---|---|---|
is | is是判断两个标识符是不是引用同一个对象 | x is y,类似 id(x)==id(y) |
is not | is not是判断两个标识符是不是引用不同对象 | x is not y,类似 id(x)!=id(y) |
is 与 == 区别:
is 用于判断 两个变量 引用对象是否为同一个
== 用于判断 引用变量的值 是否相等
a = [1, 2, 3]
b = [1, 2, 3]
b is a
Flase
b == a
True
06 私有属性和私有方法
6.1 应用场景及定义方式
应用场景
- 在实际开发中,对象 的 某个属性或方法 可能只希望 在对象的内部被使用,而 不希望在外部被访问到
- 私有属性 就是 对象 不希望公开的 属性
-
私有方法 就是 对象 不希望公开的 方法
定义方式
- 在 定义属性或方法时,在 属性名或者方法名 前 增加 两个下划线,定义的就是 私有 属性和方法
Women |
---|
name __ age |
__ init __ (self,name) : __secret(self): |
class Women:
def __init__(self,name):
self.name = name
self.__age = 18
def __secret(self):
# 在对象的方法内部,是可以访问对象的私有属性的
print("%s 的年龄是 %d" % (self.name, self.__age))
xiaofang = Women("小芳")
# 私有属性,在外界不能直接被访问
# print(xiaofang.__age)
# 私有方法,同样不允许在外界直接被访问
# xiaofang.__secret()
6.2 伪私有属性和私有方法(科普)
提示:在日常开发中,不要使用这种方式,访问对象的 私有属性 或 私有方法
Python中,并没有真正意义的私有
- 在给 属性、方法命名时,实际是对 名称 做了一些特殊处理,是的外界无法访问到
- 处理方式:在名称前面加上 _类名 => _类名_名称
# 私有属性,在外界不能直接被访问
print(xiaofang._Women__age)
# 私有方法,同样不允许在外界直接被访问
xiaofang._Women__secret()