“笨办法”学Python 3基础篇 - 项目骨架与自动测试

“笨办法”学Python 3基础篇系列文章

“笨办法”学Python 3基础篇 第一部分-打印与输入
“笨办法”学Python 3基础篇 第二部分-文件操作
“笨办法”学Python 3基础篇 第三部分-函数
“笨办法”学Python 3基础篇 第四部分-数据容器与程序结构
“笨办法”学Python 3基础篇 第五部分-面向对象的类
“笨办法”学Python 3基础篇 第六部分-项目骨架与自动测试
“笨办法”学Python 3基础篇 第七部分-搭建简易的网站



前言

本节将介绍python项目的基本结构,python项目的运行环境以及自动测试所撰写的python代码。从本节开始,我们所关注的python程序将不再是一个孤立的.py程序,而是作为一个python工程项目来进行设计。这是本质区别。

6.1 准备工作-安装虚拟环境virtualenv和测试工具nose

我们再也不能随便找一个文件目录,然后新建一个python脚本就编程了。我们现在需要为python项目新建一个虚拟环境,并据此创建项目的目录。虚拟环境就是一个用来安装软件的“假的”地方,在虚拟环境中,你可以针对不同项目使用不同版本的相互冲突的软件包,且不会污染主系统级别的配置。

6.1.1 安装virtualenv

这里只介绍Windows 10的安装方法。利用pip命令来实现基本的安装,在PowerShell中输入

pip install virtualenv

接下来就是等待下载安装了。
安装好了virtualenv后,就需要在起始目录下创建一个.venvs文件夹,我的起始目录是C:\User\wqai, 这里的wqai是我的用户名。

mkdir .venvs

在.venvs文件夹里,我们通过virtualenv来创建名为csdn_py的虚拟环境,

virtualenv --system-site-packages .venvs/csdn_py

virtualenv创建虚拟环境csdn_py
安装完后,激活虚拟环境csdn_py,在Scripts文件夹中运行activate脚本

.\.venvs\csdn_py\Scripts\activate

此时shell执行了activate脚本,将csdn_py设为了虚拟环境。后续我们的python项目就在这个虚拟环境中创建。激活成功,PowerShell中将出现(csdn_py),如图所示。
激活csdn_py虚拟环境

6.1.2 安装测试工具

书中介绍的是nose工具,不幸的是nose测试工具已经停止更新了。但这并不影响使用,在学习初级python编程时并没有什么大的影响。所谓触类旁通,学会nose测试工具后再学习新的测试工具相信也能很快的掌握。
安装nose测试工具很简单,但前提是powershell使用的是上一小节的虚拟环境。采用pip命令来下载和安装nose,nose将会被安装到.venvs\csdn_py\lib的虚拟环境下面,而非主系统软件包目录。具体命令为

pip install nose

安装过程如图:
安装nose工具

6.2 创建一个名为“room”的项目骨架目录

6.2.1 Python基本项目骨架

当我们用python编写一个项目时,也需要同其他编程方式一样搭建项目架构。根据《“笨办法”学Python 3》中的介绍,一个Python项目的基本骨架目录结构为:
python项目骨架
项目名称是可自定义的。在项目名称目录下,分支出bin、tests、docs和NAME目录,其中:

  1. NAME目录是项目的主模块,可以进行自定义的命名,程序的python脚
    本存放在该目录下
  2. tests目录是存放自动测试脚本的模块

我们采用如下命令,来构建一个名为"room"的项目基本架构:

mkdir projects
cd projects
mkdir room
cd room
mkdir bin
mkdir docs
mkdir tests
mkdir room 

执行命令后,项目目录变为

  1. projects\
  2. 	room\
    
  3. 			room\
    
  4. 			bin\
    
  5. 			docs\
    
  6. 			tests\
    

6.2.2 创建__init__.py和setup.py

接下来,需要为room这个主模块和tests模块构建初始文件。采用如下命令分别在两个目录下生成__init__.py初始化脚本。

new-item -type file room\__init_.py
new-item -type file tests\__init__.py

完成这一命令后,就创建好了空的名称为room的Python模块目录。接下来需要干的事情,就是把python代码放入对应的模块中。
另外,如果你的python项目想让别人安装使用,还需要在room的主目录下创建一个setup.py。该脚本根据项目的不同有所不同,我还没有尝试过自己生成的脚本在另一个环境中安装,因此,这里把书中提供的模板提供出来:

try:
	from setuptools import setup
except ImportError:
	from distutils.core import setup

config = {
	'description': 'My Project',
	'author': 'My Name',
	'url': 'URL to get it at.',
	'download_url': 'Where to download it.',
	'author_email': 'My email.',
	'version': '0.1',
	'install_requires': ['nose'],
	'packages': ['name'],
	'scripts': [],
	'name': 'projectname'
}

setup(**config)	

6.2.3 创建一个空的测试骨架

在tests/目录下建立一个简单的测试专用骨架room_tests.py,代码如下:

from nose.tools import *
import room

def setup():
	print("SETUP!")

def teardown():
	print("TEAR DOWN!")

def test_basic():
	print("I RAN!")

对于刚刚建立的空项目room,我们可以在room/目录下运行nosetests来检查项目配置是否正确。
nosetests空项目

6.3 自动化测试脚本

自动化测试脚本是用来帮助程序员减轻代码Bug检查的工作量,写代码测试的过程可以强迫程序员清晰地理解之前写的代码,帮助理解代码实现的功能和原理,也可以培养良好的编程习惯。

6.3.1 机器人找金币程序

这里采用一个机器人找金币的例子,如图所示,移动区域为(0,0)至(3,3),金币处于(1,1)的位置。
机器人寻宝藏

首先在room\目录下编写一个robot.py程序,定义一个robot类,功能为机器人当前位置坐标的初始化,上下左右移动,以及判断游戏进度。具体代码为:

class Robot:

    def __init__(self, pos):
        self.x_pos = pos[0]
        self.y_pos = pos[1]

    def move(self, direction):
        if direction == 'north':
            self.y_pos += 1
        elif direction == 'east':
            self.x_pos += 1
        elif direction == 'south':
            self.y_pos -= 1
        elif direction == 'west':
            self.x_pos -= 1
        else:
            print("illegal action!")

        return (self.x_pos, self.y_pos) 

    def reward(self):
        if  (self.x_pos, self.y_pos) == (1,1):
            print(f"x_pos: {self.x_pos}, y_pos: {self.y_pos}")
            print("Win!")
            return 'Win!'
        elif self.x_pos < 0 or  self.x_pos > 3 or self.y_pos < 0 or self.y_pos > 3:
            print("Die!")
            return 'Die!'
        else:
            print(f"x_pos: {self.x_pos}, y_pos: {self.y_pos}")
            print("Keep finding!")
            return 'Keep finding!' 

这段代码定义了一个Robot类,包含_init__, move和reward三个函数。在__init__函数中,参数pos的数据类型为元组(tuple),元组数据类型是一种受保护的数据容器,元组中的数据无法被修改,只能被使用。在初始化时,将pos中的元组数据元素赋值给机器人的x和y坐标。move 函数则主要根据运动方向:北,东,南,西来改变机器人的位置坐标,并以元组形式返回当前坐标。award 函数则是判断此时机器人的位置坐标与死亡或奖励的关系。当坐标处于地图之外时,游戏失败。当坐标位于金币所在位置时,游戏成功。当坐标位于地图内未达到目标位置时,则继续寻找。

6.3.2 为robot.py编写一个自动测试脚本

在tests\目录下新建一个robot_test.py的测试脚本,对Robot类中的__init__, move 和 reward函数进行测试。测试采用assert_equal(A, B)实现,该函数实现的是断言功能,用来判断A与B是否相同。值得注意的是,在编写robot_test.py测试脚本时,需要从room\robot.py 引入 Robot 类。具体的自动测试代码如下:

from nose.tools import *
from room.robot import Robot

def test_robot():
    robot_start = Robot((0,0))
    assert_equal(robot_start.x_pos, 0)
    assert_equal(robot_start.y_pos, 0)

def test_move():
    robot_start = Robot((0,0))
    assert_equal(robot_start.move('north'), (0, 1))
    assert_equal(robot_start.move('east'), (1, 1))
    assert_equal(robot_start.move('south'), (1, 0))
    assert_equal(robot_start.move('west'), (0, 0))

def test_reward():
    robot_win = Robot((1, 1))
    robot_failure = Robot((-1, 3))
    robot_keep = Robot((0, 2))
    assert_equal(robot_win.reward(), 'Win!')
    assert_equal(robot_failure.reward(), 'Die!')
    assert_equal(robot_keep.reward(), 'Keep finding!')

在PowerShell中运行nosetests,结果为:
Robot类测试结果
测试结果表明,在robot_test.py自动测试脚本中的test_robot, test_move, test_reward均测试成功,说明Robot 类功能正常。

6.3.3 编写一个游戏引擎

在room\下新建一个engine.py脚本,引用了robot.py脚本中的Robot类。该引擎脚本的主要功能是在地图的随机点生成机器人对象,并通过用户输入来移动机器人寻找金币。具体的代码如下(附加了注释,这里就不再详细描述每段代码的功能,总之就是要好好运用类)

from sys import exit 
import random
from robot import Robot

class Engine:
    #机器人随机出生点
    def __init__(self):
        self.x_pos = random.randint(0, 3)
        self.y_pos = random.randint(0, 3) 
    #运行游戏
    def play(self):
        robot_obj = Robot((self.x_pos, self.y_pos)) #生成机器人对象
        self.target = robot_obj.reward()
        # 机器人开始运动,寻找金币
        while self.target == 'Keep finding!':
             self.direction = input("Awaiting command:> ") #输入运动方向指令 
             robot_obj.move(self.direction) #机器人移动到新位置
             self.target = robot_obj.reward()
        # 机器人走出地图,死亡, 游戏结束
        if self.target == 'Die!':
            print("""
            Game Over!
            """)
            exit(1)     
        # 机器人找到金币点,游戏结束
        else:
            print("""
            You Are Lucky!
            """)
            exit(1)

game = Engine()
game.play()               

PowerShell的运行结果如图。两次机器人的出生点都不一样。第一次找到金币结束游戏,第二次走出地图死亡。
引擎结果


结语

写好项目框架和自动测试脚本,将使得编程更加规范,也一定程度上加快了有效编程的进度。我个人认为是非常好用的,也是非常方便的,尤其是通过测试工具反向设计源代码的方法很有意思。后续,在读别人的代码时,可以通过编写测试脚本来进一步加深代码的理解。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值