三、面向对象的三大支柱
3.2 继承
3.2.1 什么是继承
继承是面向对象编程中的核心概念,一个类继承另一个类的功能,并可以增加新的功能,成为类的继承。前者称为子类(基类),后者称为父类(派生类)。
class Baseclass:
# 基类的属性和方法定义
pass
class Drivedclass(Baseclass):
# 派生类的属性和方法定义
pass
其中,子类可以继承父类全部的属性和方法,并可以增加新的属性、方法。例如:
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):
if not isinstance(age,(int,float)):
raise ValueError('age must be a number.')
elif age <= 0:
raise ValueError('age cannot be negative or equal to zero.')
else:
self._age = age
def watch(self):
if self._age > 18:
print(f'{self.name} will watch movie at weekends.')
else:
print(f'{self.name} will watch documentary at weekends.')
class Student(Person):
"""学生,子类"""
def __init__(self,name,age,course):
""" 在子类中调用父类的构造方法__init__需要用super()"""
super().__init__(name,age) # 调用父类的构造方法
self._course = course
@property
def course(self):
return self._course
def learn(self):
print(f'{self._name} will learn {self._course} next year.')
def main():
# 定义一个子类对象
stu = Student('joy',15,'math')
# 访问父类的属性
print(stu.name) # joy
# 访问子类的属性
print(stu.course) # math
# 修改父类属性
stu.age = 14
print(stu.age) # 14
# 调用父类的方法
stu.watch() # joy will watch documentary at weekends.
# 调用子类方法
stu.learn() # joy will learn math next year.
if __name__ == '__main__':
main()
3.2.2 里氏替换原则
- 子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力。
- 在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为里氏替换原则,其核心思想是:子类必须能够替换其父类。
- 即在软件工程中,如果我们用子类的对象替换掉所有使用父类对象的地方,那么程序的行为不会发生变化。
- 这个原则的关键点是继承和扩展:子类可以扩展父类的功能,但不能改变父类原有的功能。这意味着子类应该只添加新的方法或属性,而不是改变父类原有的方法。
3.2.3 方法重写
- 子类不能改变父类的方法,可以继承或者重写。
- 子类可以重写父类的方法。当子类定义了一个与父类同名的方法时,子类的方法会覆盖父类的方法。
- 重写的方法应该具有与父类方法相同的方法签名(即方法名和参数列表应该相同)。
3.2.4 继承的注意事项
__init__ | 1、子类在创建时,会首先调用父类的构造方法__init__。如果子类没有定义自己的构造方法,它将直接使用父类的构造方法。 2、如果子类需要扩展父类的构造方法,它应该首先调用super()来确保父类的构造方法被正确执行。 |
方法重写 | 1、 子类可以重写父类的方法。当子类定义了一个与父类同名的方法时,子类的方法会覆盖父类的方法。 2、重写的方法应该具有与父类方法相同的方法签名(即方法名和参数列表应该相同)。 |
属性的隐藏 | 1、子类可以访问父类的属性,包括实例属性和类属性。 2、如果子类定义了一个与父类同名的属性,那么父类的属性将被“隐藏”,而不是被覆盖。这称为属性隐藏,而不是属性重写。 |
继承和封装 | 继承是一种代码重用机制,但也需要注意封装原则。子类应该只依赖父类的公共接口(即公共方法和属性),而不是直接访问父类的私有属性或方法 |
3.3 多态
3.3.1 什么是多态
- 通过方法重写,可以让父类的方法在不同子类中拥有不同版本,进而当调用重写的方法时,不同的子类对象会表现出不同的行为,这就是多态。
- 抽象方法:在Python中,抽象方法通常通过抽象基类(ABC,Abstract Base Classes)来实现。Python的abc模块提供了
ABC/ABCMeta
和abstractmethod
装饰器来定义抽象基类和抽象方法。抽象基类是一个不能被实例化的类,它至少包含一个抽象方法(不需要再父类实现,必须在子类实现)。抽象方法是一个必须在任何直接子类中被实现的方法。
from abc import ABCMeta,abstractmethod
class Pet(metaclass=ABCMeta): #也可写作Animal(ABC),表示继承ABC基类,效果等同继承ABCMeta元类
"""动物"""
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@abstractmethod # 定义一个抽象方法
def voice(self):
pass # 父类中不需要提供具体实现,但必须在子类中实现
class Dog(Pet):
"""狗"""
def voice(self):
"""重写voice方法"""
print('%s:汪汪汪' % self._name)
class Cat(Pet):
"""猫"""
def voice(self):
"""重写voice方法"""
print('%s:喵喵喵' % self._name)
def main():
# 创建子类猫狗的对象
pets = [Dog('Alan'),Dog('Zoe'),Cat('Amy')] # 子类对象
# 全部调用voice方法
for i in pets:
i.voice()
if __name__ == '__main__':
main()
四、练习
4.1 定义一个类描述三条线构成的三角形周长和面积
"""
1、定义一个类求三条边构成的三角形周长和面积
① 验证三条边能否构成三角
② 计算周长、面积
"""
class Triangle:
"""三角形"""
# __slots__ = ['a', 'b', 'c']
def __init__(self, a, b, c):
# 三条边a,b,c
self._a = a
self._b = b
self._c = c
"""验证是否构成三角形"""
@staticmethod
def test(a, b, c):
return a + b > c and a + c > b and b + c > a
"""只读a"""
@property
def a(self):
return self._a
"""只读b"""
@property
def b(self):
return self._b
"""只读c"""
@property
def c(self):
return self._c
@a.setter
def a(self, x):
self._a = x
return self._a
@b.setter
def b(self, x):
self._b = x
return self._b
@c.setter
def c(self, x):
self._c = x
return self._c
def l(self):
print('三角形周长: %d' % int(self._a + self._b + self._c))
def s(self):
p = (self._a + self._b + self._c)/2
print('三角形面积: %.2f' % float((p * (p - self._a) * (p - self._b) * (p - self._c)) ** 0.5))
def main():
# 定义一个对象
t1 = Triangle(1, 4, 5)
# 验证是否构成三角,如果构成,则求面积,调用静态方法建议用类名调用
if Triangle.test(t1.a, t1.b, t1.c):
print('a=%d, b=%d, c=%d' % (t1.a, t1.b, t1.c))
t1.l()
t1.s()
# 如果不能构成,让用户选择是否修改
else:
while True:
print('您输入的边长无法构成三角形')
back = input('是否选择修改?(y/n)')
if back == 'y':
while True:
try:
a = int(input('请输入新边长a'))
b = int(input('请输入新边长b'))
c = int(input('请输入新边长c'))
# while not t1.test(t1.a, t1.b, t1.c):
if Triangle.test(a, b, c):
t1.a = a
t1.b = b
t1.c = c
print(t1.a, t1.b, t1.c)
t1.l()
t1.s()
break
else:
print('您输入的边长无法构成三角形,请重新输入')
except ValueError:
print('请输入有效的整数')
elif back == 'n':
print('您拒绝修改,未构成三角形,流程结束')
break
else:
print('回复不合规,请重新输入y or n')
if __name__ == '__main__':
main()
4.2 定义一个类描述数字时钟
from time import localtime, time, sleep
class Clock:
def __init__(self, h, m, s):
self._h = h
self._m = m
self._s = s
"""类方法,后续用于创造一个等于当前时间的对象"""
@classmethod
def localtime(cls):
now = time() # 返回当前时间的时间戳
t = localtime(now) # 获取当前时间的struct_time元组
return cls(t.tm_hour, t.tm_min, t.tm_sec) # 返回一个h,m,s等于当前时间的对象
def clock(self):
"""这是一个无限循环时钟,每秒秒+1,秒满60进1,分满60进1,小时满24返0"""
while self._s < 60:
sleep(1)
self._s += 1
if self._s == 60:
self._s -= 60
self._m += 1
if self._m == 60:
self._m -= 60
self._h += 1
if self._h == 24:
self._h -= 24
print('%02d:%02d:%02d' % (self._h, self._m, self._s))
def main():
t = Clock.localtime() # 创建一个h,m,s等于tm_hour,tm_min,tm_sec的对象
t.clock() # 调用方法,创建无限循环时钟
if __name__ == '__main__':
main()
# 骆昊大神的标准答案:
"""
from time import time, localtime, sleep
class Clock(object):
"""数字时钟"""
def __init__(self, hour=0, minute=0, second=0):
self._hour = hour
self._minute = minute
self._second = second
@classmethod
def now(cls):
ctime = localtime(time())
return cls(ctime.tm_hour, ctime.tm_min, ctime.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()
"""
4.3 定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法
class Dot:
"""
定义一个类,功能:
① 描述点的位置
② 获取点移动后的位置
③ 计算点移动的距离
"""
def __init__(self, x=0, y=0):
"""假设存在横纵坐标轴,点self的位置相对于(0,0)点可以看成(x,y)"""
self._x = x
self._y = y
@property
def x(self):
"""描述点的位置,横向"""
return self._x
@property
def y(self):
"""描述点的位置,纵向"""
return self._y
def move(self, dx, dy):
"""dx,dy是横纵向移动距离,方法可计算移动后到达的点的位置,移动的距离上加下减,左加右减"""
self._x += dx
self._y += dy
return self._x, self._y
def after_move(self, x, y):
"""定位一个移动后点"""
self._x = x
self._y = y
return self._x, self._y
def distance(self, dot):
"""dot1和dot2是两个点的(x,y)坐标,方法用于计算任意两点之间距离的公式,有无实例都可用"""
distance = ((self._x - dot.x) ** 2 + (self._y - dot.y) ** 2) ** 0.5
return '%.2f' % distance
def str(self):
return '(%s,%s)' % (str(self._x), str(self._y))
def main():
# 点的初始位置
dot1 = Dot(2, 3)
dot2 = Dot(0,0)
# 已知点移动距离
dot2.move(1, 2)
print(dot2.str())
# 求两点距离
print(dot1.distance(dot2))
# 已知移动后点位置
dot1.after_move(3, 4)
print(dot1.str())
# 求两点距离
print(dot1.distance(dot2))
if __name__ == '__main__':
main()
4.4 奥特曼打小怪兽
"""
奥特曼打小怪兽
vision1.3(当前版本)设定:
奥特曼属性:name, hp, attack, mp, magic_attack, ultimate_attack
小怪兽属性:name, hp, attack
初始设定:奥特曼hp=mp=100 小怪兽3个,hp=100
奥特曼技能:
①普攻:小怪兽扣血1-20,奥特曼回蓝5-15
②魔法:蓝耗30,蓝量不够当回合技能释放失败,不能出手;
释放成功时小怪兽扣血30-50,奥特曼回血10-20,扣蓝30
③大招:蓝耗50,蓝量不够当回合技能释放失败,不能出手;
释放成功时小怪兽扣血50-80,奥特曼回血20-30,扣蓝50
奥特曼每回合出手60%概率普攻,30%概率魔法,10%概率大招
小怪兽技能:
①普攻:奥特曼扣血1-20
胜负判定:奥特曼血量先到0,小怪兽赢;全部小怪兽血量到0,奥特曼赢;双方同一回合到0,平局
"""
import random
import abc
class Fighter(abc.ABC):
"""定义所有格斗者的基本信息"""
def __init__(self, name, hp):
"""
name: 姓名
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, value):
"""设置血量"""
self._hp = value
return self._hp
"""存活状态"""
def live(self):
if self._hp > 0:
return True
else:
return False
"""抽象方法:普攻"""
@abc.abstractmethod
def attack(self):
pass
class Ultraman(Fighter):
"""奥特曼,需要定义蓝量,普攻和特殊技能"""
__slots__ = ['_name', '_hp', '_mp']
def __init__(self, name, hp, mp):
"""mp: 蓝量"""
super().__init__(name, hp)
self._mp = mp
"""定义蓝相关"""
@property
def mp(self):
return self._mp
@mp.setter
def mp(self, value):
self._mp = value
return self._mp
"""小怪兽普攻可以给奥特曼造成1-10点伤害"""
def attack(self):
self._hp -= random.randint(1, 20)
if self._hp < 0:
self._hp = 0
else:
self._hp = self._hp
return self._hp
"""奥特曼的普攻可以给自己回复5-15点蓝量"""
def attack_mp(self):
self._mp += random.randint(5, 15)
if self._mp >= 100: # 大于满蓝值时,保持满蓝,蓝量无法溢出
self._mp = 100
return self._mp
"""奥特曼的魔法攻击可以给自己回复10-20点血量"""
def magic_hp(self):
self._hp += random.randint(10, 20)
if self._hp >= 100: # 大于满血值时,保持血蓝,血量无法溢出
self._hp = 100
return self._hp
"""奥特曼的大招可以给自己回复10-30点血量"""
def ultimate_hp(self):
self._hp += random.randint(20, 30)
if self._hp >= 100: # 大于满血值时,保持满血,血量无法溢出
self._hp = 100
return self._hp
"""奥特曼魔法攻击耗蓝30"""
def magic_mp(self):
self._mp -= 30
return self._mp
"""奥特曼大招耗蓝50"""
def ultimate_mp(self):
self._mp -= 50
return self._mp
class Monster(Fighter):
"""小怪兽"""
__slots__ = ['_name', '_hp']
def __init__(self, name, hp):
super().__init__(name, hp)
"""奥特曼普攻给小怪兽造到1-20点伤害"""
def attack(self):
self._hp -= random.randint(1,20)
if self._hp < 0:
self._hp = 0
else:
self._hp = self._hp
return self._hp
"""奥特曼魔法攻击给小怪兽造成30-50点伤害"""
def magic_hp(self):
self._hp -= random.randint(30,50)
if self._hp < 0:
self._hp = 0
else:
self._hp = self._hp
return self._hp
"""奥特曼大招给小怪兽造成50-80点伤害"""
def ultimate_hp(self):
self._hp -= random.randint(50,80)
if self._hp < 0:
self._hp = 0
else:
self._hp = self._hp
return self._hp
def main():
aa = Ultraman('李元芳', 100, 100)
m1 = Monster('小绿', 100)
m2 = Monster('小黄', 100)
m3 = Monster('小蓝', 100)
num = 0
mm = [m1, m2, m3]
while aa.hp > 0 or m1.hp + m2.hp + m3.hp > 0:
num += 1 # 每轮回合数+1
print('-'*20 + f'第{num}回合' + '-'*20)
i = random.randint(0, len(mm)-1) # mm[index1],奥特曼出手,攻击随机一只小怪兽,小怪兽同时出手攻击奥特曼
j = random.randint(0, 9) # i是0-9之间任意一个随机数 # 攻击方式:60%概率普攻,30%魔法攻击,10%大招
if j < 6: # 普攻,小怪兽扣血,奥特曼回蓝
print('奥特曼%s普攻了小怪兽%s' % (str(aa.name), str(mm[i].name)))
print('奥特曼剩余蓝量%s' % str(aa.attack_mp()))
print('小怪兽剩余血量%d' % int(mm[i].attack()))
print('小怪兽%s普攻了奥特曼%s' % (str(mm[i].name), str(aa.name)))
print('奥特曼剩余血量%d' % int(aa.attack()))
elif j < 9: # 魔法攻击,小怪兽扣血,奥特曼扣蓝,回血
if aa.mp >= 30:
print('奥特曼%s魔法攻击了小怪兽%s' % (str(aa.name), str(mm[i].name)))
print('回血-剩余血量%s' % str(aa.magic_hp()))
print('奥特曼剩余蓝量%s' % str(aa.magic_mp()))
print('小怪兽剩余血量%d' % int(mm[i].magic_hp()))
else:
print('奥特曼蓝量不够,释放魔法技能失败')
print('奥特曼剩余蓝量%s' % str(aa.mp))
print('小怪兽剩余血量%d' % int(mm[i].hp))
print('小怪兽%s普攻了奥特曼%s' % (str(mm[i].name), str(aa.name)))
print('奥特曼剩余血量%d' % int(aa.attack()))
else: # 大招,小怪兽扣血,奥特曼扣蓝,回血
if aa.mp >= 60:
print('奥特曼%s大招攻击了小怪兽%s' % (str(aa.name), str(mm[i].name)))
print('回血-剩余血量%s' % str(aa.ultimate_hp()))
print('奥特曼剩余蓝量%s' % str(aa.ultimate_mp()))
print('小怪兽剩余血量%d' % int(mm[i].ultimate_hp()))
else:
print('奥特曼蓝量不够,释放大招失败')
print('奥特曼剩余蓝量%s' % str(aa.mp))
print('小怪兽剩余血量%d' % int(mm[i].hp))
print('小怪兽%s普攻了奥特曼%s' % (str(mm[i].name), str(aa.name)))
print('奥特曼剩余血量%d' % int(aa.attack()))
# 判断奥特曼是否死亡或者本回合全部小怪兽已经死亡,如果是,游戏结束
if aa.hp <= 0 or (m1.hp + m2.hp + m3.hp <= 0):
if aa.hp <= 0 and (m1.hp + m2.hp + m3.hp > 0):
print('奥特曼阵亡')
# 判断奥特曼死亡时小怪兽的存活情况
print('-'*20, '打扫战场', '-'*20)
count = 0
if m1.hp <= 0:
print('%s阵亡' % str(m1.name))
else:
print('%s存活' % str(m1.name))
count += 1
if m2.hp <= 0:
print('%s阵亡' % str(m2.name))
else:
print('%s存活' % str(m2.name))
count += 1
if m3.hp <= 0:
print('%s阵亡' % str(m3.name))
else:
print('%s存活' % str(m3.name))
count += 1
print('共%d只小怪兽存活,小怪兽胜利,挑战失败' % count)
if (m1.hp + m2.hp + m3.hp <= 0) and aa.hp > 0:
print('-'*20, '打扫战场', '-'*20)
print('全部小怪兽阵亡,奥特曼胜利,挑战成功')
if (m1.hp + m2.hp + m3.hp <= 0) and aa.hp <= 0: # 平局
print('-'*20, '打扫战场', '-'*20)
print('奥特曼和全部小怪兽同归于尽,平局')
print('游戏结束')
break
# 是否本回合被攻击的小怪兽死亡
elif mm[i].hp <= 0:
print('小怪兽%s阵亡' % str(mm[i].name))
del mm[i]
print('还剩%d只小怪兽' % len(mm))
else:
print('本回合无人阵亡,游戏继续')
if __name__ == '__main__':
main()
4.5 扑克游戏
"""
扑克游戏
四个玩家,1副牌,实现发牌和理牌
"""
import random # 后续扑克牌列表排序需要
class Card:
"""扑克牌"""
def __init__(self, flower, num):
self._flower = flower
self._num = num # JQK
@property
def flower(self):
return self._flower
@property
def num(self):
return self._num
# 把数字转化为标志,int》str
def sign(self):
if self._num == 11:
sign = 'J'
elif self._num == 12:
sign = 'Q'
elif self._num == 13:
sign = 'K'
else:
sign = str(self._num)
return sign
def __str__(self):
return '%s%s' % (self._flower, self._num)
def __repr__(self):
return self.__str__()
class Poker:
"""一幅牌,4*13"""
def __init__(self):
self._cards = [Card(flower, num)
for flower in '♠♥♣♦'
for num in range(1, 14)] # 默认存在扑克牌列表,无需每次定义实例时定义
self._count = 0 # count+1:发牌数
@property
def cards(self):
return self._cards
def shuffle(self):
"""洗牌(本来按顺序排列的),发牌数归零"""
random.shuffle(self._cards) # 随机打乱顺序
self._count = 0 # 发牌数归0
def next(self):
"""发牌"""
if self.if_remain():
card = self._cards[self._count] # count作为索引,最大等于len(cards)-1
self._count += 1
return card
else:
return None
def if_remain(self):
"""判断牌是否还有,True为还有,False代表发完"""
return self._count < len(self._cards) # 返回一个布尔值
class Player:
"""玩家,姓名,持有的牌"""
def __init__(self, name):
self._name = name
self._hold_cards = [] # 持有的牌初始默认为空
@property
def name(self):
return self._name
@property
def hold_cards(self):
return self._hold_cards
def hold(self, card):
"""发牌后,持有的牌发生变化"""
self._hold_cards.append(card)
return self._hold_cards
def sort(self):
"""收到发牌后,对手里的牌进行排序"""
self._hold_cards.sort(key=key1, reverse=True) # 调用一个外部的排序函数key1进行排序
return self._hold_cards
def key1(card):
"""定义排序依据,排序函数以Card类的实例作为参数"""
flower = {'♥': 1, '♠': 2, '♣': 3, '♦': 4} # 花色排序
sort_key = (card.num, flower[card.flower])
return sort_key # 数字升序,花色升序
def main():
"""先定义4个玩家,1副牌"""
p = Poker()
players = [Player('张三'), Player('李四'), Player('王五'), Player('王小二')]
"""洗牌发牌"""
p.shuffle()
while p.if_remain():
for pr in players:
pr.hold(p.next()) # 发牌
for pr in players:
print('%s:' % pr.name, end=' ')
pr.sort()
print(pr.hold_cards)
if __name__ == '__main__':
main()
# 遇到的问题:
# print(Card('♣', 1)) # <__main__.Card object at 0x0000016C72326050>
# print(Player('张三')) # <__main__.Player object at 0x0000016C72326710>
"""当你尝试打印一个对象的默认表示时,Python 会显示 <类名 对象地址> 的格式。
这是因为当你直接打印一个对象时,Python 会调用该对象的 __str__ 方法来获取字符串表示。
如果类没有定义 __str__ 方法,Python 会使用默认的实现,它显示对象的类型和内存地址。
所以,如果需要直接打印对象,需要在类中提前定义字符串方法。"""
# 类中可以直接调用外部函数,外部函数可以直接用一个类的实例最为参数
4.6 工资结算系统
"""
工资结算系统
某公司有三种类型的员工 分别是部门经理、程序员和销售员
需要设计一个工资结算系统 根据提供的员工信息来计算月薪
部门经理的月薪是每月固定15000元
程序员的月薪按本月工作时间计算 每小时150元
销售员的月薪是1200元的底薪加上销售额5%的提成
"""
from abc import ABC, abstractmethod
class Employee(ABC):
"""员工信息"""
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@abstractmethod
def title(self):
pass
@abstractmethod
def salary(self):
pass
class Manager(Employee):
"""经理:底薪"""
def __init__(self, name):
super().__init__(name)
self._base = 15000
@property
def base(self):
return self._base
@base.setter
def base(self, value):
self._base = value
def title(self):
title = 'Manager'
return title
def salary(self):
salary = self._base
return salary
class Programmer(Employee):
"""程序员:工作时长*时薪"""
def __init__(self, name, time):
super().__init__(name)
self._time = time # 工作时长
self._time_salary = 150 # 时薪
@property
def time(self):
return self._time
@time.setter
def time(self, value):
self._time = value
@property
def time_salary(self):
return self._time_salary
@time_salary.setter
def time_salary(self, value):
self._time_salary = value
def title(self):
title = 'Programmer'
return title
def salary(self):
salary = self._time_salary * self._time
return salary
class Salesman(Employee):
"""销售人员:底薪+销售额*回扣比例"""
def __init__(self, name, sales):
super().__init__(name)
self._sales = sales
self._base = 1200 # 底薪
self._commission = 0.05 # 佣金比例
@property
def sales(self):
return self._sales
@property
def base(self):
return self._base
@property
def commission(self):
return self._commission
@base.setter
def base(self, value):
self._base = value
@sales.setter
def sales(self, value):
self._sales = value
@base.setter
def base(self, value):
self._base = value
@commission.setter
def commission(self, value):
self._commission = value
def title(self):
title = 'Salesman'
return title
def salary(self):
salary = self._base + self._sales * self._commission
return salary
def main():
"""试试多态"""
# 公司A员工列表
employees = [Manager('a'), Manager('b'), Manager('c'),
Programmer('aa', 100), Programmer('bb', 80), Programmer('cc', 120),
Salesman('aaa', 200000), Salesman('bbb', 250000), Salesman('ccc', 220000)]
"""通过实例获取当前薪资情况"""
print('当前经理底薪:%s\n当前程序员时薪:%s\n当前销售底薪%s\n当前销售提成%s\n' %
(Manager('a').base,
Programmer('aa', 100).time_salary,
Salesman('aaa', 200000).base,
Salesman('aaa', 200000).commission))
# 输出员工薪资
for i in employees:
print('涨薪前:%s-%s的收入:%d元' % (i.title(), i.name, i.salary()))
"""一次失败的涨薪
涨薪:经理底薪涨到18000,程序员时薪170,销售底薪1000,佣金比例0.06
Manager.base = 18000
Programmer.time_salary = 170
Salesman.base = 1000
Salesman.commission = 0.06
print('\n调薪后经理底薪:%s\n调薪后程序员时薪:%s\n调薪后销售底薪%s\n调薪后销售提成%s\n' %
(Manager.base, Programmer.time_salary, Salesman.base, Salesman.commission))
# 输出员工薪资
for i in employees:
print('涨薪后:%s-%s的收入:%d元' % (i.title(), i.name, i.salary())"""
# 因为我前面创建实例等于已经为实例属性附了值,所以修改类属性时不再影响已经单独赋值的实例属性,所以收入没变
# 所以,如果我想调整薪水,只能逐个修改实例属性。但我不想逐个修改实例属性怎么办
"""使用isinstance批量修改每一类的实例属性"""
salary_set = {} # 利用字典去重
for i in employees:
if isinstance(i, Manager):
i.base = 18000
salary_set['经理底薪:'] = i.base
elif isinstance(i, Programmer):
i.time_salary = 170
salary_set['程序员时薪:'] = i.time_salary
elif isinstance(i, Salesman):
i.base = 1000
i.commission = 0.06
salary_set['销售底薪:'] = i.base
salary_set['销售提成:'] = i.commission
"""输出调薪后薪资设置"""
print('\n')
for key, value in salary_set.items():
print('调薪后'+key+str(value))
"""输出调薪后员工薪资"""
print('\n')
for i in employees:
print('涨薪后:%s-%s的收入:%d元' % (i.title(), i.name, i.salary()))
if __name__ == '__main__':
main()