Python 中面对对象的定义、可变参数、关键字参数、命名关键字参数、装饰器、slots魔法、继承、静态方法、及抽象类的相关概念与相关例题

  • 面向对象
  • 练习;
        • 1. 定义一个类描述数字的时钟
        • 2.定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法
        • 3. 定义一个类 时间自己设定的倒计时器
  • 可变参数
  • 关键字参数
  • 命名关键字参数
  • 装饰器
  • slots魔法
  • 继承
  • 静态方法
  • 抽象类
  • 练习:
        • 1. 奥特曼打小怪兽
        • 2. 定义一个类为银行账户,有取钱、存钱、转账的功能
        • 3. blackjack 小游戏(21点小游戏)
        • 4. 定义一个类 描述数学里分数的化简、相加、相减、相乘以及相除的功能
  • pygame的应用
        • 1.大球吃小球
        • 2. 贪吃蛇游戏

面向对象

类是对象的蓝图和模板 有了类就可以创建对象。定义类需要的两件事情:数据抽象和行为抽象;数据抽象-抽取对象共同的静态特征-名词-属性;行为抽象-抽取对象共有的动态特征-动词-方法;定义类的关键字是 -class-类名(每个单词首字母大写)。

-step1:定义类
class Student(object):
# 构造方法(构造器/构造子)-constructor
# 调用该方法的时候不是直接使用方法的名字而是使用类的名字
-Step2.调用构造方法创建对象
def __init__(self,name,age):
    #给对象绑定属性
    self.name=name
    self.age=age

def study(self,course):
    print('%s正在学习%s:'% (self.name, course))

#我们定义一个方法就代表对象可以接受这个消息
#对象的方法的第一个参数都是统一self
#它代表了接受消息的对象-对象.消息(参数)
def  watch_av(self):
    if self.age>=18:
        print('%s正在观看美国大片'% self.name)
    else:
        print('%s推荐你观看熊出没' % self.name)

def main():
    -Step3.给对象发消息
    #实际上条用的是Student类中__init__方法
    stu1=Student('罗哈',38)
    #通过给对象发消息让对象完成某些工作
    #解决任何的问题都是通过让对象去做事情
    stu1.name='白元芳'#定义一个对象,没有给其赋年龄
    stu2=Student('王大锤',16)
    stu1.study('python 程序设计')#给对象stu1送入course参数
    stu2.study('HTML网页设计')
    stu2.watch_av()
if __name__ == '__main__':
    main()
结果:
白元芳正在学习python 程序设计:
王大锤正在学习HTML网页设计:
王大锤推荐你观看熊出没

我们定义一个类实际上是把数据的函数绑在一起,形成一个逻辑上的整体,这个整体就叫做对象,而且将来任何时候想要使用这种对象直接复用这个类就可以了。

"""
有一个游泳池半径为r,过道半径为r+3,在修建一堵围墙,且过道造价每25.5元每平米,围墙造价为32.5每平米,求过道的造价,围墙的造价  以及总造价。
"""
from math import pi#引入数学常量pi
class Circle(object):
    def __init__(self,radius):
        """创建构造器"""
        self._radius=radius
    def area(self):
        """求面积的函数"""
         return pi * self._radius**2
    def perometer(self):
        """求周长的函数"""
         return 2 * pi* self._radius
def main():
    r = float(input('请输入游泳池的半径:'))
    big=Circle(r+3)
    small=Circle(r)
    print('围墙的造价为:%.2f元'% (big.perometer()*32.5))
    print('过道的造价为:%.2f元' % ((big.area()-small.area())*25.5))
if __name__ == '__main__':
    main()

练习;

1. 定义一个类描述数字的时钟

class Clock(object):
    """
    数字时钟
    """

    def __init__(self, hour=0, minute=0, second=0):
        """
        构造器

        :param hour: 时
        :param minute: 分
        :param second: 秒
        """
        self._hour = hour
        self._minute = minute
        self._second = second

    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 __str__(self):
        """显示时间"""
        return '%02d:%02d:%02d' % \
               (self._hour, self._minute, self._second)

#自己定义时间
def main():
    clock = Clock()
    while True:
        print(clock)
        sleep(1)
        clock.run()
if __name__ == '__main__':
    main()
结果为:
00:00:00
00:00:01
00:00:02
00:00:03
00:00:04
00:00:05
#与系统时间保持一致,只需将主函数改为一下即可:
def  main():
    localtime = time.localtime(time.time())#取当本地时间的时分秒
    hour=localtime[3]
    minute=localtime[4]
    second=localtime[5]
    local=Clock(hour,minute,second)
    while True:
        print(local)
        sleep(1)
        local.run()
结果:
19:25:37
19:25:38
19:25:39
19:25:40
19:25:41

2.定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法

from math import sqrt
class Point(object):

    def __init__(self, x=0, y=0):
        """
        构造器

        :param x: 横坐标
        :param y: 纵坐标
        """
        self.x = x
        self.y = y

    def move_to(self, x, y):
        """
        移动到指定位置

        :param x: 新的横坐标
        "param y: 新的纵坐标
        """
        self.x = x
        self.y = y

    def move_by(self, dx, dy):
        """
        移动指定的增量

        :param dx: 横坐标的增量
        "param dy: 纵坐标的增量
        """
        self.x += dx
        self.y += dy

    def distance_to(self, other):
        """
        计算与另一个点的距离

        :param other: 另一个点
        """
        dx = self.x - other.x
        dy = self.y - other.y
        return sqrt(dx ** 2 + dy ** 2)

    def __str__(self):
        return '(%s, %s)' % (str(self.x), str(self.y))
def main():
    p1 = Point(3, 5)
    p2 = Point()
    print(p1)
    print(p2)
    p2.move_by(-1, 2)
    print(p2)
    print(p1.distance_to(p2))
if __name__ == '__main__':
    main()

3. 定义一个类 时间自己设定的倒计时器

from time import sleep
class Timepiece(object):
    def __init__(self, hour=0, minute=0, second=0):
     """构造器"""
        self._hour = hour
        self._minute = minute
        self._second = second
    def run(self):
        self._second -= 1
        if self._second==-1:
            self._second=59
            self._minute-=1
        if self._minute==-1:
            self._minute=59
            self._hour-=1
            if self._hour==0:
                return '时间到!'


    def __str__(self):
        return '%02d:%02d:%02d' % (self._hour, self._minute, self._second)


def main():
    clock=Timepiece(1,30,30)
    while True:
        print(clock)
        sleep(1)
        clock.run()

if __name__ == '__main__':
    main()
结果为:
01:30:30
01:30:29
01:30:28
01:30:27
01:30:26 

可变参数

当参数的个数不确定时,我们一般是使用可变参数进行传参。例如:

#当传入为列表时,在参数前加一个*
def  my_sum(*args):
    total=0
    for val in args:
        total+=val
    return total
def main():
    my_list = [1, 25, 3, 40, 5]
    print(my_sum(*my_list))
结果为:
74

关键字参数

根据参数名来决定如何执行

def say_hello(**kwargs):
    print(kwargs)
    for key in kwargs:
        print(key, '--->', kwargs[key])
    if 'name' in kwargs:
        print('您好,%s!' % kwargs['name'])
    elif 'age' in kwargs:
        age = kwargs['age']
        if age <= 16:
            print('你还是个小屁孩')
        else:
            print('你是一个成年人')
    else:
        print('请提供个人信息')
def main():
    say_hello(name='骆昊', age=38)
    param = {'name': '王大锤', 'age': 16, 'tel': '133445566'}
    # 如果希望将一个字典作为关键字参数传入  与要在参数前方两个*
    say_hello(**param)
结果:
{'name': '骆昊', 'age': 38}
name ---> 骆昊
age ---> 38
您好,骆昊!
{'name': '王大锤', 'age': 16, 'tel': '133445566'}
name ---> 王大锤
age ---> 16
tel ---> 133445566
您好,王大锤!

命名关键字参数

在函数进行参数初始化时,你可以在其中任意一个位置加上’‘并添加上逗号,在‘’前的在主函数进行传参时可不指名参数名,但在‘*’之后的,必须指明参数名,否则会报错。

def foo(a, b, c, *, name, age):
    print(a + b + c)
    print(name, ':', age)
def main():
    foo(1,2,3,name='hao',age=38)
    foo(1,2,3,'hao',38)
结果为:
6
hao:38
TypeError: foo() takes 3 positional arguments but 5 were given

装饰器

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

class Student(object):
    def __init__(self, name, age):
     #构造器
        self._name = name
        self._age = age

    # 属性访问器
    @property
    def name(self):
        return self._name

    # 属性修改器
    @name.setter
    def name(self, name):
        self._name = name if 2 <= len(name) <= 4 else '无名氏'

    @property
    def age(self):
        return self._age

    # 属性修改器
    @age.setter
    def age(self, age):
        if 15 <= age <= 25:
            self._age = age
        else:
            print('无效的年龄')

    def study(self, course):
        print('%s正在学习%s:' % (self._name, course))

    def watch_av(self):
        if self._age >= 18:
            print('%s正在观看美国大片' % self._name)
        else:
            print('%s推荐你观看熊出没' % self._name)
def main():
    stu1 = Student('王大锤', 25)
    stu1.study('思想品德')
    stu1.watch_av()
    stu1.age = 200
    print()
if __name__ == '__main__':
    main()
结果为:
王大锤正在学习思想品德:
王大锤正在观看美国大片
无效的年龄

slots魔法

我们可以对类的属性进行绑定,如:

class Person(object):
    # 限定Person对象只能绑定_name, _age和_gender属性
    __slots__ = ('_name', '_age', '_gender')

继承

继承:从已经有的类创建新的过程提供继承信息的成为父亲(超类/基类),得到继承信息的成为子类(派生类/衍生类),通过继承我们将子类中的程序代码抽取到父类中子类通过继承并复用这些代码来减少重复代码的编写,将来如果要维护子类的公共代码只需要在父类中进行操作即可,任何时候子类都可以替换父类 -李氏原则。

class Person(object):
    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 watch_av(self):
        if self._age>=18:
            print('%s正在观看影片'%  self._name)
        else:
            print('只能看熊出没'% 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._tltle = title

    def teach(self, course):
        print('%s正在讲%s' % (self._name, course))

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
    #方法重写(override)-覆盖/置换/覆写
    #子类在继承父类方法之后 对方法进行了重写
    #当我们给子类发送watch_av消息时 执行的是子类重新写过的方法
    def watch_av(self):
        print('学生正在观看电影')
    def study(self, course):
        print('%s正在学习%s:' % (self._name, course))
def main():
    t1 =Teacher('骆昊',38,'教授')
    t1.teach('python')
    t1.watch_av()
    stu1 = Student('王大锤', 25,'初三')
    stu1.study('思想品德')
    stu1.watch_av()

if __name__ == '__main__':
    main()
结果:
骆昊正在讲python
骆昊正在观看影片
王大锤正在学习思想品德:
学生正在观看电影

静态方法

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

from math import sqrt

class Triangle(object):
    def __init__(self, a, b, c):
        """
        构造器
        :param a:非负整数
        :param b: 非负整数
        :param c: 非负整数
        """
        self._a = a
        self._b = b
        self._c = c

    @staticmethod
    def is_valid(a, b, c):
        return a + b > c and b + c > a and a + c > b

   #三角形的周长
    def perimeter(self):
        return self._a + self._b + self._c

    #三角形的面积
    def area(self):
        half = self.perimeter() / 2
        return sqrt(half * (half - self._a) * (half - self._b) * (half - self._c))


def main():
    a, b, c = 12, 13, 15
    if Triangle.is_valid(a, b, c):
        t = Triangle(a, b, c)
        print(t.perimeter())
        print(t.area())
    else:
        print('无法构成三角形')

if __name__ == '__main__':
    main()
结果:
40
74.83314773547883

抽象类

python没有从语言层面支持抽象类的概念,我们可以通过abc模块来制造抽象类的效果。在定义类的时候通过指定metaclass=ABCMeta可以将类申明为抽象类,抽象类是不能创造对象的 ,抽象类存在的意义是专门拿给其子类继承的。abc模块中还有一个包装器abstractmethod,通过这个包装器可以将方法包装为抽象方法 必须要求子类重写这个 抽象方法。

from abc import ABCMeta,abstractmethod
class Employee(object,metaclass=ABCMeta):

    """员工"""
    def __init__(self, name):
        """初始化方法"""
        self._name = name

    @property
    def name(self):
        return self._name
    #强制子类必须要重写包装的方法
    @abstractmethod
    def get_salary(self):
        """获得月薪"""
        pass
    """经理类的工资"""
class Manager(Employee):
    def get_salary(self):
        return 15000.0
    """程序员类的工资"""
class Programmer(Employee):
    def __init__(self, working_hour):
        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.0 * self._working_hour
        """销售类的工资"""
class Saleman(Employee):
    def __init__(self, name, sales=0):
        super().__init__(name)
        self._sales = sales
    @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.0 + self._sales * 0.05

def main():
    emps=[Manager('刘备'),Programmer('诸葛亮'),
          Manager('曹操'),Saleman('貂蝉'),
          Saleman('吕布'),Programmer('张辽'),
          Programmer('赵云')]
    for emp in emps:
        if isinstance(emp,Programmer):
            emp.working_hour=int(input('请输入%s本月工作时间:'% emp.name))
        elif isinstance(emp,Saleman):
            emp.sales=float(input('请输入%s本月销售额:'% emp.name))
        #同样是接收get_salary这个消息  但是不同的员工表现出不同的行为
        #因为三个字类都重写了get_salary方法  所以这个方法会表现出多态行为
        print('%s本月工资为:¥%.2f元'%(emp.name,emp.get_salary()))
if __name__ == '__main__':
    main()
结果:
刘备本月工资为:¥15000.00元
请输入诸葛亮本月工作时间:120
诸葛亮本月工资为:¥18000.00元
曹操本月工资为:¥15000.00元
请输入貂蝉本月销售额:140000
貂蝉本月工资为:¥8200.00元
请输入吕布本月销售额:100000
吕布本月工资为:¥6200.00元
请输入张辽本月工作时间:200
张辽本月工资为:¥30000.00元
请输入赵云本月工作时间:140
赵云本月工资为:¥21000.00

练习:

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('骆昊', 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()
结果:
========第01回合========
骆昊使用普通攻击打了王大锤.
骆昊的魔法值恢复了9点.
王大锤回击了骆昊.
​~~~骆昊奥特曼~~~
生命值: 982
魔法值: 129

​~~~舒小玲小怪兽~~~
生命值: 250
​~~~白元芳小怪兽~~~
生命值: 500
​~~~王大锤小怪兽~~~
生命值: 730
========第02回合========
骆昊使用了魔法攻击.
舒小玲回击了骆昊.
​~~~骆昊奥特曼~~~
生命值: 970
魔法值: 109

​~~~舒小玲小怪兽~~~
生命值: 238
​~~~白元芳小怪兽~~~
生命值: 488
​~~~王大锤小怪兽~~~
生命值: 717
    .
    .
    .
直到其中一方生命值为0,则结束

2. 定义一个类为银行账户,有取钱、存钱、转账的功能

class Account(object):
    def __init__(self, *, card_no, owner, balance=0):
        """
        构造器
        :param card_no: 卡号,为一串字符串
        :param owner: 持卡者姓名,为一串字符串
        :param balance: 卡余额,为一串字符串
        """
        self._card_no = card_no
        self._owner = owner
        self._balance = balance

    @property
    def balance(self):
        return self._balance

    """取钱行为"""

    def withdraw(self, money):
        if 0 < money <= self._balance:
            self._balance -= money
            return True
        return False

    """存钱行为"""

    def deposit(self, money):
        if money > 0:
            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(card_no='11223344', owner='王大锤', )
    print(account.balance)
    account.deposit(2000)
    account.withdraw(500)
    print(account.balance)
    if account.withdraw(5000):
        print(account.balance)
    else:
        print('余额不足')
    account2 = Account(card_no='22334455', owner='李大钊', )
    if account.transfer(account2, 2000):
        print(account.balance)
        print(account2.balance)
    else:
        print('转账失败!')


if __name__ == '__main__':
    main()
结果:
0
1500
余额不足
转账失败!

3. blackjack 小游戏(21点小游戏)

"""
21点小游戏,一台电脑和一个人,电脑为庄家,
首先从玩家开始发牌,再给自己发,发完后,玩家查看自己的底牌,
JQK代表10点,A代表11点,玩家判断是否需要下一张牌,牌的总点数要接近21点
等到玩家不要了,电脑开始判断是否要牌,当点数和小于18点时,电脑自动要牌
直到电脑要牌结束,玩家与电脑总点数进行比较,两家都小于21点时,接近21点的胜利,
两家都大于21点时,电脑胜利。
"""
from random import randrange
class Card(object):
    """一张牌"""

    def __init__(self, suite, face):
        """
        构造器
        :param suite: 牌的花色
        :param face: 牌的点数,为非负整数
        """
        self._suite = suite
        self._face = face

    @property
    def face(self):
        return self._face

    @property
    def suite(self):
        return self._suite

    def __str__(self):
        """
        A,J,Q,K的显示
        :return: 返回牌的花色与点数
        """
        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(1, 14):
                card = Card(suite, face)
                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):
        """
        构造器
        :param 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)
        return card
    def arrange(self):
        """给玩家手里的牌排序"""
        self._cards_on_hand.sort(key=get_key)

def get_key(card):
    """获取当前牌的点数"""
    return card.face

def is_prime(face):
    """如果为JQK则点数为10,如果为A则点数为11,其他点数不变"""
    if face==11 or face==12 or face== 13:
        return 10
    elif face==1:
        return 11
    else:
        return face

def person_ask():
    """询问人类玩家是否需要牌,1则需要,0则不要"""
    x=int(input('请输入0或1:'))
    if x==1:
        return True
    return False

def main():
    p=Poker()
    p.shuffle()
    player1= Player('东邪')
    player2=Player('电脑')
    my_sum=0     #初始电脑与玩家的点数和都为0
    com_sum=0
    my_num=player1.get(p.next).face#玩家取牌
    print('我的底牌为:%s'% my_num)
    com_num=player2.get(p.next).face#电脑取牌
    my_num=is_prime(my_num)#判断是否为JQKA
    my_sum+=my_num#求点数和
    com_num=is_prime(com_num)
    com_sum+=com_num
    while person_ask():#玩家判断是否继续要牌
        my_num=player1.get(p.next).face
        print('我要的牌为:%s'% my_num)
        my_num = is_prime(my_num)
        my_sum += my_num
        print('我的总点数为:%s'% my_sum)
        if com_sum<=18:#电脑自动取牌
            com_num = player2.get(p.next).face
            com_num = is_prime(com_num)
            com_sum += com_num
        print('电脑总点数为:%s' % com_sum)
    #判断输赢
    if my_sum>21 :
        print('电脑胜利!')
    elif com_sum<21 and my_sum<=com_sum:
        print('电脑胜利!')
    else:
        print('我胜利!')


if __name__ == '__main__':
    main()

结果:
我的底牌为:2
请输入011
我要的牌为:5
我的总点数为:7
电脑总点数为:18
请输入011
我要的牌为:1
我的总点数为:18
电脑总点数为:23
请输入010
我胜利!

4. 定义一个类 描述数学里分数的化简、相加、相减、相乘以及相除的功能

from math import gcd


class Fraction(object):
    def __init__(self, num, den):
        """在分母不为0的前提下在进行各种操作,分母为0,分数没有意义"""
        if den == 0:
            raise ValueError('分母不能为0')
        self._num = num
        self._den = den
        #可先进性简化的处理,如果想要看到原始的值,可不处理
        # self.normalize()
        # self.simplify()

    @property
    def num(self):
        return self._num

    @property
    def den(self):
        return self._den

    # 如果分母为负,则将负号换到分子上,如果同为负号 ,则同时拿掉负号
    def normalize(self):
        if self._den < 0:
            self._num = -self._num
            self._den = -self._den
        return self

    def __str__(self):
        #如果分子为0,则返回0
        if self._num == 0:
            return '0'
        #如果分母为1,则返回分母
        elif self._den == 1:
            return str(self._num)
        else:
            return '%d/%d' % (self._num, self._den)

    # 化简
    def simplify(self):
        #在分母不为1,分子不为0的情况下,求分子与分母的最大公约数
        if self._num != 0 and self._den != 1:
            factor = gcd(abs(self._num), abs(self._den))
            self._num //= factor
            self._den //= factor
        return self
    #可直接引入求最大公约数函数,也可自己写
    # def gcd(self):
    #     (self._num, self._den) = (self._den, self._num) \
    #         if self._num > self._den else (self._num, self._den)
    #     for factor in range(self._num, 0, -1):
    #         if self._num % factor == 0 and self._den % factor == 0:
    #             return factor
    #相加函数
    def add(self, other):
        return Fraction(self._num * other.den + self._den * other.num,
                        self._den * other.den).simplify().normalize()
    #相减函数
    def sub(self, other):
        return Fraction(self._num * other.den - self._den * other.num,
                        self._den * other.den).simplify().normalize()
    #相乘函数
    def mul(self, other):
        return Fraction(self._num * other.num, self._den * other.den) \
            .simplify().normalize()
    #相除函数
    def div(self, other):
        return Fraction(self._num * other.den, self._den * other.num) \
            .simplify().normalize()
    #相加,相减,相乘,相除,分别定义了两种写法
    def __add__(self, other):
        return self.add(other)

    def __sub__(self, other):
        return self.sub(other)

    def __mul__(self, other):
        return self.mul(other)

    def __truediv__(self, other):
        return self.div(other)

def main():
    n1 = Fraction(-3, 5)
    print(n1)
    n2 = Fraction(2,- 3)
    print(n2)
    print(n1.add(n2))
    print(n1.div(n2))
    print(n1 + n2)
    print(n1 / n2)

if __name__ == '__main__':
    main()
结果:
-3/5
2/-3
-19/15
9/10
-19/15
9/10

pygame的应用

1.大球吃小球

from random import randint
from math import sqrt
import pygame


class Ball(object):

    def __init__(self, x, y, color, radius, sx, sy):
        """
        构造器
        :param x: 球的球心坐标x
        :param y: 球的球心坐标y
        :param color: 球的颜色
        :param radius: 球的半径
        :param sx: 球移动在x轴上的分量
        :param sy: 球移动在y轴上的分量
        """
        self._x = x
        self._y = y
        self._color = color
        self._radius = radius
        self._sx = sx
        self._sy = sy

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y

    @property
    def radius(self):
        return self._radius

    def move(self):
        """球进行移动,并判断是撞到边界,撞到之后反向移动"""
        self._x += self._sx
        self._y += self._sy
        #球是否撞到右边界 或者左边界,撞倒后反向移动
        if self._x + self._radius >= 800 or self._x - self._radius <= 0:
            self._sx = -self._sx
        #判断球是否撞到下边界 或者上边界 撞倒后反向  
        if self._y + self._radius >= 600 or self._y - self._radius <= 0:
            self._sy = -self._sy

    def draw(self, screen):
        #将球画在屏幕上
        pygame.draw.circle(screen, self._color, (self._x, self._y),
                           self._radius, 0)

    def eat(self, other):
        """判断球能否吃成功,成功返回True,失败返回False"""
        if sqrt((self._x - other.x) ** 2 + (self._y - other.y) ** 2) \
                < self._radius + other.radius and self._radius > other.radius:#判断两个圆心的距离要小于两个圆半径相加的距离,表示两颗碰到了
            self._radius =int(sqrt(other.radius ** 2 + self._radius ** 2))
            #吃到球后 球新的半径
            return True#
        return False


def main():
    balls = []
    pygame.init()
    screen = pygame.display.set_mode((800, 600))#画边框
    pygame.display.set_caption('大球吃小球')#命名游戏名
    clock = pygame.time.Clock()#延迟,之后定义球的移动速度
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                #鼠标按键事件
            elif event.type == pygame.MOUSEBUTTONDOWN \
                    and event.button == 1:
                x, y = event.pos
                color = random_color()
                radius = randint(10, 100)#球半径随机
                sx, sy = randint(-10, 10), randint(-10, 10)#球的移动速度随机
                ball = Ball(x, y, color, radius, sx, sy)#定义一个对象
                balls.append(ball)
        clock.tick(24)
        for ball in balls:
            ball.move()
            #用循环来判断球之间是否能吃
        for i in range(len(balls)-1 ):
            for j in range(len(balls)-1):
                if i!=j:
                    if balls[i].eat(balls[j]):
                        del balls[j]
        refresh(screen, balls)
    pygame.quit()


def refresh(screen, balls):
    #重新刷新界面
    bg_color = (242, 242, 242)
    screen.fill(bg_color)
    for ball in balls:
        ball.draw(screen)
    pygame.display.flip()


def random_color():
    #球的颜色随机出现
    red = randint(0, 255)
    green = randint(0, 255)
    blue = randint(0, 255)
    return red, green, blue


if __name__ == '__main__':
    main()

2. 贪吃蛇游戏

from abc import ABCMeta, abstractmethod
from random import randint

import pygame

BLACK_COLOR = (0, 0, 0)#常量黑色
GREEN_COLOR = (0, 255, 0)#常量绿色
FOOD_COLOR = (230, 185, 185)#常量食物的颜色

UP = 0#向上移动
RIGHT = 1#向右移动
DOWN = 2#向下移动
LEFT = 3#向左移动


class GameObject(object, metaclass=ABCMeta):
    #定义父类为抽象类
    def __init__(self, x=0, y=0, color=BLACK_COLOR):
        """
        初始化
        :param x: 事物右上角坐标点的x轴
        :param y: 事物右上角坐标点的y轴
        :param color: 事物的颜色
        """
        self._x = x
        self._y = y
        self._color = color

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y
    #子类必须重构该方法
    @abstractmethod
    def draw(self, screen):
        pass


class Wall(GameObject):
    #围墙类
    def __init__(self, x, y, width, height, color=BLACK_COLOR):
        """

        :param x: 围墙左上角点的x轴
        :param y:  围墙左上角点的y轴
        :param width: 围墙的宽度
        :param height: 围墙的高度
        :param color: 围墙的颜色
        """
        super().__init__(x, y, color)
        self._width = width
        self._height = height

    @property
    def width(self):
        return self._width

    @property
    def height(self):
        return self._height

    def draw(self, screen):
        """画围墙"""
        pygame.draw.rect(screen, self._color,
                         (self._x, self._y, self._width, self._height), 4)


class Food(GameObject):
    #食物类
    def __init__(self, x, y, size, color=FOOD_COLOR):
        """

        :param x: 圆外切矩形的左上角的点的x轴
        :param y: 圆外切矩形的左上角的点的y轴
        :param size: 食物的大小
        :param color: 食物的颜色
        """
        super().__init__(x, y, color)
        self._size = size
        #食物隐藏  制造出食物闪烁效果
        self._hidden = False

    def draw(self, screen):
        #食物没有隐藏的情况下画食物
        if not self._hidden:
            pygame.draw.circle(screen, self._color,
                               (self._x + self._size // 2, self._y + self._size // 2),
                               self._size // 2, 0)
        #画完之后将食物隐藏
        self._hidden = not self._hidden


class SnakeNode(GameObject):
    """蛇的节点类"""
    def __init__(self, x, y, size, color=GREEN_COLOR):
        """

        :param x: 节点(矩形)左上角点的x轴
        :param y: 节点(矩形)左上角点的y轴
        :param size: 节点大小
        :param color: 节点颜色
        """
        super().__init__(x, y, color)
        self._size = size

    @property
    def size(self):
        return self._size

    def draw(self, screen):
        #画实心节点
        pygame.draw.rect(screen, self._color,
                         (self._x, self._y, self._size, self._size), 0)
        #画节点边框
        pygame.draw.rect(screen, BLACK_COLOR,
                         (self._x, self._y, self._size, self._size), 1)


class Snake(GameObject):
    #蛇类
    def __init__(self):
        super().__init__()
        self._dir = LEFT#定义蛇刚开始的方向为向左
        self._nodes = []#蛇的节点容器
        self._alive = True#蛇活着
        #确定蛇的每个节点的坐标(初始值为5节)
        for index in range(5):
            node = SnakeNode(290 + index * 20, 290, 20)
            self._nodes.append(node)
    #取蛇的前进方向
    @property
    def dir(self):
        return self._dir
    #取蛇存活的状态
    @property
    def alive(self):
        return self._alive
    #取蛇头的坐标
    @property
    def head(self):
        return self._nodes[0]

    @property
    def snake_len(self):
        """
        获取蛇的结点长度

        :return: 蛇的长度
        """
        return len(self._nodes)

    def is_node(self, x, y):
        """
        遍历蛇的每个节点
        :param x: 蛇的节点(矩形)的左上角的点的x轴
        :param y: 蛇的节点(矩形)的左上角的点的x轴
        :return: 判断食物是否会出现在蛇的节点上,是则返回True,不是则返回False
        """
        for node in self._nodes:
            if node.x == x and node.y == y:
                return True
        return False
    #改变蛇的方向
    def change_dir(self, new_dir):
        if (self._dir + new_dir) % 2 != 0:
            #上下方向和左右方向不能同时进行即向上不(下)走时,不能向下(上)掉头,向左(右)走时,不能向右(左)掉头
            self._dir = new_dir

    def move(self):
        #蛇移动
        if self._alive:#蛇活着,将初始方向赋给蛇的方向
            snake_dir = self._dir
            x, y, size = self.head.x, self.head.y, self.head.size#获取蛇头的xy坐标及大小
            if snake_dir == UP:#向上移动
                y -= size#y坐标减少
            elif snake_dir == RIGHT:#向右移动
                x += size#x坐标增加
            elif snake_dir == DOWN:#向下移动
                y += size#y坐标增加
            else:#向左移动
                x -= size#x坐标减少
            new_head = SnakeNode(x, y, size)#获取新蛇头的位置
            self._nodes.insert(0, new_head)
            self._nodes.pop()#删掉最后一个蛇的节点

    def collide(self, wall):
        """
        撞墙

        :param wall: 围墙
        """
        head = self._nodes[0]#获取蛇头
        #如果蛇头的x坐标小于围墙的左边界或者大于围墙的右边界 蛇头的y坐标小于围墙的上边界 或者大于围墙的下边界 则表示蛇撞死了
        #即把蛇的状态为False
        if head.x < wall.x or head.x + head.size > wall.x + wall.width \
                or head.y < wall.y or head.y + head.size > wall.y + wall.height:
            self._alive = False

    def eat_food(self, food):
        #蛇吃食物
        #如果蛇头的xy坐标与食物的xy相等(矩形的左上角点)
        if self.head.x == food.x and self.head.y == food.y:
            tail = self._nodes[-1]#获取最后一个节点
            self._nodes.append(tail)#将蛇的长度加上一个节点的长度
            return True#吃到食物返回True 没吃到返回False
        return False

    def eat_me(self):
        #判断蛇是否撞到了自己的节点
        head = self._nodes[0]#获取蛇头
        for index in range(3, len(self._nodes)):
            if self._nodes[index].x == head.x and self._nodes[index].y == head.y:
                self._alive = False#撞倒后 将蛇的状态表示为False

    def draw(self, screen):
        #将蛇的每个节点画在界面上
        for node in self._nodes:
            node.draw(screen)


def show_text(screen, scores):
    """
    在界面中显示分数

    :param screen:  绘制的界面
    :param scores:  绘制的分数列表
    :return:  None
    """
    my_font = pygame.font.SysFont('宋体', 60)  # 设置字体格式
    num = my_font.render('GAME OVER', False, [0, 0, 0])  # 字体的文本和颜色
    screen.blit(num, (180, 150))  # 绘制文字
    scores.sort(reverse=True)  # 对分数进行排序
    length=len(scores) if len(scores)<10 else 10
    for index in range(length):  # 遍历欠前十的分数
        my_font = pygame.font.SysFont('宋体', 40)  # 设置字体
        score = my_font.render(str(index + 1) +':' + str(scores[index]), False, [255, 225, 0])#显示的方式及颜色
        screen.blit(score, (300, 200+index*40))  # 绘制分数
    pygame.display.flip()  # 刷新界面


def main():
    def refresh():
        """刷新游戏窗口"""
        screen.fill((242, 242, 242))
        wall.draw(screen)#将围墙绘制在界面上
        food.draw(screen)#将食物绘制在界面上
        snake.draw(screen)#将蛇绘制在界面上
        pygame.display.flip()

    def handle_key_event(key_event):
        """处理按键事件"""
        key = key_event.key#获取按键
        if key == pygame.K_F2:#如果按下F2键 重启游戏
            reset_game()
        else:
            if snake.alive:
                new_dir = snake.dir
                if key == pygame.K_w:#按下w键  蛇向上走
                    new_dir = UP
                elif key == pygame.K_d:#按下d键  蛇向右走
                    new_dir = RIGHT
                elif key == pygame.K_s:#按下s键  蛇向下走
                    new_dir = DOWN
                elif key == pygame.K_a:#按下a键  蛇向左走
                    new_dir = LEFT
                if new_dir != snake.dir:#新的方向与原来的方向不相等的情况下  改变方向
                    snake.change_dir(new_dir)

    def create_food(snake):
        #随机出食物的xy坐标  让食物随机出现在界面上
        row = randint(0, 29)
        col = randint(0, 29)
        x, y = 10 + 20 * col, 10 + 20 * row
        if not snake.is_node(x, y):
            #判断食物是否出现在蛇的节点上
            return Food(10 + 20 * col, 10 + 20 * row, 20)

    def reset_game():
        #重启游戏
        nonlocal food, snake
        food = create_food(snake)#产生新的食物
        snake = Snake()#产生新的蛇
    scores=[]#分数列表
    wall = Wall(10, 10, 600, 600)#定义墙对象
    snake = Snake()#产生蛇
    food = create_food(snake)#产生食物
    pygame.init()#初始化
    screen = pygame.display.set_mode((620, 620))#界面的大小
    pygame.display.set_caption('贪吃蛇')#游戏的名字
    screen.fill((242, 242, 242))#界面填充的颜色
    pygame.display.flip()#刷新界面
    clock = pygame.time.Clock()#时钟
    running = True
    while running:
        for event in pygame.event.get():#获取事件
            if event.type == pygame.QUIT:#判断事件是否停止
                running = False
                #判断键盘鼠标事件
            elif event.type == pygame.KEYDOWN:
                handle_key_event(event)
        if snake.alive:
            refresh()#刷新游戏窗口
        clock.tick(5)
        if snake.alive:
            snake.move()#蛇移动
            snake.eat_me()#蛇是否碰到了自己
            snake.collide(wall)#蛇是否撞墙
            if snake.eat_food(food):#蛇有没有吃到食物
                food = create_food(snake)#吃到后创建新的食物
        else:
            #如果蛇死了 则记录分数及显示GAME OVER!
            score= snake.snake_len - 5
            scores.append(score)
            show_text(screen, scores)

    pygame.quit()#关闭界面


if __name__ == '__main__':
    main()

你可能感兴趣的:(总结)