面向对象进阶


文章目录

  • 面向对象进阶
  • 前言
  • 一. @property装饰器
  • 二. \_\_slots\_\_魔法
  • 三. 静态方法和类方法
  • 四. 类之间的关系
  • 五. 继承和多态
  • 六. 综合案例
    • 案例1:奥特曼打小怪兽
    • 案例2:扑克游戏
    • 案例3:工资结算系统
    • 案例4:21点(Black Jack)[扑克游戏](https://blog.csdn.net/Mikchy/article/details/100531868?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm=1001.2101.3001.4242)
  • 总结

前言

其实在前面的文章中我们已经了解了面向对象的一些入门知识,也知道了如何去定义类,如何创建对象以及如何给对象发消息。而在实际操作中,为了能够更好的使用面向对象编程思想进行程序开发,我们还需要对Python中的面向对象编程这一重要思想进行更为深入的了解和研究。


一. @property装饰器

之前我们讨论过Python中属性和方法访问权限的问题,虽然我不建议将属性设置为私有的,但是如果直接将属性暴露给外界也是有问题的,比如我们没有办法检查赋给属性的值是否有效。我们之前的建议是将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,代码如下所示。

class Person(object):def __init__(self, name, age):self._name = nameself._age = age# 访问器 - getter方法@propertydef name(self):return self._name# 访问器 - getter方法@propertydef age(self):return self._age# 修改器 - setter方法@age.setterdef age(self, age):self._age = agedef play(self):if self._age <= 16:print('%s正在玩飞行棋.' % self._name)else:print('%s正在玩斗地主.' % self._name)def main():person = Person('法外狂徒', 12)person.play()person.age = 22person.play()# person.name = '张三'  # AttributeError: can't set attribute 就说明age这个属性已经修改成功。if __name__ == '__main__':main()# 法外狂徒正在玩飞行棋.
# 法外狂徒正在玩斗地主.

我们通过使用person.name = "张三"发现直接报错,他说无法设置属性,也就是说,这里没有这个属性嘛,但我们应该看到前面的年龄为啥可以直接修改,就是因为我们用了@age.setter,那这样的话,我们再内部就实现了相关修改,从现象来看就是年龄这个属性时可以修改的,而且再次调用person.play()时,显示的是法外狂徒是在玩斗地主的,这就说明他年龄已经完成修改了,而且也没有报错,一切正常嘛。可见setter的作用就是让一个私有属性或者受保护的属性可以在外界就能修改。

二. __slots__魔法

我们讲到这里,不知道大家是否已经意识到,Python是一门动态语言。通常,动态语言允许我们在程序运行时给对象绑定新的属性或方法,当然也可以对已经绑定的属性和方法进行解绑定。但是如果我们需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义__slots__变量来进行限定。需要注意的是__slots__的限定只对当前类的对象生效,对子类并不起任何作用。

class Person(object):# 限定Person对象只能绑定_name, _age和_gender属性__slots__ = ('_name', '_age', '_gender')def __init__(self, name, age):self._name = nameself._age = age@propertydef name(self):return self._name@propertydef age(self):return self._age@age.setterdef age(self, age):self._age = agedef play(self):if self._age <= 16:print('%s正在玩飞行棋.' % self._name)else:print('%s正在玩斗地主.' % self._name)def main():person = Person('法外狂徒', 22)person.play()person._gender = '男'# AttributeError: 'Person' object has no attribute '_is_gay'# person._is_gay = Trueif __name__ == '__main__':main()# 法外狂徒正在玩斗地主.

同样的,我们发现对于Person 这个对象是没有_is_gay这个属性的。就算你设定person._is_gay=True也没用,这就体现了setter的功能嘛。

三. 静态方法和类方法

之前,我们在类中定义的方法都是对象方法,也就是说这些方法都是发送给对象的消息。实际上,我们写在类中的方法并不需要都是对象方法,例如我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长未必能构造出三角形对象,因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这个方法很显然就不是对象方法,因为在调用这个方法时三角形对象尚未创建出来(因为都不知道三条边能不能构成三角形),所以这个方法是属于三角形类而并不属于三角形对象的。我们可以使用静态方法来解决这类问题,代码如下所示。

from math import sqrtclass Triangle(object):def __init__(self, a, b, c):self._a = aself._b = bself._c = c@staticmethoddef is_valid(a, b, c):return a + b > c and b + c > a and a + c > bdef perimeter(self):return self._a + self._b + self._cdef area(self):half = self.perimeter() / 2return sqrt(half * (half - self._a) *(half - self._b) * (half - self._c))def main():a, b, c = 3, 4, 5# 静态方法和类方法都是通过给类发消息来调用的if Triangle.is_valid(a, b, c):t = Triangle(a, b, c)  # 实例化一个对象print(t.perimeter())# 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数# print(Triangle.perimeter(t))print(t.area())# print(Triangle.area(t))else:print('无法构成三角形.')if __name__ == '__main__':main()# 12
# 6.0

我们发现,对于某些中间函数,其实我们再使用的过程中,仅仅是调用了一下,并不在乎他在整个过程中的具体使用情况,也就是说,这个函数基本上是不怎么改变的,在整个计算的过程中,想这种的函数,就比较实用使用静态方法嘛。

和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。

from time import time, localtime, sleepclass Clock(object):"""数字时钟"""def __init__(self, hour=0, minute=0, second=0):self._hour = hourself._minute = minuteself._second = second@classmethoddef now(cls):ctime = localtime(time())  # 获取当前时间return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)def run(self):"""走字"""self._second += 1if self._second == 60:self._second = 0self._minute += 1if self._minute == 60:self._minute = 0self._hour += 1if self._hour == 24:self._hour = 0def 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()

就像这里的now()方法,他在使用的过程中就是查看系统此时的时间,作用也是基本固定,所以使用静态函数就比较好嘛。小白感觉这里最难搞的应该就是run()函数了,虽然看起来简单,但是能不能写出来又是另外一回事。当然了,最后就是写个死循环嘛,让程序一直走就好,就像真的时钟一样,每秒走一下(就是程序的运行间隔时间为1秒,即sleep(1))。

四. 类之间的关系

简单的说,类和类之间的关系有三种:is-a、has-a和use-a关系。

  1. is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。
  2. has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。
  3. use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。

我们可以使用一种叫做UML(统一建模语言)的东西来进行面向对象建模,其中一项重要的工作就是把类和类之间的关系用标准化的图形符号描述出来。关于UML我们在这里不做详细的介绍,有兴趣的读者可以自行阅读《UML面向对象设计基础》一书。

注: 利用类之间的这些关系,我们可以在已有类的基础上来完成某些操作,也可以在已有类的基础上创建新的类,这些都是实现代码复用的重要手段。复用现有的代码不仅可以减少开发的工作量,也有利于代码的管理和维护,这是我们在日常工作中都会使用到的技术手段。

五. 继承和多态

刚才我们提到了,可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为里氏替换原则。下面我们先看一个继承的例子。

class Person(object):"""人"""def __init__(self, name, age):self._name = nameself._age = age@propertydef name(self):return self._name@propertydef age(self):return self._age@age.setterdef age(self, age):self._age = agedef play(self):print('%s正在愉快的玩耍.' % self._name)def watch_tv(self):if self._age >= 18:print('%s正在观看玄幻大片.' % self._name)else:print('%s只能观看《熊出没》.' % self._name)class Student(Person):"""学生"""def __init__(self, name, age, grade):super().__init__(name, age)self._grade = grade@propertydef grade(self):return self._grade@grade.setterdef grade(self, grade):self._grade = gradedef study(self, course):print('%s的%s正在学习%s.' % (self._grade, self._name, course))class Teacher(Person):"""老师"""def __init__(self, name, age, title):super().__init__(name, age)self._title = title@propertydef title(self):return self._title@title.setterdef title(self, title):self._title = titledef teach(self, course):print('%s%s正在教%s.' % (self._name, self._title, course))def main():stu = Student('法外狂徒', 15, '高一')stu.study('数学')stu.watch_tv()t = Teacher('张三', 16, '大师')t.teach('艾欧尼亚玩ADC')t.watch_tv()if __name__ == '__main__':main()# 高一的法外狂徒正在学习数学.
# 法外狂徒只能观看《熊出没》.
# 张三大师正在教艾欧尼亚玩ADC.
# 张三只能观看《熊出没》.

注: 子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。

from abc import ABCMeta, abstractmethodclass Pet(object, metaclass=ABCMeta):"""宠物"""def __init__(self, nickname):self._nickname = nickname@abstractmethoddef make_voice(self):"""发出声音"""passclass 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从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过abcabc Abstract Base Classes,即Python的抽象基类)模块的ABCMeta元类和abstractmethod包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。上面的代码中,DogCat两个子类分别对Pet类中的make_voice抽象方法进行了重写并给出了不同的实现版本,当我们在main函数中调用该方法时,这个方法就表现出了多态行为(同样的方法实现了不同的事情)。

六. 综合案例

案例1:奥特曼打小怪兽

from abc import ABCMeta, abstractmethod
from random import randint, randrangeclass Fighter(object, metaclass=ABCMeta):"""战斗者"""# 通过__slots__魔法限定对象可以绑定的成员变量__slots__ = ('_name', '_hp')def __init__(self, name, hp):"""初始化方法:param name: 名字:param hp: 生命值"""self._name = nameself._hp = hp@propertydef name(self):return self._name@propertydef hp(self):return self._hp@hp.setterdef hp(self, hp):self._hp = hp if hp > 0 else 0@propertydef alive(self):return self._hp > 0@abstractmethod  # 抽象类def attack(self, other):"""攻击:param other: 被攻击的对象"""passclass Ultraman(Fighter):"""奥特曼"""__slots__ = ('_name', '_hp', '_mp')def __init__(self, name, hp, mp):"""初始化方法:param name: 名字:param hp: 生命值:param mp: 魔法值"""super().__init__(name, hp)self._mp = mpdef attack(self, other):other.hp -= randint(20, 30)def huge_attack(self, other):"""究极必杀技(打掉对方至少50点或四分之三的血):param other: 被攻击的对象:return: 使用成功返回True否则返回False"""if self._mp >= 50:self._mp -= 50injury = other.hp * 3 // 4injury = injury if injury >= 50 else 50other.hp -= injuryreturn Trueelse:self.attack(other)return Falsedef magic_attack(self, others):"""魔法攻击:param others: 被攻击的群体:return: 使用魔法成功返回True否则返回False"""if self._mp >= 20:self._mp -= 20for temp in others:if temp.alive:temp.hp -= randint(15, 20)return Trueelse:return Falsedef resume(self):"""恢复魔法值"""incr_point = randint(1, 10)self._mp += incr_pointreturn incr_pointdef __str__(self):return '~~~%s奥特曼~~~\n' % self._name + \'生命值: %d\n' % self._hp + \'魔法值: %d\n' % self._mpclass Monster(Fighter):"""小怪兽"""__slots__ = ('_name', '_hp')  # 规定只能有name 和 hp。def attack(self, other):other.hp -= randint(10, 20)def __str__(self):return '~~~%s小怪兽~~~\n' % self._name + \'生命值: %d\n' % self._hpdef is_any_alive(monsters):"""判断有没有小怪兽是活着的"""for monster in monsters:if monster.alive > 0:return Truereturn Falsedef select_alive_one(monsters):"""选中一只活着的小怪兽"""monsters_len = len(monsters)while True:index = randrange(monsters_len)monster = monsters[index]if monster.alive > 0:return monsterdef display_info(ultraman, monsters):"""显示奥特曼和小怪兽的信息"""print(ultraman)for monster in monsters:print(monster, end='')def main():u = Ultraman('法外狂徒', 1000, 120)m1 = Monster('六只小鸡', 250)m2 = Monster('石头人', 500)m3 = Monster('蛤蟆', 750)ms = [m1, m2, m3]fight_round = 1while 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 += 1print('\n========战斗结束!========\n')if u.alive > 0:print('%s奥特曼胜利!' % u.name)else:print('小怪兽胜利!')if __name__ == '__main__':main()

案例2:扑克游戏

import randomclass Card(object):"""一张牌"""def __init__(self, suite, face):self._suite = suiteself._face = face@propertydef face(self):return self._face@propertydef suite(self):return self._suitedef __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)def __repr__(self):return self.__str__()class Poker(object):"""一副牌"""def __init__(self):self._cards = [Card(suite, face) for suite in '♠♥♣♦'for face in range(1, 14)]self._current = 0@propertydef cards(self):return self._cardsdef shuffle(self):"""洗牌(随机乱序)"""self._current = 0random.shuffle(self._cards)@propertydef next(self):"""发牌"""card = self._cards[self._current]self._current += 1return card@propertydef has_next(self):"""还有没有牌"""return self._current < len(self._cards)class Player(object):"""玩家"""def __init__(self, name):self._name = nameself._cards_on_hand = []@propertydef name(self):return self._name@propertydef cards_on_hand(self):return self._cards_on_handdef get(self, card):"""摸牌"""self._cards_on_hand.append(card)def arrange(self, card_key):"""玩家整理手上的牌"""self._cards_on_hand.sort(key=card_key)# 排序规则-先根据花色再根据点数排序
def get_key(card):return (card.suite, card.face)def main():p = Poker()p.shuffle()players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')]for _ in range(13):for player in players:player.get(p.next)for player in players:print(player.name + ':', end=' ')player.arrange(get_key)print(player.cards_on_hand)if __name__ == '__main__':main()# 东邪: [♠A, ♠5, ♠6, ♣2, ♣4, ♣6, ♣9, ♥6, ♥8, ♥9, ♥K, ♦A, ♦10]
# 西毒: [♠2, ♣3, ♣8, ♣10, ♣Q, ♥2, ♥5, ♥7, ♥10, ♦2, ♦5, ♦6, ♦9]
# 南帝: [♠3, ♠7, ♠8, ♣5, ♣J, ♥A, ♥3, ♥J, ♦3, ♦4, ♦7, ♦8, ♦K]
# 北丐: [♠4, ♠9, ♠10, ♠J, ♠Q, ♠K, ♣A, ♣7, ♣K, ♥4, ♥Q, ♦J, ♦Q]

案例3:工资结算系统

"""
某公司有三种类型的员工,分别是部门经理、程序员和销售员。
需要设计一个工资结算系统,根据提供的员工信息来计算月薪:
1. 部门经理的月薪是每月固定15000元
2. 程序员的月薪按本月工作时间计算 每小时150元
3. 销售员的月薪是1200元的底薪加上销售额5%的提成
"""
from abc import ABCMeta, abstractmethodclass Employee(object, metaclass=ABCMeta):"""员工"""def __init__(self, name):"""初始化方法:param name: 姓名"""self._name = name@propertydef name(self):return self._name@abstractmethoddef get_salary(self):"""获得月薪:return: 月薪"""passclass Manager(Employee):"""部门经理"""def get_salary(self):return 15000.0class Programmer(Employee):"""程序员"""def __init__(self, name, working_hour=0):super().__init__(name)self._working_hour = working_hour@propertydef working_hour(self):return self._working_hour@working_hour.setterdef working_hour(self, working_hour):self._working_hour = working_hour if working_hour > 0 else 0def get_salary(self):return 150.0 * self._working_hourclass Salesman(Employee):"""销售员"""def __init__(self, name, sales=0):super().__init__(name)self._sales = sales@propertydef sales(self):return self._sales@sales.setterdef sales(self, sales):self._sales = sales if sales > 0 else 0def get_salary(self):return 1200.0 + self._sales * 0.05def main():emps = [Manager('刘备'), Programmer('诸葛亮'),Manager('曹操'), Salesman('荀彧'),Salesman('吕布'), Programmer('张辽'),Programmer('赵云')]for emp in emps:  # isinstance 两个对象是否为同一个类if isinstance(emp, Programmer):emp.working_hour = int(input('请输入%s本月工作时间: ' % emp.name))elif isinstance(emp, Salesman):emp.sales = float(input('请输入%s本月销售额: ' % emp.name))# 同样是接收get_salary这个消息但是不同的员工表现出了不同的行为(多态)print('%s本月工资为: ¥%s元' %(emp.name, emp.get_salary()))if __name__ == '__main__':main()# 刘备本月工资为: ¥15000.0元
# 请输入诸葛亮本月工作时间: 150
# 诸葛亮本月工资为: ¥22500.0元
# 曹操本月工资为: ¥15000.0元
# 请输入荀彧本月销售额: 2000
# 荀彧本月工资为: ¥1300.0元
# 请输入吕布本月销售额: 22220
# 吕布本月工资为: ¥2311.0元
# 请输入张辽本月工作时间: 1
# 张辽本月工资为: ¥150.0元
# 请输入赵云本月工作时间: 150
# 赵云本月工资为: ¥22500.0元

案例4:21点(Black Jack)扑克游戏

import random
import numpy as np
from sys import exit"""
1、对牌组进行洗牌
2、发牌2-1、初始化发牌,发两张2-2、中途要拍,发一张
3、计算手牌分数
4、胜负判断
5、是否继续叫牌
6、是否继续下一轮
""""""
初始化变量扑克牌组
"""
playing_cards = {"♠A": 1, "♠2": 2, "♠3": 3, "♠4": 4, "♠5": 5, "♠6": 6, "♠7": 7,"♠8": 8, "♠9": 9, "♠10": 10, "♠J": 10, "♠Q": 10, "♠K": 10,"♥A": 1, "♥2": 2, "♥3": 3, "♥4": 4, "♥5": 5, "♥6": 6, "♥7": 7,"♥8": 8, "♥9": 9, "♥10": 10, "♥J": 10, "♥Q": 10, "♥K": 10,"♣A": 1, "♣2": 2, "♣3": 3, "♣4": 4, "♣5": 5, "♣6": 6, "♣7": 7,"♣8": 8, "♣9": 9, "♣10": 10, "♣J": 10, "♣Q": 10, "♣K": 10,"♦A": 1, "♦2": 2, "♦3": 3, "♦4": 4, "♦5": 5, "♦6": 6, "♦7": 7,"♦8": 8, "♦9": 9, "♦10": 10, "♦J": 10, "♦Q": 10, "♦K": 10
}
poker_number = 1
poker_names = list(playing_cards.keys())
poker_list = poker_names * poker_number# 回合数
game_round = 1# 用于判断手中排是否有A
A_list = ["♠A", "♥A", "♣A", "♦A"]# 总分情况(玩家: 电脑)
total_score = np.array([0, 0])# 模块化函数
"""
对牌组进行打乱
"""
def random_pokers(rand_poker_list):random.shuffle(rand_poker_list)"""
发牌,两种函数:
第一个,初始化发牌,一次给2张
第二个,后面叫牌,一次给1张
"""
def get_one_poker(input_poker_list):return input_poker_list.pop(random.randint(0, len(input_poker_list)-1))def init_get_poker(input_poker_list):return [input_poker_list.pop(random.randint(0, len(input_poker_list)-1)),input_poker_list.pop(random.randint(0, len(input_poker_list)-1))]"""
计算手中牌的分数
输入:手牌
返回值:分数
"""
def score_count(hand_poker):score = 0for i in hand_poker:score += playing_cards.get(i)# 判断有没有Afor i in hand_poker:if i in A_list:if score + 10 <= 21:score = score + 10else:breakreturn score"""
胜负判断函数
输入:玩家和电脑分数
输出:输赢情况,并且返回比分,用于加入总比分
"""
def judge_win_lose(your_score, pc_score):if your_score > 21 and pc_score > 21:print("平局")return np.array([0, 0])elif your_score <= 21 and pc_score > 21:print("你赢了")return np.array([1, 0])elif your_score > 21 and pc_score <= 21:print("你输了")return np.array([0, 1])else:if your_score > pc_score:print("你赢了")return np.array([1, 0])elif your_score == pc_score:print("平局")return np.array([0, 0])else:print("你输了")return np.array([0, 1])"""
是否要牌
输入:玩家输入Y/N
输出:返回状态
"""
def if_get_poker():if_continue = input("是否叫牌(Y/N)>>>>>>:")if if_continue.upper() == "Y":return Trueelif if_continue.upper() == "N":return Falseelse:print("输入有误,请重新输入")if_get_poker()"""
询问玩家是否继续
返回值:状态
"""
def continue_or_over(total_your, total_pc):if_continue = input("是否继续下一轮(Y/N)>>>>>>:")if if_continue.upper() == "Y":if len(poker_list) < 15:print("不好意思,剩余扑克数过少,只有{}张,游戏结束".format(len(poker_list)))print("")print("这盘游戏最终比分为:(玩家:电脑)>>>>{}:{}".format(total_your, total_pc))if total_your > total_pc:print("你最终赢得了胜利")elif total_your < total_pc:print("你最终输掉了游戏")else:print("和电脑五五开")exit(1)else:return Trueelif if_continue.upper() == "N":print("玩家不玩了,游戏结束")print("")print("这盘游戏最终比分为:(玩家:电脑)>>>>{}:{}".format(total_your, total_pc))if total_your > total_pc:print("你最终赢得了胜利")elif total_your < total_pc:print("你最终输掉了游戏")else:print("和电脑五五开")exit(1)else:print("输入有误,请重新输入")continue_or_over()"""
处于一局游戏的流程
输入:牌组
返回值:该局比分
"""
def every_round(input_poker_list):# 初始化玩家和电脑的手牌your_hand_poker = []pc_hand_poker = []# 初始化两张牌your_init_poker = init_get_poker(input_poker_list)pc_init_poker = init_get_poker(input_poker_list)print("你的手牌是:{}和{}".format(your_init_poker[0], your_init_poker[1]))print("电脑的手牌是:{}和?".format(pc_init_poker[0]))# 判断有没有21点your_score = score_count(your_init_poker)pc_score = score_count(pc_init_poker)# 加入手牌your_hand_poker.extend(your_init_poker)pc_hand_poker.extend(pc_init_poker)if your_score == 21 or pc_score == 21:print("牌面有21点")return judge_win_lose(your_score, pc_score)else:# 玩家叫牌while True:if_get_your = if_get_poker()if if_get_your == True:new_poker = get_one_poker(input_poker_list)your_hand_poker.append(new_poker)your_score = score_count(your_hand_poker)print("你当前的手牌为:{}".format(your_hand_poker))if your_score > 21:print("你的手牌超过21点,你输了")print("此时电脑的手牌为:{}".format(pc_hand_poker))return np.array([0, 1])else:continueelse:print("你停止叫牌")# 轮到电脑叫牌,要求,叫牌直到大于等于玩家分数while your_score > pc_score:new_poker = get_one_poker(input_poker_list)pc_hand_poker.append(new_poker)pc_score = score_count(pc_hand_poker)print("此时电脑的手牌是:{}".format(pc_hand_poker))return judge_win_lose(your_score, pc_score)"""
游戏开始咯
"""
input("按'回车'后,游戏正式开始")while True:# 游戏提示print("********现在是第{}局游戏********".format(game_round))# 先洗牌random_pokers(poker_list)# 开始每一局, 记录当前输赢情况curr_score = every_round(poker_list)total_score = np.add(total_score, curr_score)# 播报当前比分print("当前总比分为:(玩家:电脑)>>>>{}:{}".format(total_score[0], total_score[1]))# print("此时牌组剩余卡数:{}".format(len(poker_list)))game_round = game_round + 1continue_or_over(total_score[0], total_score[1])print("")

试了几次之后发现这个21点的扑克游戏感觉有点问题,但是不知道如何解决,实在是太菜了,有一次吐槽自己。


总结

本文主要是学习了面向对象编程思想的进阶形式,实在是太难了,再消化几天吧。一是装饰器的使用,对于这种私有属性(被保护的属性)的属性修改问题(也就是你设定setter 和getter在实现修改);二是魔法方法限定类对象只能绑定某些特定的属性;三是静态方法和类方法之间的使用关系以及静态方法的较为适用的范围;四是类与类之间的关系,这就涉及到了继承和多态嘛。最后的最后就是大佬写的几个小案例,当然了对于现在的我来说算是个大工程了,还是那句话,可能我看得懂,但是写出来还是不可能的,还是多动手敲敲代码吧,没啥花里胡哨的,继续加油。

太难了吧,脑壳疼,溜了遛了。Loading(09/100)。。。

Day09.面向对象进阶相关推荐

  1. Day09 - 面向对象进阶

    在前面的章节我们已经了解了面向对象的入门知识,知道了如何定义类,如何创建对象以及如何给对象发消息.为了能够更好的使用面向对象编程思想进行程序开发,我们还需要对Python中的面向对象编程进行更为深入的 ...

  2. Python之面向对象进阶

    Python之面向对象进阶 进阶有:Python 类的成员.成员修饰符.类的特殊成员. 一.类的成员 类的成员可以分为三大类:字段.方法和属性. 注:所有成员中,只有普通字段的内容保存对象中,即:根据 ...

  3. Day 23 面向对象进阶

    目录 面向对象进阶 类的继承 什么是继承 为什么要用继承 对象的继承 对象查找属性的顺序 类的派生 类的组合 菱形继承问题 类的分类 菱形继承问题 多态与多态性 多态 多态性 面向对象进阶 类的继承 ...

  4. Java面向对象进阶

    相关额外的代码待上传... /*** 面向对象进阶*/ package com.org.lxh;import com.org.lxh.ext.Demo; import com.org.lxh.impl ...

  5. JavaSE——面向对象进阶(封装、this、static、代码块、包、权限修饰符、main方法详解)

    第2节 面向对象进阶 一.封装与private 概述: 封装的意义在于保护或者防止代码(数据)被我们无意中破坏.保护成员属性,不让类以外的程序直接访问和修改. 封装原则: 隐藏对象的属性和实现细节,仅 ...

  6. python内置函数面向对象_Pyhton——面向对象进阶二:类的内置函数补充、描述符...

    Pyhton--面向对象进阶二: 一.类的内置函数补充 1.isinstance(obj,cls)--检查obj是否是该类的对象 class Hoo: def __init__(self,name,t ...

  7. 第四十三篇 面向对象进阶2

    目录 第四十三篇 面向对象进阶2 一.类的封装 1.封装分为两个层面 2.应用 二.类的property特性 1. BMI(Body Mass Index):身体体质指数 2.装饰器用法(只在Pyth ...

  8. Python面向对象进阶和socket网络编程

    写在前面 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese:def __init__(self, ...

  9. Java大数据-Week2-Day2面向对象进阶

    第二章第三节 面向对象进阶 文章目录 第二章第三节 面向对象进阶 前言 封装 this关键字 静态static 包 包2 权限修饰符 代码块 main方法详解 总结 前言 # 第三章第3节 面向对象进 ...

最新文章

  1. mysql 8.0配置主从同步_MySQL8.0.19开启GTID主从同步CentOS8
  2. ExtJS FormPanel不执行校验
  3. ThinkPad R400在Windows7下的几个问题
  4. python中的try......except......
  5. python按字节读取_Python read函数按字节(字符)读取文件的实现
  6. 如何确定Ionic是否适合您的项目
  7. 天津科技大学中外合作办学计算机科学怎么样,天津科技大学计算机类(中外合作办学)(计算机科学与技术(信息处理专业2016年在安徽理科高考录取最低分数线...
  8. qq浏览器网页翻译_如何通过Edge浏览器调用“谷歌翻译”,将整个网页翻译为中文...
  9. POJ2104(K-th Number)
  10. cass生成曲线要素_CASS菜鸟入门指南-道路断面
  11. SAP B1打印报表实现本页合计和本单合计的方法
  12. 点云边界提取方法总结
  13. C语言 输出正三角形图形
  14. 修改dhcp服务器租期,修改dhcp租期命令是expire day?
  15. 地球物理及空间物理相关数据下载
  16. android发布市场,TWS耳机集中发布,安卓阵营搅动市场
  17. SocketInputStream.socketRead0 导致线程hangs的解决方案
  18. 福州安卓培训_关于利用东风商学院开展电控发动机维修技师远程培训的通知
  19. 机器学习-预剪枝和后剪枝
  20. Java实现比赛打分,去掉最小值及最大值,求平均值

热门文章

  1. java中对象的清除 正确的说法是_下列关于Java中垃圾回收的相关说法,正确的是()...
  2. 做个可以赚钱的宅男 - PTC网赚推荐
  3. 美军如何在不可信设备上安全访问国防部网络?
  4. Effective C++ 条款14
  5. R语言使用ggplot2可视化甜甜圈图(Donut chart)
  6. 【学习小记】狄利克雷卷积+杜教筛
  7. win7系统老是弹窗怎么解决_win7系统右下角经常弹广告怎么办|win7系统右下角弹出广告的屏蔽方法...
  8. 蚊子看了想咬人,人类看了都说好
  9. 中级工程师职称有什么作用?有含金量吗?为什么要办?甘建二
  10. linux 25端口漏洞,Linux通过栈溢出进行提权实战(dpwwn03)