python面向对象实验总结,python面向对象的特征
面向对象编程是一种编程方法,需要用“类”和“对象”来实现。所以,面向对象编程其实就是“类”和“对象”的运用。今天,我将向您介绍python面向对象开发及其基本特性。有兴趣的朋友来看看吧。
00-1010前言扑克游戏。工资结算系统。摘要
目录
面向对象编程对于初学者来说不难理解,但是很难应用。虽然我们总结了面向对象的三步法(定义类、创建对象、向对象发送消息),但是说起来容易做起来难。大量的编程实践和阅读高质量的代码可能是这个阶段最能帮到你的两件事。接下来,我们将通过经典案例来分析面向对象编程的知识,同时通过这些案例来讲解如何运用之前所学的Python知识。
前言
说明:为了简单起见,我们的扑克只有52张牌(没有大小王)。游戏需要将52张牌交给4个玩家,每个玩家13张牌,按照黑桃、红心、草花、方块从小到大的顺序和点数排列。其他功能暂时不会实现。
使用面向对象的编程方法,我们首先需要从问题的需求中找到对象并抽象出相应的类,此外还要找到对象的属性和行为。当然这个事情也不是特别难。我们可以从需求描述中找到名词和动词。名词通常是宾语或宾语的属性,动词通常是宾语的行为。扑克游戏中至少应该有三种对象,即牌、扑克和玩家,它们不是孤立的。和类之间的关系大致可以分为is-a关系(继承)、has-a关系(关联)和use-a关系(依赖)。显然,扑克和牌有一个has-a关系,因为一副扑克有(has-a)52张牌;玩家的牌和玩家的牌之间不仅存在相关性,还存在依赖性,因为玩家拥有(has-a)牌,玩家使用(use-a)牌。
卡的属性很明显,有花色和点数。我们可以用0到3的四个数字来代表四种不同的花色,但是这个代码的可读性会很差,因为我们不知道黑桃、红心、花、方块和0到3的数字的对应关系。如果一个变量的值只有有限的几个选项,我们可以使用枚举。与C、Java等语言不同,Python没有声明枚举类型的关键字,但可以通过继承enum模块的enum类来创建枚举类型。代码如下所示。
从枚举导入枚举
类别套件(枚举):
颜色(枚举)
黑桃、红心、梅花、方块=范围(4)
从上面的代码可以看出,定义枚举类型实际上就是定义符号常量,比如SPADE,HEART等。每个符号常数都有其对应的值,所以可以用黑桃的数字0来代替组曲。铁锹;同理,可以使用Suite。菱形代替数字3代表正方形。注意,使用符号常量肯定比使用文字常量好,因为看英文就能理解符号常量的意思,代码的可读性也会提高很多。Python中的枚举类型是一种迭代类型。简单地说,可以将枚举类型放入for-in循环中,依次取出每个符号常量及其对应的值,如下所示。
适用于套件:中的套件
print(f“{ suite } : { suite . value }”)
接下来,我们可以定义卡类。
类别卡:
品牌
def __init__(自身,套件,面部):
self.suite=套件
self.face=face
def __repr__(self):
suites=
faces=[ , A , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , J , Q , K]
#根据牌的花色和点数得到相应的人物。
return f“{ suites[self . suite . value]} { faces[self . face]}”
您可以通过下面的代码测试Card类。
card1=卡片(套件。铲子,5)
card2=卡片(套件。心,13)
print(card1, card2) # ♠5 ♥K
接下来我们定义扑克类。
import randomclass Poker:
"""扑克"""
def __init__(self):
# 通过列表的生成式语法创建一个装52张牌的列表
self.cards = [Card(suite, face) for suite in Suite
for face in range(1, 14)]
# current属性表示发牌的位置
self.current = 0
def shuffle(self):
"""洗牌"""
self.current = 0
# 通过random模块的shuffle函数实现列表的随机乱序
random.shuffle(self.cards)
def deal(self):
"""发牌"""
card = self.cards[self.current]
self.current += 1
return card
@property
def has_next(self):
"""还有没有牌可以发"""
return self.current < len(self.cards)
可以通过下面的代码来测试下Poker
类。
poker = Poker()poker.shuffle()
print(poker.cards)
定义玩家类。
class Player:"""玩家"""
def __init__(self, name):
self.name = name
self.cards = []
def get_one(self, card):
"""摸牌"""
self.cards.append(card)
def arrange(self):
self.cards.sort()
创建四个玩家并将牌发到玩家的手上。
poker = Poker()poker.shuffle()
players = [Player(东邪), Player(西毒), Player(南帝), Player(北丐)]
for _ in range(13):
for player in players:
player.get_one(poker.deal())
for player in players:
player.arrange()
print(f{player.name}: , end=)
print(player.cards)
执行上面的代码会在player.arrange()
那里出现异常,因为Player
的arrange
方法使用了列表的sort
对玩家手上的牌进行排序,排序需要比较两个Card
对象的大小,而<
运算符又不能直接作用于Card
类型,所以就出现了TypeError
异常,异常消息为:'<' not supported between instances of 'Card' and 'Card'
。
为了解决这个问题,我们可以对Card
类的代码稍作修改,使得两个Card
对象可以直接用<
进行大小的比较。这里用到技术叫运算符重载,Python中要实现对<
运算符的重载,需要在类中添加一个名为__lt__
的魔术方法。很显然,魔术方法__lt__
中的lt
是英文单词less than的缩写,以此类推,魔术方法__gt__
对应>
运算符,魔术方法__le__
对应<=
运算符,__ge__
对应>=
运算符,__eq__
对应==
运算符,__ne__
对应!=
运算符。
修改后的Card
类代码如下所示。
class Card:"""牌"""
def __init__(self, suite, face):
self.suite = suite
self.face = face
def __repr__(self):
suites = ♠♥♣♦
faces = [, A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K]
# 根据牌的花色和点数取到对应的字符
return f{suites[self.suite.value]}{faces[self.face]}
def __lt__(self, other):
# 花色相同比较点数的大小
if self.suite == other.suite:
return self.face < other.face
# 花色不同比较花色对应的值
return self.suite.value < other.suite.value
说明: 大家可以尝试在上面代码的基础上写一个简单的扑克游戏,如21点游戏(Black Jack),游戏的规则可以自己在网上找一找。
工资结算系统。
要求:某公司有三种类型的员工,分别是部门经理、程序员和销售员。需要设计一个工资结算系统,根据提供的员工信息来计算员工的月薪。其中,部门经理的月薪是固定15000元;程序员按工作时间(以小时为单位)支付月薪,每小时200元;销售员的月薪由1800元底薪加上销售额5%的提成两部分构成。
通过对上述需求的分析,可以看出部门经理、程序员、销售员都是员工,有相同的属性和行为,那么我们可以先设计一个名为Employee
的父类,再通过继承的方式从这个父类派生出部门经理、程序员和销售员三个子类。很显然,后续的代码不会创建Employee
类的对象,因为我们需要的是具体的员工对象,所以这个类可以设计成专门用于继承的抽象类。Python中没有定义抽象类的关键字,但是可以通过abc
模块中名为ABCMeta
的元类来定义抽象类。关于元类的知识,后面的课程中会有专门的讲解,这里不用太纠结这个概念,记住用法即可。
from abc import ABCMeta, abstractmethodclass Employee(metaclass=ABCMeta):
"""员工"""
def __init__(self, name):
self.name = name
@abstractmethod
def get_salary(self):
"""结算月薪"""
pass
在上面的员工类中,有一个名为get_salary
的方法用于结算月薪,但是由于还没有确定是哪一类员工,所以结算月薪虽然是员工的公共行为但这里却没有办法实现。对于暂时无法实现的方法,我们可以使用abstractmethod
装饰器将其声明为抽象方法,所谓抽象方法就是只有声明没有实现的方法,声明这个方法是为了让子类去重写这个方法。接下来的代码展示了如何从员工类派生出部门经理、程序员、销售员这三个子类以及子类如何重写父类的抽象方法。
class Manager(Employee):"""部门经理"""
def get_salary(self):
return 15000.0
class Programmer(Employee):
"""程序员"""
def __init__(self, name, working_hour=0):
super().__init__(name)
self.working_hour = working_hour
def get_salary(self):
return 200 * self.working_hour
class Salesman(Employee):
"""销售员"""
def __init__(self, name, sales=0):
super().__init__(name)
self.sales = sales
def get_salary(self):
return 1800 + self.sales * 0.05
上面的Manager
、Programmer
、Salesman
三个类都继承自Employee
,三个类都分别重写了get_salary
方法。重写就是子类对父类已有的方法重新做出实现。相信大家已经注意到了,三个子类中的get_salary
各不相同,所以这个方法在程序运行时会产生多态行为,多态简单的说就是调用相同的方法,不同的子类对象做不同的事情。
我们通过下面的代码来完成这个工资结算系统,由于程序员和销售员需要分别录入本月的工作时间和销售额,所以在下面的代码中我们使用了Python内置的isinstance
函数来判断员工对象的类型。我们之前讲过的type
函数也能识别对象的类型,但是isinstance
函数更加强大,因为它可以判断出一个对象是不是某个继承结构下的子类型,你可以简答的理解为type
函数是对对象类型的精准匹配,而isinstance
函数是对对象类型的模糊匹配。
emps = [Manager(刘备), Programmer(诸葛亮), Manager(曹操),
Programmer(荀彧), Salesman(吕布), Programmer(张辽),
]
for emp in emps:
if isinstance(emp, Programmer):
emp.working_hour = int(input(f请输入{emp.name}本月工作时间: ))
elif isinstance(emp, Salesman):
emp.sales = float(input(f请输入{emp.name}本月销售额: ))
print(f{emp.name}本月工资为: ¥{emp.get_salary():.2f}元)
总结
面向对象的编程思想非常的好,也符合人类的正常思维习惯,但是要想灵活运用面向对象编程中的抽象、封装、继承、多态需要长时间的积累和沉淀,这件事情无法一蹴而就,属于路漫漫其修远兮,吾将上下而求索的东西。
到此这篇关于Python超细致探究面向对象的文章就介绍到这了,更多相关Python 面向对象内容请搜索盛行IT软件开发工作室以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT软件开发工作室!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。