前言
在 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 私有属性可以让对象可以像操作“公开的实例变量”一样处理“私有属性” (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)