天天看点

Python 面向对象系列(二)01 封装02 小明爱跑步案例03 摆放家具案例04 士兵突击案例05 身份运算符06 私有属性和私有方法

面向对象封装案例练习

  • 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 封装

  1. 封装 是面向对象编程的一大特点
  2. 面向对象编程的 第一步 —— 将 属性 和 方法 封装 到一个抽象的 类 中
  3. 外界 使用 类 创建 对象,然后 让对象调用方法
  4. 对象方法的细节 都被 封装 在 类的内部
一个对象的 属性 可以是 另外一个类创建的对象

02 小明爱跑步案例

需求

  1. 小明 体重 75.0 公斤
  2. 小明每次 跑步 会减肥0.5 公斤
  3. 小明每次 吃东西 体重增加 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 小明爱跑步案例扩展 – 小美也爱跑步

需求

  1. 小明 和 小美 都爱跑步
  2. 小明 体重 75.0 公斤
  3. 小美 体重 45.0 公斤
  4. 每次 跑步 会减肥0.5 公斤
  5. 每次 吃东西 体重增加 1 公斤
属性 方法
Person

name

weight

__ init __(self,name,weight):

__ str __(self):

run(self):

eat(self):

提示:
  1. 在 对象的方法内部,是可以 直接访问对象的属性 的
  2. 同一个 类 创建的 多个对象 之间,属性 互不干扰

代码实现

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 摆放家具案例

需求

  1. 房子(House) 有 户型、总面积 和 家具名称列表
    • 新房子没有任何的家具
  2. 家具(HouseItem) 有 名字 和 占地面积,其中
    • 席梦思(bed) 占地 4 平米
    • 衣柜(chest) 占地 2 平米
    • 餐桌(table) 占地 1.5 平米
  3. 将以上三件 家具 添加 到 房子 中
  4. 打印房子时,要求输出:户型、总面积、剩余面积、家具名称列表

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):

剩余面积

  1. 在创建房子对象时,定义一个 剩余面积的属性、初始值和总面积相等
  2. 当调用 add_item 方法,向房间 添加家具 时,让 剩余面积 -= 家具面积

    思考:应该先开发哪一个类?

    答案 —— 家具类

  3. 家具简单
  4. 房子要使用到家具,被使用的类,通常应该先开发

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

小结

  1. 创建了一个 家具类,使用到 __ init __ 和 __ str __ 两个内置方法
  2. 使用 家具类 创建了三个家具对象

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]

家具:[]

小结

  1. 创建了一个 房子类,使用到 __ init __ 和 __ str __ 两个内置方法
  2. 准备了一个 add_item 方法 准备添加家具
  3. 使用 房子类 创建了一个房子对象
  4. 让 房子对象 调用了三次 add_item 方法 方法,将 三件家具 以实参传递到 add_item 方法 内部

3.4 添加家具

需求

  1. 判断 家具面积 是否 超过剩余面积,如果超过,则提示不能添加这件家具
  2. 将 家具名称 追加到 家具名称列表 中
  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. 判断家具的面积
        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]

家具:[‘席梦思’, ‘衣柜’, ‘餐桌’]

小结

  1. 主程序只负责创建 房子 对象 和 家具 对象
  2. 让 房子 对象调用 add_item 方法 方法 将家具添加到房子中
  3. 面积计算、剩余面积、家具列表 等处理都被 封装 到 房子类的内部

04 士兵突击案例

需求

  1. 士兵 许三多 有一把 AK47
  2. 士兵 可以 开火
  3. 抢 能够 发射 子弹
  4. 抢 能够 填装子弹 —— 增加子弹数量
属性 方法
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方法需求

  1. 判断是否有抢,没有抢没法冲锋
  2. 喊一声口号
  3. 装填子弹
  4. 射击

代码实现

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()