黑马程序员python网课笔记(接上一篇)

面向对象封装案例

01. 小明爱跑步

  • 需求
  1. 体重75公斤
  2. 每次跑步减0.5公斤
  3. 吃东西体重增加1公斤
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.run()
xiaomei.eat()
print(xiaomei)
# 多个对象之间互不影响

小明 爱跑步 , 跑步锻炼身体
小明 爱吃饭 , 吃饭增长体重
我的名字是小明 体重是75.50 公斤
小美 爱跑步 , 跑步锻炼身体
小美 爱吃饭 , 吃饭增长体重
我的名字是小美 体重是45.50 公斤

02.摆放家具

  • 需求
  1. 房子有户型,总面积和家具名称列表
    • 新房子无家具
  2. 家具:名字和占地面积
    • 席梦思:4
    • 衣柜:2
    • 餐桌:1.5
  3. 将家具添加到房子
  4. 打印房子输出:户型,总面积,剩余面积,家具名称列表
  • 剩余面积
  1. 创建对象时定义
  2. 调用add_item方法添加家具
  • 先开发哪个类?

家具类:家具类简单,房子要使用到家具,在开发中被使用的类先开发

class HouseItem:
    def __init__(self, name, area):
        self.name = name
        self.area = area
    def __str__(self):
        return "%s 占地 %.2f" % (self.name, self.area)
bed = HouseItem("席梦思", 4)
chest = HouseItem("衣柜", 2)
table = HouseItem("餐桌", 1.5)
print(bed)
print(chest)
print(table)

席梦思 占地 4.00
衣柜 占地 2.00
餐桌 占地 1.50

定义房子类

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):
        return ("户型:%s\\n总面积: %.2f\\n剩余面积:%.2f\\n家具: %s"
                % (self.house_type, self.area,
                   self.free_area, self.item_list))
    # python能自动将括号里的代码连接到一起
    def add_item(self,item):
        print("要添加%s" % item)
bed = HouseItem("席梦思", 4)
chest = HouseItem("衣柜", 2)
table = HouseItem("餐桌", 1.5)
print(bed)
print(chest)
print(table)

my_house = House("两室一厅", 60)
my_house.add_item(bed)
my_house.add_item(chest)
my_house.add_item(table)

席梦思 占地 4.00
衣柜 占地 2.00
餐桌 占地 1.50
要添加席梦思 占地 4.00
要添加衣柜 占地 2.00
要添加餐桌 占地 1.50

完成摆放家具:

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):
        return ("户型:%s\\n总面积: %.2f\\n剩余面积:%.2f\\n家具: %s"
                % (self.house_type, self.area,
                   self.free_area, self.item_list))
    # python能自动将括号里的代码连接到一起
    def add_item(self,item):
        print("要添加%s" % item)
        if item.area > self.free_area:
            print("%s面积太大" % item.area)
            return
        self.item_list.append(item.name)
        self.free_area -= item.area
bed = HouseItem("席梦思", 4)
chest = HouseItem("衣柜", 2)
table = HouseItem("餐桌", 1.5)
print(bed)
print(chest)
print(table)

my_house = House("两室一厅", 60)
my_house.add_item(bed)
my_house.add_item(chest)
my_house.add_item(table)
print(my_house)

席梦思 占地 4.00
衣柜 占地 2.00
餐桌 占地 1.50
要添加席梦思 占地 4.00
要添加衣柜 占地 2.00
要添加餐桌 占地 1.50
户型:两室一厅
总面积: 60.00
剩余面积:52.50
家具: ['席梦思', '衣柜', '餐桌']

03.士兵突击

一个对象的属性可以是另一个类创建的对象

  • 需求:
  1. 士兵 许三多 有一把ak47
  2. 士兵可以开火
  3. 枪可以发射子弹
  4. 枪装填子弹

枪类:

class Gun:
    def __init__(self, model):
        self.model = model
        self.bullet_count = 0

    def add_bullet(self, count):
            self.bullet_count += count

    def shoot(self):
        if self.bullet_count <= 0:
            print("%s没有子弹了" % self.model)
            return
        self.bullet_count -= 1
        print("%s突突突%d" % (self.model, self.bullet_count))

ak47 = Gun("ak47")
ak47.add_bullet(50)
ak47.shoot()

ak47突突突49

士兵类:

class Gun:
    def __init__(self, model):
        self.model = model
        self.bullet_count = 0

    def add_bullet(self, count):
            self.bullet_count += count

    def shoot(self):
        if self.bullet_count <= 0:
            print("%s没有子弹了" % self.model)
            return
        self.bullet_count -= 1
        print("%s突突突%d" % (self.model, self.bullet_count))

class Soldier:
    def __init__(self, name):
        self.name = name
        self.gun = None
    def fire(self):
        if self.gun == None:
            print("%s还没有枪" % self.name)
            return
        print("冲啊...%s" % self.name)
        self.gun.add_bullet(50)
        self.gun.shoot()
ak47 = Gun("ak47")
xusanduo = Soldier("许三多")
xusanduo.gun = ak47
xusanduo.fire()

冲啊...许三多
ak47突突突49

身份运算符(补充)

用于比较两个对象的内存地址是否相同—是否为对同一个对象的引用

  • 在python中针对None的比较建议使用is
  • ==用于判断两个引用变量的值是否相等

私有属性和私有方法

01.应用场景及定义方法

应用场景

  • 对象的某些属性和方法不需要对外界公开,只在内部使用

定义方式

  • 在属性名或方法名前加两个下划线
class Women:
    def __init__(self, name):
        self.name = name
        self.__age = 18
    def secret(self):
        # 在对象的方法内部可以访问私有属性
        print("%s的年龄是%d" % (self.name, self.__age))

xiaofan = Women("小芳")
# print(xiaofan.age)
xiaofan.secert()

小芳的年龄是18

02.伪私有属性和私有方法(了解)

python中不存在真正的私有属性和私有方法

  • 在给出的属性、方法命名时对名称做特殊处理导致外界访问不到
  • 处理方式:在名称前加上_类名 ⇒ _类名__名称
class Women:
    def __init__(self, name):
        self.name = name
        self.__age = 18
    def secret(self):
        # 在对象的方法内部可以访问私有属性
        print("%s的年龄是%d" % (self.name, self.__age))

xiaofan = Women("小芳")
print(xiaofan._Women__age)
xiaofan.secret()
  • 子类对象不能在自己的方法内部直接访问父类的私有方法和属性
  • 子类可以通过父类的公有方法访问到私有方法和私有属性

继承

面向对象三大特性:

  1. 封装
  2. 继承 子类拥有父类所以的方法
  3. 多态
  • 语法:
class Animal:
    def eat(self):
        print("吃")
**class dog(Animal):**# 继承
    def bark(self):
        print("叫")
dog = dog()
dog.bark()
dog.eat()
  • 术语: 子类,父类,继承

    派生类,基类,派生

  • 继承的传递性

  1. c继承b,b继承a
  2. c类拥有b和a所以属性和方法
  • 方法的重写
  1. 父类的方法不能满足子类的需求,就需要子类对方法进行重写

    • 覆盖父类的方法:在子类中重新编写父类 的方法实现(定义一个同名方法)
    • 对父类方法进行扩展:子类方法实现存在父类方法。在子类中重写父类方法,在需要的位置通过 super().父类方法 实现对父类方法的执行
    class Animal:
        def eat(self):
            print("吃")
    class dog(Animal):# 继承
        def bark(self):
            print("叫")
        def eat(self):
            print("吃骨头")
            super().eat()
    dog = dog()
    dog.bark()
    dog.eat()
    
    叫
    吃骨头
    吃
    
    • 另一种调用父类方法:父类名.方法(self),不推荐这种方式(了解)

多继承

概念子类可以拥有多个父类,拥有所有父类的属性和方法

语法:class 子类名(父类名1,父类名2)

  • 开发时如果不同的父类中会有同名的方法,应该避免多继承
  • python中的MRO—方法搜索顺序:
  1. python中提供内置对象__mro__可以查看方法搜索顺序
  2. mro主要用于多继承时判断方法、属性的调用路径
  3. 输出时是按照__mro__的输出结果从左向右的顺序查找
  4. 在当前类中找到方法就不再执行
  5. 如果在Object类中也没有找到就会报错

当前类→父类1→父类2→object类

新式类和旧式类

object是python为所有对象提供的基类,提供一些内置方法和属性,可以使用dir方法函数查看

  • 新式类:以object为基类的类
  • 旧式类:不以object为基类的类
class A(object):
    pass
class B:
    pass
a = A()
b = B()
print(dir(a))
print(dir(b))
# 新版本python中默认为新式类

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

多态

特性:不同子类对象调用相同的方法,产生不同执行结果

  1. 可以增加代码灵活度
  2. 以继承和重写父类方法 为前提
  3. 不会影响类的内部设计

案例演练

需求

  1. 在dog类中封装game方法
  2. 定义xiaotianquan继承dog类,重写game方法
  3. 定义person类,封装和狗玩的方法
class Dog():
    def __init__(self, name):
        self.name = name
    def game(self):
        print("%s蹦蹦跳跳的玩耍" % self.name)
class xiaotianquan(Dog):
    def game(self):
        print("%s飞到天上去玩耍" % self.name)
class person():
    def __init__(self, name):
        self.name = name
    def game_with_dog(self, dog):
        print("%s和%s快乐的玩耍" % (self.name, dog.name))
        dog.game()

wangcai = Dog("旺财")
huahua = xiaotianquan("飞天花花")
xiaoming = person("小明")
xiaoming.game_with_dog(wangcai)
xiaoming.game_with_dog(huahua)

小明和旺财快乐的玩耍
旺财蹦蹦跳跳的玩耍
小明和飞天花花快乐的玩耍
飞天花花飞到天上去玩

类属性

01. 类的结构

  1. 使用类名()创建对象,分为两部:
    1. 在内存中分配空间
    2. 调用初始化方法__init__为对象初始化
  2. 对象创建后,内存中就存在实例
    1. 对象叫做类的实例
    2. 创建对象叫做实例化
    3. 对象的属性为实例属性
    4. 对象调用的方法叫做实例方法
  3. 多个对象的方法在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部

02. 类是一个特殊的对象

  • class AAA:定义的类属于类对象
  • obj1 = AAA()属于实例对象
  • 类对象在内存中只有一份,一个类可以创建出多个实例对象
  • 除封装实例的属性和方法外,类对象还可以拥有自己的属性和方法
    1. 类属性
    2. 类方法
  • 通过 类名.的方法访问类的属性和方法

03. 类属性和实例属性

概念:

  • 类属性为类对象中定义的属性
  • 记录类的相关特征
  • 类属性不会用于记录具体对象特征

示例需求:

  • 定义一个工具类
  • 每件工具都有自己的name
  • 需求——知道使用这个类,创建了多少个工具对象?
class Tool(object):
    count = 0
    def __init__(self,name):
        self.name = name
        Tool.count +=1
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")
print(Tool.count)

3

属性获取机制(了解):

  • 在python中获取属性存在向上查找机制

    1. 首先在对象内部查找对象属性
    2. 没有找到就会向上寻找类属性
    class Tool(object):
        count = 0
        def __init__(self,name):
            self.name = name
            Tool.count +=1
    tool1 = Tool("斧头")
    tool2 = Tool("榔头")
    tool3 = Tool("水桶")
    print(tool3.count)
    # 不建议使用,因为会给对象添加count属性
    
    3
    

类方法

类方法的创建:

  • 语法:
@classmethod
def 类方法名(cls):
	pass
  • cls告诉解释器这是一个类方法
  • 由哪一个类调用的方法,方法内的cls就是哪一个类的引用
  • 类似于self
  • 使用其他名称也可(不推荐)
  • 在方法内可以通过cls.访问类属性,也可以调用其他类方法

实力需求:

  • 定义一个工具类
  • 每件工具都有自己的name
  • 需求——在类中封装一个show_tool_count的类方法,输出使用当前这个类,创建的对象个数
class Tool(object):
    count = 0
    @classmethod
    def show_tool_count(cls):
        print("工具对象的数量 %d" % cls.count)
    def __init__(self,name):
        self.name = name
        Tool.count +=1
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")
Tool.show_tool_count()

工具对象的数量 3

静态方法

  • 在开发中,需要封装一个方法:
    1. 不需要访问实例属性或者实例方法
    2. 也不需要访问类属性和类方法
  • 这个时候可以封装成静态方法
  • 通过 类名. 调用
  • 语法
@staticmethod
def 静态方法名():
	pass

简单的示例:

class Dog(object):
    @staticmethod
    def run():
        print("小狗要跑")
Dog.run()
# 通过类名访问不需要实例化

小狗要跑

方法综合案例

需求:

  1. 设计一个Game类
  2. 属性:
    1. 定义一个类属性 top_score记录游戏的历史最高得分
    2. 定义一个示例属性 player_name记录当前游戏的玩家姓名
  3. 方法:
    1. 静态方法 show_help显示游戏的帮助信息
    2. 类方法 show_top_score 显示历史最高分
    3. 实例方法 start_game开始当前玩家的游戏
  4. 主程序步骤
    1. 查看帮助信息
    2. 查看历史最高分
    3. 创建游戏对象,开始游戏
class Game(object):
    top_score = 0
    def __init__(self, player_name):
        self.player_name = player_name
    @staticmethod
    def show_help():
        print("帮助信息:")
    @classmethod
    def show_top_score(cls):
        print("历史记录%d" % cls.top_score)
    def start_game(self):
        print("%s开始游戏啦" % self.player_name)
Game.show_help()
Game.show_top_score()
game = Game("小明")
game.start_game()

帮助信息:
历史记录0
小明开始游戏啦

单例

01. 单例设计模式

设计模式:

  1. 设计模式是前人工作的总结,针对某一个特定问题的成熟的解方案
  2. 为了可重用代码,让代码更容易理解

单例设计模式:

  1. 目的——让 类 创建的对象,在系统中只有一个实例
  2. 每一次执行 类名() 返回的对象,内存地址相同

**new 方法:**

  1. 使用类名()创建对象时,会首先调用__new__()方法为对象分配空间
  2. new()方法是内置的静态方法
    1. 为对象分配空间
    2. 返回对象引用
  3. 解释器获得对象引用后,将引用作为第一个参数,传递给__init__方法
  4. 重写__new__()方法一定要return super().new(cls)
class MusicPlayer(object):
    def __new__(cls, *args, **kwargs):
        print("创建对象,分配空间")
        return super().__new__(cls)

    def __init__(self):
        print("播放器初始化")
player1 = MusicPlayer()
player2 = MusicPlayer()
print(player1)
print(player2)

创建对象,分配空间
播放器初始化
创建对象,分配空间
播放器初始化
<__main__.MusicPlayer object at 0x0000028CFCFB3E50>
<__main__.MusicPlayer object at 0x0000028CFCFB3E20>
  1. 单例设计模式代码实现
class MusicPlayer(object):
    # 记录第一个被创建对象的引用
    instance = None

    def __new__(cls, *args, **kwargs):
        # 判断类属性是否为空对象
        if cls.instance is None:
            # 调用父类方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)
        return cls.instance

player1 = MusicPlayer()
player2 = MusicPlayer()
print(player1)
print(player2)

<__main__.MusicPlayer object at 0x0000024C8E613EE0>
<__main__.MusicPlayer object at 0x0000024C8E613EE0>

扩展:

  1. 每次创建对象时,python会自动调用两个方法:

    1. __new__分配空间
    2. __init__对象初始化
  2. 创建对象时还会调用初始化方法

  3. 需求:让初始化方法只执行一次

  4. 解决方法:

    1. 定义类属性init_flag标记是否执行初始化动作
    2. 在__init__中判断init_flag
    class MusicPlayer(object):
        # 记录第一个被创建对象的引用
        instance = None
        # 记录是否执行过初始化方法
        init_flag = False
        def __new__(cls, *args, **kwargs):
            # 判断类属性是否为空对象
            if cls.instance is None:
                # 调用父类方法,为第一个对象分配空间
                cls.instance = super().__new__(cls)
            return cls.instance
        def __init__(self):
            # 判断是否执行初始化动作
            if MusicPlayer.init_flag:
                return
            # 执行初始化动作
            print("初始化播放器")
            # 修改标记值
            MusicPlayer.init_flag = True
    
    player1 = MusicPlayer()
    player2 = MusicPlayer()
    print(player1)
    print(player2)
    
    初始化播放器
    <__main__.MusicPlayer object at 0x0000022EE6713E80>
    <__main__.MusicPlayer object at 0x0000022EE6713E80>
    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值