学习笔记
开发工具:Spyder
文章目录
- 多态
- 例子(判断哪些情况是多态)
- 重写
- 内置可重写函数
- 举个例子1(`__str__`函数)
- 举个例子2(`__repr__`函数)
- 运算符重载
- 算数运算符(对象在运算符左边)
- 例子(对象 + #)
- 反向算数运算符重载(对象在运算符右边)
- 例子(# + 对象)
- 复合运算符
- 例子(对象 += #)
- 比较运算符重载
- 例子
- 设计原则
- 设计原则
- 类的单一职责
- 依赖倒置
- 组合复用原则
- 里氏替换
- 迪米特法则
多态
- 定义
父类的同一种动作或者行为,在不同的子类上有不同的实现。
- 作用
①继承将相关概念的共性进行抽象,多态则在共性的基础上,体现类型的个性化(一个行为有不同的实现)。
②增强程序扩展性,体现开闭原则。
备注:开闭原则为对扩展开放,对修改关闭。
例子(判断哪些情况是多态)
类代码:
class Weapon:"""
武器
"""
def __init__(self,atk):
self.atk = atk
def buy(self):
print("购买武器")
def attack(self):
# 子类如果没有当前方法,就会报错
raise NotImplementedError()
class Gun(Weapon):
"""
枪
"""
def __init__(self,atk,speed):
super().__init__(atk)
self.att_speed = speed
def attack(self):
print("开枪")
class Grenade(Weapon):
"""
手雷
"""
def __init__(self, atk, range):
super().__init__(atk)
self.explode_range = range
def attack(self):
print("爆炸")
问:下面这两段代码,体现了多态么?
g01 = Gun(10, 1)g01.buy()
g01.attack()gren01 = Grenade(30, 5)
gren01.buy()
gren01.attack()
答:都没有,多态要求调用父类,执行子类,以上两段代码都是调用子类,所以均不是多态。
那什么时候才是多态呢?看下面一段代码:
g01 = Gun(10, 1)def my_use(weapon):
weapon.attack() #调用父类
my_use(g01) #执行子类
在这里,我们定义了一个my_use()方法。
在my_use()方法中,我们想要用父类的实例对象weapon来调用attack()方法,但实际上传入的参数为子类实例对象g01,所以在执行时,执行的是子类的attack()方法。这时,就满足了多态的要求,即调用父类,执行子类。这里的调用父类,即我们认为的观念上的父类,但实际传入的实例对象为子类的实例对象。
PS:个人理解,若有错误,请求指出。
重写
子类实现了父类中相同的方法(方法名、参数),在调用该方法时,实际调用的是子类的方法。
内置可重写函数
在python中,以双下划线开头,并以双下划线结尾的是,系统定义的成员。我们可以在自定义类中进行重写,进而改变其行为。
比如:
__str__函数:将对象转换为字符串(对人友好的)
__repr__函数:将对象转换为字符串(解释器可识别的)
举个例子1(__str__函数)
代码:
class BunnyA:def __init__(self, name, age):
self.name = name
self.age = age
class BunnyB:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return "我叫%s,我是一只%d个月大的兔兔" % (self.name, self.age)
b0A = BunnyA("大白", 8)
print(b0A)
b0B = BunnyB("小黄", 7)
print(b0B)
结果:
举个例子2(__repr__函数)
代码:
class BunnyA:def __init__(self, name, age):
self.name = name
self.age = age
class BunnyB:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
#返回给人看
return "我叫%s,我是一只%d个月大的兔兔" % (self.name, self.age)
def __repr__(self):
#返回给解释器看
return "BunnyB('%s', %d)" % (self.name, self.age)
b0A = BunnyA("大白", 8)
print(b0A)
print(b0A.__repr__())
print("-----------------")
b0B = BunnyB("小黄", 7)
print(b0B)
print(b0B.__repr__())
print("-----------------")
b0B2 = eval(b0B.__repr__())
print(b0B2)
结果:
备注:eval()函数,可以在其中放入字符串(python代码的字符串),然后执行。
举个例子
运算符重载
运算符重载可以让自定义的类生成的对象(实例)能使用运算符进行操作。
算数运算符(对象在运算符左边)
例子(对象 + #)
代码:
class Vector:def __init__(self, x):
self.x = x
def __str__(self):
return "向量的x变量是:%s"%self.x
# 对象 +
def __add__(self, other):
return Vector(self.x + other)
v01 = Vector(10)
v02 = v01 + 5
print(v02)
结果:
反向算数运算符重载(对象在运算符右边)
例子(# + 对象)
代码:
class Vector:def __init__(self, x):
self.x = x
def __str__(self):
return "向量的x变量是:%s"%self.x
# 对象 +
def __add__(self, other):
return Vector(self.x + other)
# + 对象
def __radd__(self, other):
return Vector(self.x + other)
v01 = Vector(10)
v02 = 1 + v01
print(v02)
结果:
复合运算符
运用反向算数运算符来实现【+=】会创造新的对象,若我们不希望创建新的对象,且在原有对象上实现【+=】,则可以用复合运算符
例子(对象 += #)
代码:
class Vector:def __init__(self, x):
self.x = x
def __str__(self):
return "向量的x变量是:%s"%self.x
# 对象 +
def __add__(self, other):
return Vector(self.x + other)
# + 对象
def __radd__(self, other):
return Vector(self.x + other)
# 累加:在原有对象基础上进行操作,不创建新对象.
def __iadd__(self, other):
self.x += other
return self
v01 = Vector(10)
print(id(v01))
v01 += 1
print(v01)
print(id(v01))
结果:
比较运算符重载
例子
代码:
class Vector:def __init__(self, x):
self.x = x
def __str__(self):
return "向量的x变量是:%s"%self.x
# 对象 +
def __add__(self, other):
return Vector(self.x + other)
# + 对象
def __radd__(self, other):
return Vector(self.x + other)
def __lt__(self, other):
return self.x < other
v01 = Vector(10)
print(v01 < 5)
结果:
设计原则
设计原则
对扩展开放,对修改关闭。
增加新功能,不改变原有代码。
类的单一职责
一个类有且只有一个改变它的原因。
依赖倒置
客户端代码(调用的类)尽量依赖(使用)抽象的组件。
抽象的是稳定的。实现是多变的。
组合复用原则
如果仅仅为了代码复用优先选择组合复用,而非继承复用。
组合的耦合性相对继承低。
里氏替换
父类出现的地方可以被子类替换,在替换后依然保持原功能。
子类要拥有父类的所有功能。
子类在重写父类方法时,尽量选择扩展重写,防止改变了功能。
迪米特法则
类与类交互时,在满足功能要求的基础上,传递的数据量越少越好。因为这样可能降低耦合度。