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
一、封装思想
- 数据角度讲,将一些基本数据类型复合成一个自定义类型。
- 行为角度讲,向类外提供必要的功能,隐藏实现的细节。
"""
封装数据:老婆(姓名,年龄,性别...)
敌人(姓名,血量,攻击力,防御力)
二维向量(x,y)
优势:更符合人类的思考方式。
将数据与对数据的操作整合在一起.
封装行为:二维列表助手类DoubleListHelper(获取多个元素get_elements)
向量(向左/向右..,求模长,求方向..)
优势:以“模块化”的方式进行编程
(可以集中精力设计/组织/指挥多个类协同工作)
"""
P119 Python面向对象–3.4 封装02
一、实例变量私有化的原因、方式和实质
- 作用:无需向类外提供的成员,可以通过私有化进行屏蔽。
- 做法:命名使用双下划线开头。
- 本质:障眼法,实际也可以访问。
私有成员的名称被修改为:_类名__成员名,可以通过_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)分而治之
– 将一个大的需求分解为许多类,每个类处理一个独立的功能。
– 拆分好处:便于分工,便于复用,可扩展性强。
(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)