python学习面向对象Day11(P116~~126)

这是一篇关于Python面向对象编程的学习笔记,涵盖了封装的概念、私有化的实例以及使用property进行封装的方法。通过内存图解析和封装设计案例,深入理解面向对象的封装思想及其在实践中的应用。
摘要由CSDN通过智能技术生成

b站达内python课学习笔记

P116 Python面向对象–3.1 课程回顾

一、day10复习

"""
    day10 复习
    类和对象
        类:抽象   向量 class Vector2    str  int   list
        对象:具体  1,2   Vector2(1,2)   "a"   1    [1,2]
        之间的区别:类与类行为不同
              对象与对象数据不同
                 Vector2(1,2) Vector2(3,4)
                   同一个类型的多个对象,数据不同(1,2/3,4),行为(求方向,求大小)相同.

        类成员:
            实例:对象的数据(变量),对象的行为(方法).
            类:类的数据(变量),类的行为(方法).
                可以被所有对象共同操作的数据
            静态方法:
                实例方法操作实例变量,表示"个体"行为.
                类方法操作类变量,表示"大家"行为.
                静态方法不能操作数据,表示为函数都可以.
"""

二、示例代码

访问实例变量和实例方法


# ------------实例---------------
class MyClass:
    def __init__(self, a):
        # 实例变量
        self.a = a

    # 实例方法
    def print_self(self):
        # 可以操作实例变量
        print(self.a)


# 通过对象访问
m01 = MyClass(100)
m01.b = 1

m02 = MyClass(100)
print(m02.b)
print(m02.a)

访问类变量和类方法

# ------------类---------------
class MyClass02:
    # 类变量
    a = 0

    # 类方法
    @classmethod  # 自动传入当前方法的参数是类,而不是对象.
    def print_self(cls):
        # 可以操作类变量
        print(cls.a)

# 通过类名访问
print(MyClass02.a)
MyClass02.print_self()

不常见不建议用的访问方式

# ------------不常用的访问方式---------------
# 访问实例方法,还可以通过类。
MyClass.print_self(m01)  # 也必须传递对象

# 访问类成员,还可以通过对象
m03 = MyClass02()
print(m03.a)
print(m03.print_self())

P117 Python面向对象–3.2 课后作业解析

一、练习1

"""
[
    ["00", "01", "02", "03"],
    ["10", "11", "12", "13"],
    ["20", "21", "22", "23"],
]
在二维列表中,获取13位置,向左,3个元素
在二维列表中,获取22位置,向上,2个元素
在二维列表中,获取03位置,向下,2个元素
"""
class Vector2:
    """
        二维向量
        可以表示位置/方向
    """

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

    @staticmethod
    def left():
        return Vector2(0, -1)

    @staticmethod
    def right():
        return Vector2(0, 1)

    @staticmethod
    def up():
        return Vector2(-1, 0)

    @staticmethod
    def down():
        return Vector2(1, 0)


class DoubleListHelper:
    @staticmethod
    def get_elements(target, vect_pos, vect_dir, count):
        """
            在二维列表中获取指定位置,指定方向,指定数量的元素.
        :param target: 二维列表
        :param vect_pos: 指定位置
        :param vect_dir: 指定方向
        :param count: 指定数量
        :return: 列表
        """
        list_result = []
        for i in range(count):
            vect_pos.x += vect_dir.x
            vect_pos.y += vect_dir.y
            element = target[vect_pos.x][vect_pos.y]
            list_result.append(element)
        return list_result

# --------------测试代码-----------------
list01 = [
    ["00", "01", "02", "03"],
    ["10", "11", "12", "13"],
    ["20", "21", "22", "23"],
]
# 在二维列表中,获取13位置,向左,3个元素
re = DoubleListHelper.get_elements(list01, Vector2(1, 3), Vector2.left(), 3)
for item in re:
    print(item)
# 在二维列表中,获取22位置,向上,2个元素
re = DoubleListHelper.get_elements(list01, Vector2(2, 2), Vector2.up(), 2)
for item in re:
    print(item)
# 在二维列表中,获取03位置,向下,2个元素
re = DoubleListHelper.get_elements(list01, Vector2(0, 3), Vector2.down(), 2)
for item in re:
    print(item)

二、练习2:列表与对象结合

"""
4. 定义敌人类
    -- 数据:姓名,血量,基础攻击力,防御力
    -- 行为:打印个人信息

   创建敌人列表(至少4个元素),并画出内存图。
   查找姓名是"灭霸"的敌人对象
   查找所有死亡的敌人
   计算所有敌人的平均攻击力
   删除防御力小于10的敌人
   将所有敌人攻击力增加50
"""
class Enemy:
    def __init__(self, name, hp, atk, defense):
        self.name = name
        self.hp = hp
        self.atk = atk
        self.defense = defense

    def print_self_info(self):
        print(self.name, self.hp, self.atk, self.defense)


list01 = [
    Enemy("玄冥二老", 86, 120, 58),
    Enemy("成昆", 0, 100, 5),
    Enemy("谢逊", 120, 130, 60),
    Enemy("灭霸", 0, 1309, 690),
]


#  查找姓名是"灭霸"的敌人对象
def find01():
    for item in list01:
        if item.name == "灭霸":
            return item


e01 = find01()
# 如果又找到,返回值为None
# 所以可以判断不是空,再调用其实例方法.
# if e01 != None:
if e01:
    e01.print_self_info()
else:
    print("没找到")


# 查找所有死亡的敌人
def find02():
    list_result = []
    for item in list01:
        if item.hp == 0:
            list_result.append(item)
    return list_result


re = find02()
for item in re:
    item.print_self_info()

#  计算所有敌人的平均攻击力
def calculate01():
    sum_atk =0
    for item in list01:
        sum_atk += item.atk
    return sum_atk / len(list01)

print(calculate01())

# 删除防御力小于10的敌人
def delete01():
    for i in range(len(list01)-1,-1,-1):
        if list01[i].defense <10:
            del list01[i]

delete01()
for item in list01:
    item.print_self_info()

# 将所有敌人攻击力增加50
def set01():
    for item in list01:
        item.atk += 50

set01()
for item in list01:
    item.print_self_info()

P118 Python面向对象–3.3 封装01

一、封装思想

  1. 数据角度讲,将一些基本数据类型复合成一个自定义类型。
  2. 行为角度讲,向类外提供必要的功能,隐藏实现的细节。
"""
    封装数据:老婆(姓名,年龄,性别...)
            敌人(姓名,血量,攻击力,防御力)
            二维向量(x,y)
            优势:更符合人类的思考方式。
                 将数据与对数据的操作整合在一起.

    封装行为:二维列表助手类DoubleListHelper(获取多个元素get_elements)
            向量(向左/向右..,求模长,求方向..)
            优势:以“模块化”的方式进行编程
                (可以集中精力设计/组织/指挥多个类协同工作)
"""

P119 Python面向对象–3.4 封装02

一、实例变量私有化的原因、方式和实质

  1. 作用:无需向类外提供的成员,可以通过私有化进行屏蔽。
  2. 做法:命名使用双下划线开头。
  3. 本质:障眼法,实际也可以访问。
    私有成员的名称被修改为:_类名__成员名,可以通过_dict_属性或dir函数查看。
# 使用方法,封装变量.
class Wife:
    def __init__(self, name, age, weight):
        self.name = name
        # 本质:障眼法(实际将变量名改为:_类名__age)
        self.__age = age
		self.__weight = weight

w01 = Wife("铁锤公主", 87, 87)
# 重新创建了新实例变量(没有改变类中定义的__age)
# w01.__age = 107 
w01._Wife__age = 107  # (修改了类中定义的私有变量)

print(w01.__dict__)# __dict__ 是python内置变量,存储对象的实例变量.

二、实例变量私有化后如何合理的访问和修改

定义实例方法来访问和修改私有变量。并且为了使对象在实例化的时候也满足输入条件,需要将修改私有变量的实例方法在 __ init__内被调用。而不是直接在__ init__内写self.xxx = xxx,因为这样的代码在对象初始化的时候私有变量是可以随意赋值,没有限制

class Wife:
    def __init__(self, name, age, weight):
        self.name = name
        # 本质:障眼法(实际将变量名改为:_类名__age)
        # self.__age = age
        self.set_age(age)
        # self.__weight = weight
        self.set_weight(weight)

    # 提供公开的读写方法
    def get_age(self):
        return self.__age

    def set_age(self, value):
        if 21 <= value <= 31:
            self.__age = value
        else:
            raise ValueError("我不要")

    # 提供公开的读写方法
    def get_weight(self):
        return self.__weight

    def set_weight(self, value):
        if 40 <= value <= 60:
            self.__weight = value
        else:
            raise ValueError("我不要")

P120 Python面向对象–3.5 封装03

一、练习1

# 练习:定义敌人类(姓名,攻击力10 -- 50,血量100 -- 200)
#    创建一个敌人对象,可以修改数据,读取数据。

class Enemy:
    def __init__(self, name, hp, atk):
        self.name = name
        # self.__hp = hp
        self.set_hp(hp)
        # self.__atk = atk
        self.set_atk(atk)

    def get_atk(self):
        return self.__atk

    def set_atk(self, value):
        if 10 <= value <= 50:
            self.__atk = value
        else:
            raise ValueError("我不要")

    def get_hp(self):
        return self.__hp

    def set_hp(self, value):
        if 100 <= value <= 200:
            self.__hp = value
        else:
            raise ValueError("我不要")


e01 = Enemy("灭霸", 25, 120)
# e01.set_atk(20)
print(e01.get_atk())

二、使用property(读取方法,写入方法)对象,封装变量

上述代码的问题在于,会使得 __ init__内的实例变量初始化不明显,看上去是在调用实例方法,因此引入property,代码如下

"""
    使用property(读取方法,写入方法)对象,封装变量.
"""
class Wife:
    def __init__(self, name, age, weight):
        self.name = name
        # self.set_age(age)
        self.age = age
        # self.set_weight(weight)
        self.weight = weight

    def get_age(self):
        return self.__age

    def set_age(self, value):
        if 21 <= value <= 31:
            self.__age = value
        else:
            raise ValueError("我不要")

    # 属性  property对象拦截对age类变量的读写操作
    age = property(get_age,set_age)

    def get_weight(self):
        return self.__weight

    def set_weight(self, value):
        if 40 <= value <= 60:
            self.__weight = value
        else:
            raise ValueError("我不要")

    weight = property(get_weight,set_weight)

w01 = Wife("铁锤公主", 30, 50)
# w01.set_age(25)
w01.age = 25
print(w01.age)
w01.weight = 45
print(w01.weight)

P121 Python面向对象–3.6 练习解析(property解析)

以下代码分析:
首先,由类变量的定义可知,atk 和 hp 是 Enemy类 的类变量,它俩指向各自的property对象,property对象拦截了__ init__构造方法对这两个类变量的操作,而是将之转为对传入property的方法的操作,而方法内的变量则是 self.__atk 和 self.__hp

本质上还是用了实例方法来初始化和修改访问实例变量,只是借用类变量和property,使得 __ init__内部回到了原来的样子,看上去像是在初始化实例变量

理解为:
根据 atk = property(get_atk, set_atk) 和 self.atk = atk 
得到 self.property(get_atk, set_atk) = atk
又 get_atk() 和 set_atk() 的返回值为 __atk
因此得到 self.__atk = atk
# 练习:定义敌人类(姓名,攻击力10 -- 50,血量100 -- 200)
#    创建一个敌人对象,可以修改数据,读取数据。
#    使用property封装变量

class Enemy:
    def __init__(self, name, hp, atk):
        self.name = name
        # self.set_hp(hp)
        self.hp = hp
        # self.set_atk(atk)
        self.atk = atk

    def get_atk(self):
        return self.__atk

    def set_atk(self, value):
        if 10 <= value <= 50:
            self.__atk = value
        else:
            raise ValueError("我不要")

    atk = property(get_atk, set_atk)

    def get_hp(self):
        return self.__hp

    def set_hp(self, value):
        if 100 <= value <= 200:
            self.__hp = value
        else:
            raise ValueError("我不要")

    hp = property(get_hp, set_hp)


e01 = Enemy("灭霸", 100, 25)
e01.hp = 150
e01.atk = 30
print(e01.hp)
print(e01.__dict__)

P122 Python面向对象–3.7 内存图解析

一、内存图

在这里插入图片描述

补充

若修改上述代码的property的传入变量
如hp = property(get_hp, set_hp)改为hp = property(None, set_hp),则发现print(e01.hp)会失败,无法读取
如hp = property(get_hp, set_hp)改为hp = property(get_hp, None),则发现可读取,但无法修改hp变量

P123 Python面向对象–3.8 封装属性

一、property封装属性之最终版本

"""
    使用标准属性,封装变量.
"""

class Wife:
    def __init__(self, name, age, weight):
        self.name = name
        self.age = age
        self.weight = weight

    @property  # 创建property对象,只负责拦截读取操作
    def age(self):
        return self.__age

    @age.setter  # 只负责拦截写入操作
    def age(self, value):
        if 21 <= value <= 31:
            self.__age = value
        else:
            raise ValueError("我不要")

    @property
    def weight(self):
        return self.__weight

    @weight.setter
    def weight(self, value):
        if 40 <= value <= 60:
            self.__weight = value
        else:
            raise ValueError("我不要")

w01 = Wife("铁锤公主", 30, 50)
w01.age = 25
print(w01.age)
w01.weight = 45
print(w01.weight)

二、属性@property总结

公开的实例变量,缺少逻辑验证。私有的实例变量与两个公开的方法相结合,又使调用者的操作略显复杂。而属性可以将两个方法的使用方式像操作变量一样方便。
拦截对实例变量的读写操作

定义:

@property
def name(self):
    return self.__name

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

调用:

对象.属性名 = 数据
变量 = 对象.属性名

说明:

– 通常两个公开的属性,保护一个私有的变量。
– @property 负责读取,@属性名.setter 负责写入
– 只写:属性名= property(None, 写入方法名)

三、练习:改写为property形式

# 练习:定义敌人类(姓名,攻击力10 -- 50,血量100 -- 200)
#    创建一个敌人对象,可以修改数据,读取数据。
#    使用@property封装变量

class Enemy:
    def __init__(self, name, hp, atk):
        self.name = name
        self.atk = atk
        self.hp = hp

    @property
    def atk(self):
        return self.__atk

    @atk.setter
    def atk(self, value):
        if 10 <= value <= 50:
            self.__atk = value
        else:
            raise ValueError("我不要")

    @property
    def hp(self):
        return self.__hp

    @hp.setter
    def hp(self, value):
        if 100 <= value <= 200:
            self.__hp = value
        else:
            raise ValueError("我不要")


e01 = Enemy("灭霸", 100, 25)
e01.hp = 150
e01.atk = 30
print(e01.hp)
print(e01.__dict__)

P124 Python面向对象–3.9 封装设计01

一、封装设计的思想(以活字印刷取代雕版印刷为例)

  1. 设计角度讲:
    (1)分而治之
    – 将一个大的需求分解为许多类,每个类处理一个独立的功能。
    – 拆分好处:便于分工,便于复用,可扩展性强。
    (2) 变则疏之
    – 变化的地方独立封装,避免影响其他类。
    (3) 高 内 聚
    – 类中各个方法都在完成一项任务(单一职责的类)。
    (4) 低 耦 合
    – 类与类的关联性与依赖度要低(每个类独立),让一个类的改变,尽少影响其他类。
    [例如:硬件高度集成化,又要可插拔]
    最高的内聚莫过于类中仅包含1个方法,将会导致高内聚高耦合。
    最低的耦合莫过于类中包含所有方法,将会导致低耦合低内聚。

P125 Python面向对象–3.10 封装设计02

"""
    封装设计思想
        需求:老张开车去东北
"""


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

    @property
    def name(self):
        return self.__name

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

    def go_to(self, str_postion, type):
        """
            去
        :param str_postion: 位置
        :param type: 方式 
        """
        print(self.name, "去", str_postion)
        type.run(str_postion)


class Car:
    def run(self, str_position):
        """
            行驶
        :param str_position: 位置 
        """
        print("汽车行驶到:", str_position)


lz = Person("老张")
car = Car()
# 老张开车去东北
lz.go_to("东北", car)

P126 Python面向对象–3.11 练习解析及作业

"""
    请以面向对象的思想,描述下列场景:
        小明在招商银行取钱
"""


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

    @property
    def name(self):
        return self.__name

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

    @property
    def money(self):
        return self.__money

    @money.setter
    def money(self, value):
        self.__money = value


class Bank:
    def __init__(self, name, money):
        self.name = name
        self.money = money

    @property
    def name(self):
        return self.__name

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

    @property
    def money(self):
        return self.__money

    @money.setter
    def money(self, value):
        self.__money = value

    def draw_money(self, person, value):
        """
            取钱
        :param person:
        :param value:
        :return:
        """
        self.money -= value
        person.money += value
        print(person.name, "取了%d钱" % value)


xm = Person("小明", 0)
zsyh = Bank("招商", 100000)
zsyh.draw_money(xm, 10000)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值