目录

  • 1 前言
    • 1.1  Python解释器安装
    • 1.2  PyCharm开发环境
  • 2 Python程序设计实验
    • 2.1  实验1 基本输入输出
    • 2.2  实验2 基本运算
    • 2.3  实验3 字符串处理
    • 2.4  实验4 列表与元组应用
    • 2.5  实验5 列表与元组应用
    • 2.6  实验6 字典与集合应用
    • 2.7  实验7 函数应用1
    • 2.8  实验8 函数应用2
    • 2.9  实验9 类的创建与应用
    • 2.10  实验10 模块应用
    • 2.11  实验11 文件操作
    • 2.12  实验12 异常处理
    • 2.13  实验13 使用正则表达式
    • 2.14  实验14 tkinter基础应用
  • 3 学习参考
    • 3.1  1 Python及其IDLE环境
    • 3.2  2 Python代码规范
    • 3.3  3 使用变量
    • 3.4  4 输入输出
    • 3.5  5 运算符
    • 3.6  6 字符串
    • 3.7  7 条件语句
    • 3.8  8 循环语句
    • 3.9  9 列表
    • 3.10  10 元组
    • 3.11  11 字典
    • 3.12  12 集合
    • 3.13  13 自定义函数
    • 3.14  14 内置函数
    • 3.15  15 类的创建和使用
    • 3.16  16 类的继承与多态
    • 3.17  17 time模块
    • 3.18  18 os模块
    • 3.19  19 random模块
    • 3.20  20 文件
    • 3.21  21 错误与异常处理
 实验9 类的创建与应用

任务1:创建圆的处理类

    下面代码能够创建圆的处理类Circle。要求:

1、将圆的周长和面积计算分别修改为对象属性perimeter、area

2、程序保存到s9A.py

class Circle( ):

    def __init__(self,radius):

        self.radius=radius

    def get_perimeter(self):

        return 2*3.1415926*self.radius

    def get_area(self):

        return 3.1415926*self.radius*self.radius

r=float(input("输入圆的半径:"))

cir=Circle(r)

print("圆周长=",cir.get_perimeter( ))

print("圆面积=",cir.get_area( ))

"""

(1)属性和方法(教材P80)

    类中可以定义数据成员和成员函数,数据成员用于描述对象特征,成员函数用于描述对象行为。

    数据成员也被称为属性,类似前面章节中学习的变量。例如:

radius=radius        #简单变量赋值

self.radius=radius  #类中属性赋值

    成员函数也被称为方法,类似前面章节中学习的函数。例如:

aera = 0

def fun(r):     #函数定义

    global area    #声明全局变量

    perimeter = 2 * 3.1415926 * r  #变量赋值

    area = 3.1415926 * r * r       #变量赋值

    return perimeter

# 主程序

r = float(input("输入圆的半径:"))

print("圆周长=", fun(r))     #函数调用

print("圆面积=", area)

   注意,这里函数fun(r)只有一个返回值perimeter。但主程序中,获得了perimeter和area两个值。其中圆面积area的值,是通过全局变量 global area返回的。

    和普通函数相比,在类中定义函数只有一点不同:第1个参数永远是类本身的实例变量self。例如:

def get_area(self)

    “self”参数代表将要创建的实例对象本身。在类的实例方法中访问实例属性时,需要以“self”为前缀,例如:

def get_area(self):

    return 3.1415926*self.radius*self.radius

    但在外部通过对象名调用对象方法时,并不需要传递这个参数。例如:

print("圆面积=",cir.get_area( ))。

(2)类属性和实例属性(教材P83)

    类中定义的属性是类属性,为所有实例对象共享。可以通过对象名或类名进行访问。

    实例属性为每个实例对象各自拥有,相互独立,只能通过对象名访问。

    例如:

class Car:

    wheels=4    #类属性,默认为公共属性 

#主程序

my_car=Car( )   #创建实例对象my_car

print(my_car.wheels)   #通过对象访问类属性,输出4

Car.wheels=6          #修改类属性

my_car.wheels=8    #修改实例属性

print(my_car.wheels)     #输出8

your_car=Car( )     #创建实例对象your_car

print(your_car.wheels)   #输出6

  类定义中,类属性wheels=4,默认为公共属性。类属性和前面普通函数中的全局变量(global area)相似。全局变量可以在函数和主程序之间,方便传递数据。

  而类的实例对象,都可以任意访问类属性。可以看出,类属性“Car.wheels=6”的更改,影响了后面新建的实例对象your_car。

  这种特点,与函数和类的封装思想,是相违背的。函数中过度使用全局变量,会降低代码的可读性和可维护性。而类属性被外部代码轻易访问,同样会破坏类的封装性能。

(3)构造方法/构造函数__init__(教材P82)

  每个类都有一个默认的__init__( )方法(构造方法,以两个下画线“--”开头和结束)。一个类定义__init__( )方法后,创建实例对象时,Python解释器会自动为新生成的实例,调用__init__( )方法

  构造方法,一般用于数据成员设置初值,或进行其他必要的初始化工作。如果用户未定义__init__( )方法Python解释器会调用默认的__init__( )方法。

  __init__ 方法的第一个参数永远是self,表示创建的实例本身。在__init__方法的内部,可以将各种属性绑定到self,为实例属性设置初始值。例如:

class Circle( ):

    def __init__(self,radius):   #radius为函数形参

        self.radius=radius       #self.radius为实例属性

        self.perimeter=2*3.1415926*radius   #实例属性

    def get_area(self):

        return 3.1415926 * self.radius * self.radius

#主程序

r=float(input("输入圆的半径:"))

cir=Circle(r)      #创建实例对象cir时,传入参数r

print("圆周长=",cir.perimeter)

print("圆面积=",cir.get_area( ))

  其中,构造方法__init__( )中的实例属性perimeter,可以通过对象名访问,即cir.perimeter。

  get_area(self)为类定义中的公有方法,可以通过参数self,访问__init__( )中的实例属性,还可以访问类的其他方法。


任务2:井字棋游戏

1、下面代码是一个简单的双人对战“井字棋”游戏,请根据注释将代码补充完整。

2、程序保存到s9B.py。

class Board( ):   #棋盘类

    def (     ①     ):   #定义类的构造函数。需补充代码

        self.face=[0]*9   #棋盘落子索引。0无子;1先手落子;2后手落子

        self.free=list(range(9))    #可用位置索引

        self.flag=("  ","★","●")     #棋子或空位

"""① 定义构造函数,参考教材P82。按照参数的有无,可分为有参和无参构造方法。从实例属性self.face、self.free和self.flag的初始值可以看出,整个构造函数未涉及其他变量。这里的构造函数,应该属于无参构造方法(self除外)

    def show(     ②    ):    #显示棋盘,需补充代码

"""② 后面主程序中,访问实例方法的代码为:bd.show( ),没有参数传递。可以看出,在类中定义的函数Show( ),应该也没有参数(self除外)。

        a=[ self.flag[x] for (    ③    ) ]    #确定各显示位置显示的字符,需补充代码

"""③ 列表推导式生成的结果a,显示的字符内容("★/●"或空位),从self.flag中获取;而显示位置取决于self.face。因此,索引x应该在self.face中,遍历一次,读取self.face中的位置信息 

s=f""" 

┌─┬─┬─┐

│{a[0]}│{a[1]}│{a[2]}│

├─┼─┼─┤

│{a[3]}│{a[4]}│{a[5]}│

├─┼─┼─┤

│{a[6]}│{a[7]}│{a[8]}│

└─┴─┴─┘"""#s变量为三引号括起来的f格式字符串

        print(s)

    def down(self,index,chess):   #在指定位置index落子。chess为落子方(=1或2)

        if not (      ④     ): return 0   #index值无效时不处理。需补充代码

        """④self.free中,存放的可用位置索引。如果落子位置index值不在self.free中,则表示落子无效。

        self.face[index]=(    ⑤     )    #修改棋盘数据,需补充代码

        """⑤self.face中,存放的棋盘落子索引。0无子,1先手落子,2后手落子。这里形参chess,表示落子方。

        self.free.remove(     ⑥     )    #修改可用位置索引,需补充代码

        """⑥self.free中,存放的可用位置索引。落子生效时,除了修改棋盘数据,还要将落子位置index,从self.free中移除

        return 1    #函数返回1,表示落子有效,返回0,落子无效

    def check(self,index,chess):

        #检查棋面状态。返回值:0游戏未完成;1平局;2胜局

        #index为落子位置、chess为落子方(=1或2)

        a=[[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7], [2,5,8],[0,4,8],[2,4,6]]     #检查方向的位置索引列表

        for p in a:      #检索各方向是否为同一玩家落子

            if index in p:    #如果落子位置在某个方向

                for c in (    ⑦    ):    #需补充代码

                    """⑦外循环p遍历a得到的值,分别为8个方向的坐标信息,[0,1,2],[3,4,5].......;内循环c,应该遍历p,得到某个方向上,3个落子位置的具体坐标

                    if self.face[c]!=chess:(     ⑧     )    #需补充代码

                   """⑧棋盘落子索引self.face[c]和当前落子方chess不同,则中止该方向的后续检查。

                else:return (      ⑨    )    #需补充代码

                """⑨这里的else,与内循环for c......匹配。else语句与循环语句搭配使用,表示循环正常执行后,执行的代码。也就是说,内循环中的break语句,没有执行过!说明当前方向上,所有的落子方,与当前落子chess,是相同的。说明已经有人胜出了,应该返回胜局。

        if not self.free:return (     ⑩    )   #需补充代码

        """⑩这里的if语句,不属于前面的内循环和外循环。也就是说,经过内循环和外循环的检查,没有决出胜负(如果已经决出胜负,肯定由前面return语句,提前返回了)。这时,可用位置索引self.free为空,说明当前棋盘,已经没有地方落子!应该返回平局。

      Python规定,0、空字符串、None都视为False,其他数值和非空字符串均为True。(教材P19)。这里的if not self.free,等同于 if self.free==False。

        else:return 0

    def reset(self):     #重置棋盘

        self.face=[0]*9   #棋盘落子索引。0无子;1先手落子;2后手落子

        self.free=list(range(9))   # 可用位置索引

#主程序

bd=(    ①    )    #创建棋盘对象,需补充代码

"""①对象的创建(P80)。前面类Board的定义中,构造函数不需要传递参数。

pr=1     #当前玩家。1先手;2后手

bd.show( )

while True:

    n=int(input(f"玩家{pr}输入落子位置(0-8):"))

    while (    ②    )==0:    #落子无效,需补充代码

    """②调用实例方法bd.down( ),传递当前落子位置n和当前玩家pr两个参数。bd.down( )返回值为0或1。0表示落子无效。

    这里的while语句不能改为if语句。判断落子无效后,应该继续落子。

    n=int(input("落子位置无效,重新输入:"))

    bd.show( )

    m=bd.check(    ③    )   #检查棋面胜负。需补充代码

    """③实例方法bd.check( )中,形参index为落子位置、chess为落子方。这里的实参,n为落子位置,pr为落子方,也就是当前玩家。

    if m>0:

        s="平局" if m==1 else f"玩家{pr}取胜"

        s=input(f"{s},是否继续?\n输入1继续,输入其他字符结束:")

        if s!="1": (     ④     )    #需补充代码

        """④这里的if s!="1"语句,与前面的玩家、胜负没有关系,就是简单判断“输入1继续,其他字符结束!"

        bd.reset( ); pr=1

    else: pr=2 if pr==1 else 1

    """这条语句的作用,就是让玩家pr变量,交替变换。