学习总结七
面向对象进阶
@property属性装饰器
关于Python中属性和方法访问权限的问题,虽然我们不建议将属性设置为私有的,但是如果直接将属性暴露给外界也是有问题的,比如我们没有办法检查赋给属性的值是否有效。我们之前的建议是将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,代码如下练习所示
@—.setter属性修改器
练习1
class Student(Person):
def __init__(self, name, age, grade):
super().__init__(name, age) # 继承父类
self._grade = grade # 定义子类独有的属性
@property
def grade(self):
return self._grade
@grade.setter
def grade(self, grade):
self._grade = grade
def study(self, course):
print('%s正在学习%s' % (self._name, course))
# 方法重写(override)
# 子类在继承父类方法之后,对方法进行了重新实现
# 当我们给子类对象发送watch_av消息时执行的是子类重写过的方法
def watch_av(self):
print('%s正在和苍老师学习技术' % self._name)
class Teacher(Person):
def __init__(self, name, age, title):
super().__init__(name, age)
self._title = title
@property
def title(self):
return self._title
@title.setter
def title(self, title):
self._title = title
def teach(self, course):
print('%s%s正在教学%s' % (self._name, self._title, course))
def main():
stu1 = Student('王大锤', 16, 5)
stu1.study('HTML网页设计')
stu1.watch_av()
teacher1 = Teacher('骆昊', 30, '叫兽')
teacher1.teach('python')
teacher1.watch_av()
if __name__ == '__main__':
main()
slots魔法
Python是一门动态语言。通常,动态语言允许我们在程序运行时给对象绑定新的属性或方法,当然也可以对已经绑定的属性和方法进行解绑定。但是如果我们需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义slots变量来进行限定。需要注意的是slots的限定只对当前类的对象生效,对子类并不起任何作用。
通过slots魔法限定对象可以绑定的成员变量
练习2
分数计算
from math import gcd
class Fraction(object):
def __init__(self, num, den):
if den == 0:
raise ValueError('分母不能为0!') # 分母为0直接报错 raise - 引发
self._num = num
self._den = den
def simplify(self): # 化简分子分母
if self._num != 0 and self._den != 1:
factor = gcd(abs(self._num), abs(self._den)) # abs表示绝对值,gcd表示求最大公约数
if factor > 1: # 对分子分母进行约分
self._num //= factor
self._den //= factor
def normalize(self): # 分数正规化(处理负号的问题)
if self._den < 0:
self._num = - self._num
self._den = - self._den
self.simplify()
def __str__(self):
if self._num == 0:
return '0' # 分子为0 ,直接输出0(字符串)
elif self._den == 1:
return str(self._num) # 分母为1,直接输出分子(字符串)
else:
return '%d/%d' % (self._num, self._den)
@property
def num(self):
return self._num
@property
def den(self):
return self._den
def add(self, other):
# 加法运算
return Fraction(self._num * other.den + self._den * other.num, self._den * other.den)
def __add__(self, other): # 运算符重载(后面调用函数,可以直接运用符号进行运算)
return self.add(other)
def sub(self, other):
# 减法运算
return Fraction(self._num * other.den - self._den * other.num, self._den * other.den)
def __sub__(self, other):
return self.sub(other)
def mul(self, other):
# 乘法运算
return Fraction(self._num * other.num, self._den * other.den)
def __mul__(self, other):
return self.mul(other)
def div(self, other):
# 除法运算
return Fraction(self._num * other.den, self._den * other.num)
def __truediv__(self, other):
return self.div(other)
def main():
f1 = Fraction(1, 2)
f2 = Fraction(1, 3)
print(f1.add(f2))
print(f1 + f2)
print(f1 - f2)
print(f1 * f2)
print(f1.div(f2))
print(f1 / f2)
if __name__ == '__main__':
main()
抽象类
抽象类就是不能够创建对象的类,这种类的存在就是专门为了让其他类去继承它。Python从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过abc模块的ABCMeta元类和abstractmethod包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。上面的代码中,Dog和Cat两个子类分别对Pet类中的make_voice抽象方法进行了重写并给出了不同的实现版本,当我们在main函数中调用该方法时,这个方法就表现出了多态行为(同样的方法做了不同的事情)
练习3
from abc import ABCMeta, abstractmethod
class Employee(object): # 定义员工(父类)
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@abstractmethod # abstractmethod可以规定后面子类必须重写方法
def get_salary(self):
pass
# 定义方法。提醒子类需要实现重写方法
class Manager(Employee):
def get_salary(self):
return 15000
class Programmer(Employee):
def __init__(self, name):
super().__init__(name)
self._working_hour = 0
@property
def working_hour(self):
return self._working_hour
@working_hour.setter
def working_hour(self, working_hour):
self._working_hour = working_hour if working_hour > 0 else 0
def get_salary(self):
return 150 * self._working_hour
class Salesman(Employee):
def __init__(self, name):
super().__init__(name)
self._sales = 0
@property
def sales(self):
return self._sales
@sales.setter
def sales(self, sales):
self._sales = sales if sales > 0 else 0
def get_salary(self):
return 1200 + self._sales * 0.05
def main():
# 抽象类不能创建对象
emps = [Manager('刘备'), Programmer('诸葛亮'), Salesman('吕布')]
for emp in emps:
# isinstange 表示判断是什么对象
if isinstance(emp, Programmer):
emp.working_hour = int(input('请输入%s本月工作时间:' % emp.name))
elif isinstance(emp, Salesman):
emp.sales = int(input('请输入%s本月销售额:' % emp.name))
# 同样是接受get_salary这个消息,但是不同的员工表现出不同的行为
# 因为三个子类都重写了get_salary方法,所以此处有多态行为
print('%s本月工资为:¥%.2f元' %(emp.name, emp.get_salary()))
if __name__ == '__main__':
main()
练习4
银行账号存钱取钱转账
class Account(object):
def __init__(self, card_number, owner, balance=0):
self._card_number = card_number
self._owner = owner
self._balance = balance
@property
def balance(self):
return self._balance
def deposit(self, money): # 存钱
if money > 0:
self._balance += money
return True
return False
def withdraw(self, money): # 取钱
if 0 < money <= self._balance:
self._balance -= money
return True
return False
def transfer(self, other, money): # 转账
if self.withdraw(money): # 从一个账户中取钱,直接调用前面的行为
other.deposit(money) # 向另一个账户中存钱,直接调用前面行为
return True
return False
def main():
account = Account('11223344', '王大锤')
print(account.balance)
account.deposit(1000)
print(account.balance)
if account.withdraw(5000):
print(account.balance)
else:
print('余额不足!')
account2 = Account('11223355', '王小锤', balance=100)
if account.transfer(account2, 800):
print(account.balance)
print(account2.balance)
else:
print('转账失败!')
if __name__ == '__main__':
main()
练习5
21点扑克游戏(完成发牌)
from random import randrange
class Card(object):
"""一张牌"""
def __init__(self, suite, face):
self._suite = suite
self._face = face
@property
def face(self):
return self._face
@property
def suite(self):
return self._suite
def __str__(self):
if self._face == 1:
face_str = 'A'
elif self._face == 11:
face_str = 'J'
elif self._face == 12:
face_str = 'Q'
elif self._face == 13:
face_str = 'K'
else:
face_str = str(self._face)
return '%s%s' % (self._suite, face_str)
class Poker(object):
"""一副牌"""
def __init__(self):
self._cards = []
self._current = 0
for suite in '♠♥♣♦':
for face in range(13):
card = Card(suite, face + 1)
self._cards.append(card)
@property
def cards(self):
return self._cards
def shuffle(self):
# 洗牌(依次将前面的牌与随机的牌交换位子),随机乱序。
self._current = 0
cards_len = len(self._cards)
for index in range(cards_len):
pos = randrange(cards_len)
self._cards[index], self._cards[pos] = self._cards[pos], self._cards[index]
@property
def next(self):
# 发牌
card = self._cards[self._current]
self._current += 1
return card
@property
def has_next(self):
# 判断还有没有牌可以发
return self._current < len(self._cards)
class Player(object):
def __init__(self, name):
self._name = name
self._cards_on_hand = []
@property
def name(self):
return self._name
@property
def cards_on_hand(self):
return self._cards_on_hand
def get(self, card):
# 摸牌
self._cards_on_hand.append(card)
def arrange(self):
# 排序
self._cards_on_hand.sort(key=get_key)
def get_key(card):
return card.face
def main():
p = Poker()
p.shuffle() # 洗牌
players = [Player('a'), Player('b'), Player('c'), Player('d')]
while p.has_next:
# for _ in range(13):
for player in players:
player.get(p.next) # 发牌
for player in players:
print(player.name + ':', end=' ')
player.arrange() # 排序
for card in player.cards_on_hand:
print(card, end=' ')
print()
if __name__ == '__main__':
main()