Python面向对象-封装

前言

在 Python入门基础中,可能有些知识点没听懂。听,你不一定能完全听懂,但是还是那件事,不放弃,不抛弃,一个点听不动,好,就是那一个点听不懂。从战略上要蔑视那一个点,继续往下走......最后,总体上的胜利就是成功了。
知识不要纠结一个点,人生亦是如此。认真对待每一个过程,但是不要纠结过不去。

面向过程

关注过程(细节) “干”

面向对象

关心解决问题的人 “找”
(1)识别对象,找人
(2)分配职责,干活 (行为 => 数据)
(3)建立交换,调用

面向对象设计过程例子:

面向对象设计过程例子

理解图

面向对象设计:先有具体对象,再从具体对象抽象出类。

面向对象设计
  • OOA: 面向对象分析
  • OOD: 面向对象设计
  • OOP: 面向对象编程
类与对象
  • 类: 抽象的概念
    数据成员:
    行为成员:
    如何划分“类”?
    两个字“行为”, 干的事情不同 (函数/方法 不同)
  • 对象: 具体的事物
    数据成员:
    行为成员:
    对象与对象有什么不同?
    数据不同,我们都知道“对象”内存空间只存储了“数据成员”
"""
练习1: 面向对象基础语法

"""


class Wifi:
    # 数据成员
    def __init__(self, name, sex):
        # self 是调用当前方法的对象地址
        self.name = name  # 创建实例变量
        self.sex = sex

    # 行为成员
    def play(self):
        print(self.name + "玩耍") # 调用实例变量


# 创建对象,实际在调用 __init__ 方法
w01 = Wifi("莉莉", "女")  # 自动将对象地址传入方法
w01.play()  # 自动将对象地址传入方法


创建实例内存图
实例成员与类成员

也称为实例变量与类变量

  • 实例变量
    练习对象的数据成员修改:
    实例成员也就是对象成员,实例成员包括两种:实例变量 + 实例方法
  • 实例变量语法
    定义:只要遵循 对象.变量名 就是在定义实例变量:
#(1)首次通过对象创建后赋值。(不鼓励)
w01 = Wife()
w01.name = "小张"   # 这种语法可以,但是不鼓励!!!
#(2)通常做法是构造函数(__init__)中创建。
def __init__(self, name)
      self.name = name
#  (3) 每个对象存储一份,通过对象地址调用
  • 实例方法
    实例方法表示对象的行为。同一个方法,操作不同的数据。
    无论创建多少个对象,方法都只有一份,并且被所有对象共享
类对象数据成员修改内存图
  • 类成员
    类成员包括: 类变量 + 类方法
    重点,类变量在方法区共用,对象变量独自开辟一个变量内存

    类变量成员内存图

  • 语法
    定义:在类中,方法外定义的变量都叫做“类变量”。
    class 类名:
    变量名 = 表达式
    调用:类名.变量名
    不建议通过对象访问类变量

  • 作用
    只存储一份在类中,被所有对象共享。

  • 类方法
    定义:
    @classmethod
    def 方法名称(cls, 参数列表)

"""
练习4: 实例变量与类变量

"""
class ICBC:
    # 定义“类变量”,类共用的变量
    total_money = 1200000

    # 定义类方法
    @classmethod
    def get_total_money(cls):
        print("总行还剩余:%d 元" % cls.total_money)

    # 定义类实例构造函数
    def __init__(self, name, money):
        self.name = name  # 定义“实例变量”
        self.money = money
        ICBC.total_money -= money  # 调用“类变量”,直接用类名

    # 定义实例方法
    def print(self):
        print("%s银行存款为:%d" % (self.name, self.money))  # 调用“实例变量”


bank01 = ICBC('曹操分行', 100000)
bank01.print()
bank02 = ICBC('刘备分行', 20000)
bank02.print()
bank03 = ICBC('孙权分行', 80000)
bank03.print()

ICBC.get_total_money()

  • 类静态方法
    定义:
    class 类名
    @staticmethod
    def 静态方法名
    方法体
    作用:将函数移入类中,函数既不属于类方法,又不属于对象方法,即可以定义为类静态方法。
class Vector:
    """
        二维向量
        可以表示位置/方向
    """

    def __init__(self, x, y):
        self.x = x
        self.y = y

    # 静态方法:表示左边方向
    @staticmethod
    def left():
        return Vector(0, -1)

    # 静态方法:表示右边方向
    @staticmethod
    def right():
        return Vector(0, 1)
封装
  • 从数据角度讲,将一些基本数据类型复合成一个自定义类型
    或者说:“将多个变量封装到一个自定义类中”
    封装数据():
    例如: 老婆(姓名,年龄,性别)
    敌人(姓名,血量,攻击力,防御力)
    二维向量 (x, y)
    面向方法,只能就叫“存储数据”,例如,字典、列表。
    封装数据(类)好处:可以用一个类名来表示你封装后的产物,代码结果清晰,事务更加明确。
    优势:更符合人类的思考方式。
    将数据与对数据的操作整合在一起。

  • 从行为角度讲,类向外提供必要(public)的功能,隐藏(private)实现的细节。

封装行为:例如,二维列表助手类(获取多个元素)
向量(向左,向右.....)
好处:以“模块化”的方式进行编程(类似一个函数只做一件事情),一个大的需求,需要分而治之。我们可以不用理会一个“模块”内部是怎么做的,只需要知道怎么用就可以,把模块串联起来即可。可以集中精力设计、组织、指挥多个类协调工作。

  • 私有成员
    做法: 命名使用双下划线开头。只是一种规定,实际还是可以访问到。
    def __私有方法
    __私有变量

重点:这里因为外层已经把 y 定义为类变量了,self.y = property 对象, 9999 赋值给 property 对象
8假如没有类变量 y (或者 类变量 y = 1000),这时 self.y 就属于 init 方法内部变量
self.y 关键在于看看它本身是什么

"""
练习10:封装练习 - 私有成员 - property
"""
class MyClass:

    def __init__(self):
        # 重点:这里因为外层已经把 y 定义为类变量了,self.y = property 对象, 9999 赋值给 property 对象
        # 假如没有类变量 y (或者 类变量 y = 1000),这时 self.y 就属于 __init__ 方法内部变量
        # self.y 关键在于看看它本身是什么
        self.y = 9999

    def get_y(self):
        return self.__y

    def set_y(self, value):
        self.__y = value

    y = property(get_y, set_y)


my01 = MyClass()
print(my01.__dict__)
property内存图
  • 封装 - 只读,只写属性


    封装 - 只写属性

    封装 - 只读属性
  • property 私有属性
    property 私有属性可以让对象可以像操作“公开的实例变量”一样处理“私有属性” (2 个方法), 即是两个公开的属性,保护一个私有的变量
    --@property 负责读取,@属性名.setter 负责写入
    --只写:属性名=property(None, 写入方法名)
  • property 私有属性定义:
    @property
    def name(self):
    return self.__name

@name.setter
def name(self, value)
self.__name = value

  • 使用 @property 封装私有变量
class Enemy:

    def __init__(self, name, attack, blood):
        self.name = name
        self.attack = attack
        self.blood = blood

    @property
    def attack(self, value):
        self.__attack = value

    @attack.setter
    def attack(self, value):
        if 10 <= value <= 50:
            self.__attack = value
        else:
            raise ValueError("攻击力范围错误")

    @property
    def blood(self):
        return self.__blood

    @blood.setter
    def blood(self, value):
        if 100 <= value <= 200:
            self.__blood = value
        else:
            raise ValueError("血量范围错误")


enemy01 = Enemy('骷髅头', 50, 100)
enemy01.blood = 120
print(enemy01.__dict__)
从设计角度讲封装的思想(重点)

(1)分而治之(总思想:分
---将一个大的需求分解为许多类,每一个类处理一个独立的功能。
---拆分的好处:便于分工,便于复用,可扩展性强。
---思想指导:活字印刷术
(2)变则疏之(对第一点补充,“分”的“度”,“点”在哪?
---分而治之,分得有一个“度”,什么时候该拆分成一个小的分类,就看它会不会变化,会变化就拆分成一个小的分类。
---变化的地方独立封装,避免影响其他的类
---分得精髓在乎一个“变”
(3) 高内聚(审查评价评价上面两步骤做得好吗?)
---内聚:一个类的内部干了多少件事
---高:一个类干的事情都能凝聚到一个点,叫“高”
---高内聚:类中的各个方法都在完成一项任务(单一职责)
---高内聚:评价一个类的内部是否都只做“一件事情”
---若不是,重构,提出去!!!

(4)低耦合 (讲的是类与类之间的关系,十有八九在说设计模式
---类与类的关联性与依赖度要低

例如:硬件高度集成化,又要可插拔
最高的内聚莫过于类中只有1个方法,将会导致高内聚高耦合。
最低的耦合莫过于类中包含所有的方法,将会导致低耦合低内聚。
所有高内聚,低耦合讲究一个平衡

  • slots: 限制类创建的对象只能有固定的类实例变量

体会: 对象区分数据的不同

"""
练习12:封装练习 - 体会变化点是数据的不同还是行为的不同

张无忌   教    赵敏   九阳神功
赵敏     教    张无忌  化妆
张无忌   上班  挣了    10000
赵敏    上班   挣了    8000

体会: 对象区分数据的不同
"""


class Person:
    def __init__(self, name):
        self.name = name

    def teach(self, other, action):
        print("%s 教 %s %s" % (self.name, other.name, action))

    def work(self, money):
        print("%s 上班,挣了 %d 元" % (self.name, money))


p1 = Person('张无忌')
p2 = Person('赵敏')
p1.teach(p2, '九阳神功')
p2.teach(p1, '化妆')
p1.work(10000)
p2.work(8000)

体会: 类区别行为的不同

"""
练习13:封装练习 - 体会变化点是数据的不同还是行为的不同
玩家(攻击力) 攻击 敌人(血量), 受伤(掉血),还可能死亡(掉装备)
敌人(攻击力) 攻击 玩家(血量), 受伤(掉血/碎屏),还可能死亡
体会: 类区别行为的不同
"""


class Player:

    def __init__(self, atk, hp):
        self.atk = atk
        self.hp = hp

    def attack(self, other):
        print("玩家攻打敌人:")
        other.damage(self.atk)

    def damage(self, atk):
        self.hp -= atk
        if self.hp <= 0:
            self.__death()

    @staticmethod
    def __death(self):
        print("玩家死亡")
        print("游戏结束")


class Enemy:

    def __init__(self, atk, hp):
        self.atk = atk
        self.hp = hp

    def damage(self, atk):
        self.hp -= atk
        print("敌人受伤了:")
        if self.hp <= 0:
            self.__die()

    @staticmethod
    def __die():
        print("敌人死亡")
        print("掉装备")

    def attack(self, other):
        print("敌人攻打玩家:")
        other.damage(self.atk)


player = Player(100, 1000)
enemy = Enemy(10, 200)
player.attack(enemy)
enemy.attack(player)
player.attack(enemy)

你可能感兴趣的:(Python面向对象-封装)