面向对象进阶
@property修饰器
为什么要使用@property修饰器呢,刚开始我也比较晕乎乎的。后面看了廖雪峰老师的文档,有了一些理解。参考:《使用@proterty》
@proterty装饰器的作用就是给负责把一个方法变成属性调用。可以使用getter和setter方法,可以使得对属性的访问安全、方便。
敲一遍示例,感受一下用法。
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
# 修改器,意味着可以外界修改类里的属性值
@age.setter
def age(self, age):
self._age = age
def play(self):
if self._age <= 18:
print('%s正在玩飞行棋。' % self._name)
else:
print('%s正在玩斗地主。' % self._name)
def main():
person = Person('llch', 18)
person.play()
# 外界修改属性值
person.age = 22
person.play()
if __name__ == '__main__':
main()
slots
Python是一门动态语言,这就允许我们可以在程序运行时给对象绑定新的属性或方法,也可以对已经绑定的属性和方法进行解绑定。__slots__
是用来限制添加实例的属性,这个限制只对当前类的对象生效,对子类不起任何作用。
静态方法和类方法
静态方法
静态方法可以定义在类中,但是没有传入参数self,也就代表不是对象本身的方法。可以在对象未创建时调用,调用写法和调用对象方法一样。
同时,在编写静态方法时,要加上@staticmethod修饰器
类方法
Python可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象,通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象。同样,类方法要加修饰器@classmethod
敲示例代码,感受一下。
from time import time, localtime, sleep
class Clock:
def __init__(self, hour=0, minute=0, second=0):
self._hour = hour
self._minute = minute
self._second = second
@classmethod
def now(cls):
t = localtime(time())
return cls(t.tm_hour, t.tm_min, t.tm_sec)
def run(self):
"""走字"""
self._second += 1
if self._second == 60:
self._second = 0
self._minute += 1
if self._minute == 60:
self._minute = 0
self._hour += 1
if self._hour == 24:
self._hour = 0
def show(self):
return '%02d:%02d:%02d' % (self._hour, self._minute, self._second)
def main():
# 通过类方法创建对象并获取系统时间
clock = Clock.now()
while True:
print(clock.show())
sleep(1)
clock.run()
if __name__ == '__main__':
main()
类之间的关系
类与类之间有三种关系:
- 继承或泛化。
- 关联。关联关系如果是整体和部分的关联,称为聚合关系;如果整体进一步负责部分的生命周期,也就是说整体和部分相互依赖,同生共死,则称为合成关系。
- 依赖。好比a用了b的东西,那么就可以说a与b是依赖关系,a依赖b。
继承和多态
提供继承信息的类称为父类,也叫超类或基类;得到继承信息的类称为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有更多的能力。继承父类使用关键字super
。
子类继承父类后,可以对父类方法重新实现,称为方法重写。通过方法重写我们可以让父类的同一个行为在子类中拥有不同版本,当我们调用这个经过子类重写的方法时,不同子类对象会表现出不同的行为,称之为多态。
敲个例子,感受一下。
from abc import ABCMeta, abstractmethod
class Pet(object, metaclass=ABCMeta):
def __init__(self, nickname):
self._nickname = nickname
@abstractmethod
def make_voice(self):
pass
class Dog(Pet):
def make_voice(self):
print('%s: 汪汪汪...' % self._nickname)
class Cat(Pet):
def make_voice(self):
print('%s: 喵...喵...' % self._nickname)
def main():
pets = [Dog('旺财'), Cat('凯蒂'), Dog('大黄')]
for pet in pets:
pet.make_voice()
if __name__ == '__main__':
main()
上述代码中,Pet类是一个抽象类,抽象类意思就是不能被实例化的类,只能被其他类继承。Python不提供抽象类的支持,但是可以通过abc模块的ABCMeta元类和abstractmethod修饰器来达到抽象类的效果。如果类中存在抽象方法,则不能被实例化。
综合案例
案例1:奥特曼打小怪兽
例子不在多。仔细敲,仔细感受,先把逻辑捋清楚在动手写。
from abc import ABCMeta, abstractmethod
from random import randint, randrange
class Fighter(object, metaclass=ABCMeta):
"""战斗者"""
# 通过__slots__魔法限定对象可以绑定的成员变量
__slots__ = ('_name', '_hp')
def __init__(self, name, hp):
"""初始化方法
:param name: 名字
:param hp: 生命值
"""
self._name = name
self._hp = hp
@property
def name(self):
return self._name
@property
def hp(self):
return self._hp
@hp.setter
def hp(self, hp):
self._hp = hp if hp >= 0 else 0
@property
def alive(self):
return self._hp > 0
@abstractmethod
def attack(self, other):
"""攻击
:param other: 被攻击的对象
"""
pass
class Ultraman(Fighter):
"""奥特曼"""
__slots__ = ('_name', '_hp', '_mp')
def __init__(self, name, hp, mp):
"""初始化方法
:param name: 名字
:param hp: 生命值
:param mp: 魔法值
"""
super().__init__(name, hp)
self._mp = mp
def attack(self, other):
other.hp -= randint(15, 25)
def huge_attack(self, other):
"""究极必杀技(打掉对方至少50点或四分之三的血)
:param other: 被攻击的对象
:return: 使用成功返回True否则返回False
"""
if self._mp >= 50:
self._mp -= 50
injury = other.hp * 3 // 4
injury = injury if injury >= 50 else 50
other.hp -= injury
return True
else:
self.attack(other)
return False
def magic_attack(self, others):
"""魔法攻击
:param others: 被攻击的群体
:return: 使用魔法成功返回True否则返回False
"""
if self._mp >= 20:
self._mp -= 20
for temp in others:
if temp.alive:
temp.hp -= randint(10, 15)
return True
else:
return False
def resume(self):
"""恢复魔法值"""
incr_point = randint(1, 10)
self._mp += incr_point
return incr_point
def __str__(self):
return '~~~%s奥特曼~~~\n' % self._name + \
'生命值: %d\n' % self._hp + \
'魔法值: %d\n' % self._mp
class Monster(Fighter):
"""小怪兽"""
__slots__ = ('_name', '_hp')
def attack(self, other):
other.hp -= randint(10, 20)
def __str__(self):
return '~~~%s小怪兽~~~\n' % self._name + \
'生命值: %d\n' % self._hp
def is_any_alive(monsters):
"""判断有没有小怪兽是活着的"""
for monster in monsters:
if monster.alive > 0:
return True
return False
def select_alive_one(monsters):
"""选中一只活着的小怪兽"""
monsters_len = len(monsters)
while True:
index = randrange(monsters_len)
monster = monsters[index]
if monster.alive > 0:
return monster
def display_info(ultraman, monsters):
"""显示奥特曼和小怪兽的信息"""
print(ultraman)
for monster in monsters:
print(monster, end='')
def main():
u = Ultraman('llch', 1000, 120)
m1 = Monster('狄仁杰', 250)
m2 = Monster('白元芳', 500)
m3 = Monster('王大锤', 750)
ms = [m1, m2, m3]
fight_round = 1
while u.alive and is_any_alive(ms):
print('========第%02d回合========' % fight_round)
m = select_alive_one(ms) # 选中一只小怪兽
skill = randint(1, 10) # 通过随机数选择使用哪种技能
if skill <= 6: # 60%的概率使用普通攻击
print('%s使用普通攻击打了%s.' % (u.name, m.name))
u.attack(m)
print('%s的魔法值恢复了%d点.' % (u.name, u.resume()))
elif skill <= 9: # 30%的概率使用魔法攻击(可能因魔法值不足而失败)
if u.magic_attack(ms):
print('%s使用了魔法攻击.' % u.name)
else:
print('%s使用魔法失败.' % u.name)
else: # 10%的概率使用究极必杀技(如果魔法值不足则使用普通攻击)
if u.huge_attack(m):
print('%s使用究极必杀技虐了%s.' % (u.name, m.name))
else:
print('%s使用普通攻击打了%s.' % (u.name, m.name))
print('%s的魔法值恢复了%d点.' % (u.name, u.resume()))
if m.alive > 0: # 如果选中的小怪兽没有死就回击奥特曼
print('%s回击了%s.' % (m.name, u.name))
m.attack(u)
display_info(u, ms) # 每个回合结束后显示奥特曼和小怪兽的信息
fight_round += 1
print('\n========战斗结束!========\n')
if u.alive > 0:
print('%s奥特曼胜利!' % u.name)
else:
print('小怪兽胜利!')
if __name__ == '__main__':
main()