TestNG+PO模式打造自动化测试框架教程详解
off999 2024-11-03 14:15 27 浏览 0 评论
今天我们来讲一下TestNG+PO模式打造自动化测试框架,分为四小点跟大家来聊一聊:
1、Java+TestNG框架基本使用
2、web自动化测试PO模式与分层设计理念
3、PO模式与TestNG结合打造自动化测试框架
4、TestNG集成Allure自动化测试报告
1、Java+TestNG框架基本使用
一.testNG介绍
TestNG是Java中的一个测试框架, 类似于JUnit 和NUnit, 功能都差不多, 只是功能更加强大,使用也更方便
Java中已经有一个JUnit的测试框架了。 TestNG比JUnit功能强大的多。 测试人员一般用TestNG来写自动化测试。 开发人员一般用JUnit写单元测试。
官方网站: http://testng.org/doc/index.html
二. eclipse中安装testNG
1. 打开Eclipse Help ->Install New Software , 然后Add "http://beust.com/eclipse"
三. testNG最简单的测试
package TankLearn2.Learn;import org.junit.AfterClass;import org.junit.BeforeClass;import org.testng.annotations.Test; public class TestNGLearn1 { @BeforeClass public void beforeClass() { System.out.println("this is before class"); } @Test public void TestNgLearn() { System.out.println("this is TestNG test case"); } @AfterClass public void afterClass() { System.out.println("this is after class"); }}四.testNG最基本的注解
注解 | 描述 |
@BeforeSuite | 注解的方法将只运行一次,运行在所有测试前此套件中。 |
@AfterSuite | 注解的方法是将只运行一次此套件中的所有测试都运行之后。 |
@BeforeClass | 注解的方法将只运行一次先行先试在当前类中的方法调用。 |
@AfterClass | 注解的方法将只运行一次后已经运行在当前类中的所有测试方法。 |
@BeforeTest | 注解的方法将被运行之前的任何测试方法属于内部类的 <test>标签的运行。 |
@AfterTest | 注解的方法将被运行后,所有的测试方法,属于内部类的<test>标签的运行。 |
@BeforeGroups | 组的列表,这种配置方法将之前运行。此方法是保证在运行属于任何这些组第一个测试方法,该方法被调用。 |
@AfterGroups | 组的名单,这种配置方法后,将运行。此方法是保证运行后不久,最后的测试方法,该方法属于任何这些组被调用。 |
@BeforeMethod | 注解的方法将每个测试方法之前运行。 |
@AfterMethod | 被注释的方法将被运行后,每个测试方法。 |
@DataProvider | 标志着一个方法,提供数据的一个测试方法。注解的方法必须返回一个Object[] [],其中每个对象[]的测试方法的参数列表中可以分配。 该@Test 方法,希望从这个DataProvider的接收数据,需要使用一个dataProvider名称等于这个注解的名字。 |
@Factory | 作为一个工厂,返回TestNG的测试类的对象将被用于标记的方法。该方法必须返回Object[]。 |
@Listeners | 定义为一个测试类的监听器。 |
@Parameters | 介绍如何将参数传递给@Test方法。 |
@Test | 标记一个类或方法作为测试的一部分。 |
五. testNG中如何执行测试
1.第一种直接执行:右键要执行的方法,点Run As ->TestNG Test
2. 第二种: 通过testng.xml文件来执行. 把要执行的case, 放入testng.xml文件中。 右键点击testng.xml, 点Run As
testng.xml<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite name="Suite1"> <test name="test12"> <classes> <class name="TankLearn2.Learn.TestNGLearn1" /> </classes> </test></suite>六.testNG按顺序执行case
1.在testng.xml中,可以控制测试用例按顺序执行。 当preserve-order="true"时,可以保证节点下面的方法是按顺序执行的
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite name="Suite1"> <test name="test12" preserve-order="true"> <classes> <class name="TankLearn2.Learn.TestNGLearn1"> <methods> <include name="TestNgLearn3" /> <include name="TestNgLearn1" /> <include name="TestNgLearn2" /> </methods> </class> </classes> </test></suite>七. testNG异常测试
测试中,有时候我们期望某些代码抛出异常。
TestNG通过@Test(expectedExceptions) 来判断期待的异常, 并且判断Error Message
package TankLearn2.Learn; import org.testng.annotations.Test; public class ExceptionTest { @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp="NullPoint") public void testException(){ throw new IllegalArgumentException("NullPoint"); }}八.testNG组测试
TestNG中可以把测试用例分组,这样可以按组来执行测试用例比如:
package TankLearn2.Learn; import org.testng.annotations.Test; public class GroupTest { @Test(groups = {"systemtest"}) public void testLogin(){ System.out.println("this is test login"); } @Test(groups = {"functiontest"}) public void testOpenPage(){ System.out.println("this is test Open Page"); }}然后在testng.xml中 按组执行测试用例
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite name="Suite1"> <test name="test1"> <groups> <run> <include name="functiontest" /> </run> </groups> </test></suite>九.testNG参数化测试
软件测试中,经常需要测试大量的数据集。 测试代码的逻辑完全一样,只是测试的参数不一样。 这样我们就需要一种 “传递测试参数的机制”。 避免写重复的测试代码
TestNG提供了2种传递参数的方式。
第一种: testng.xml 方式使代码和测试数据分离,方便维护
第二种:@DataProvider能够提供比较复杂的参数。 (也叫data-driven testing)
方法一: 通过testng.xml 传递参数给测试代码
package TankLearn2.Learn;import org.testng.annotations.Parameters;import org.testng.annotations.Test; public class ParameterizedTest1 { @Test @Parameters("test1") public void ParaTest(String test1){ System.out.println("This is " + test1); }}testng.xml<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite name="Suite1"> <parameter name="test1" value="Tank" /> <parameter name="test1" value="Xiao" /> <test name="test12"> <classes> <class name="TankLearn2.Learn.ParameterizedTest1" /> </classes> </test></suite>方式二: 通过DataProvider传递参数
package TankLearn2.Learn; import org.testng.annotations.DataProvider;import org.testng.annotations.Test; public class DataProviderLearn { @DataProvider(name="user") public Object[][] Users(){ return new Object[][]{ {"root","passowrd"}, {"cnblogs.com", "tankxiao"}, {"tank","xiao"} }; } @Test(dataProvider="user") public void verifyUser(String userName, String password){ System.out.println("Username: "+ userName + " Password: "+ password); }}十.testNG忽略测试
有时候测试用例还没准备好, 可以给测试用例加上@Test(enable = false), 来禁用此测试用例
package TankLearn2.Learn; import org.testng.annotations.Test; public class TesgNGIgnore { @Test(enabled = false) public void testIgnore(){ System.out.println("This test case will ignore"); }}十一.testNG依赖测试
有时候,我们需要按顺序来调用测试用例, 那么测试用例之间就存在依赖关系。 TestNG支持测试用例之间的依赖
package TankLearn2.Learn; import org.testng.annotations.Test; public class DependsTest { @Test public void setupEnv(){ System.out.println("this is setup Env"); } @Test(dependsOnMethods = {"setupEnv"}) public void testMessage(){ System.out.println("this is test message"); }}十二.testNG测试报告结果
测试报告是测试非常重要的部分.
TestNG默认情况下,会生产两种类型的测试报告HTML的和XML的。 测试报告位于 "test-output" 目录下.
当然我们也可以设置测试报告的内容级别.
verbose="2" 标识的就是记录的日志级别,共有0-10的级别,其中0表示无,10表示最详细
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite name="Suite1"> <test name="test12" verbose="2"> <classes> <class name="TankLearn2.Learn.TestNGLearn1" /> </classes> </test></suite>web自动化测试PO模式与分层设计理念
一、什么是PO模式
全称:page object model 简称:POM/PO
PO模式最核心的思想是分层,实现松耦合!实现脚本重复使用,实现脚本易维护性!
主要分三层:
1.基础层BasePage:封装一些最基础的selenium的原生的api方法,元素定位,框架跳转等。
2.PO层:元素定位、获得元素对象,页面动作
3.测试用例层:业务逻辑,数据驱动!
三者的关系:PO层继承继承层,测试用例层调用PO层!
二、什么是自动化测试框架
说到自动化框架,我相信很多人应该都听过这个词,但是不知其到底是个什么东西,为什么要用自动化框架。有很多人堆自动化框架都是懵懵懂懂,就跟谈恋爱一样,朦胧美!
一个好的自动化测试框架是可以让不那么懂技术的人也可以写自动化测试脚本的,
一个好的自动化测试框架可以减少自动化测试中脚本管理和维护当中的人力物力和财力。
其实自动化框架的一个最大的意义在于可重用性。因为在框架里,你可以实现很多的通用功能来简化整个脚本的开发过程。并且生成美观的测试报告。
三、非PO模式和PO模式优缺点对比
非PO模式 | PO模式 |
面向过程的线性脚本 | POM把页面元素定位和业务操作流程分开。实现松耦合。 |
复用性差 | UI元素的改变不需要修改业务逻辑代码。只需要找到对应的PO页修改定位即可,数据代码分离 |
维护性差 | PO能使我们的测试代码提高代码的可读性,高复用性,可维护性。 |
四、如何从0到1搭建PO模型
非PO模式举个栗子:有如下百度搜索脚本:
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
class Test(unittest.TestCase):
def test01(self):
# 打开浏览器
driver = webdriver.Chrome()
# 加载百度首页
driver.get('http://www.baidu.com')
# 在百度搜索栏中输入软件测试
driver.find_element(By.ID, 'kw').send_keys('软件测试')
# 点击百度一下按钮
driver.find_element(By.ID, 'su').click()
def test02(self):
# 打开浏览器
driver = webdriver.Chrome()
# 加载百度首页
driver.get('http://www.baidu.com')
# 在百度搜索栏中输入软件测试
driver.find_element(By.ID, 'kw').send_keys('硬件测试')
# 点击百度一下按钮
driver.find_element(By.ID, 'su').click()如何把上述栗子改成PO模式呢?
1、基础层BasePage
from selenium import webdriver
class BasePage:
#构造方法
def __init__(self):
# 打开浏览器
self.driver = webdriver.Chrome() # Alt+Enter
# 加载百度首页
self.driver.get('http://www.baidu.com')
#封装定位元素
def find_ele(self,*args):
ele = self.driver.find_element(*args)
return ele2、PO层:封装百度页面元素定位,元素对象以及页面操作
from selenium.webdriver.common.by import By
from base.base_page import BasePage
class BaiduPage(BasePage):
#元素定位,
baidu_text_loc = (By.ID, 'kw')
baidu_submit_loc = (By.ID, 'su')
#获得元素对象,
def get_text_obj(self):
ele = self.find_ele(*BaiduPage.baidu_text_loc)
return ele
def get_submit_obj(self):
ele = self.find_ele(*BaiduPage.baidu_submit_loc)
return ele
#页面操作
def search(self,search_string):
self.get_text_obj().send_keys(search_string)
self.get_submit_obj().click()3、测试用例层:业务逻辑和数据驱动
from ddt import ddt, data
from po.baidu_page import BaiduPage
@ddt
class BaiduTest(unittest.TestCase):
@data('软件测试','硬件测试')
def test01(self,seaString):
BaiduPage().search(seaString)
time.sleep(5)
if __name__ == '__main__':
unittest.main()从上面的PO案例:让我们更加了解清晰PO的优点在于:
1.POM把页面元素定位和业务操作流程分开。实现松耦合。
2.UI元素的改变不需要修改业务逻辑代码。只需要找到对应的PO页修改定位即可,数据代码分离
3.PO能使我们的测试代码提高代码的可读性,高复用性,可维护性。
五、自动化测试框架和PO的关系
自动化框架=po+各种封装(日志处理封装,全局配置文件的封装,数据库连接的封装,excel操作封装,数据驱动封装等)
其实想要胜任UI自动化测试岗位还需要掌握以下内容:
1.python或java
2.selenium的API
3.unittest/pytest单元测试框架
4.htmltestrunner/allure测试报告
5.数据驱动dtt(excel,yaml,mysql)或pytest中的fixtrue
6.关键字驱动:公共类,方法封装,随机数,数据库连接,全局登录
7.全局配置文件处理
8.日志处理
9.断言
10.第三方库
11.git和github或码云集成开发!
12.jenkins持续集成
3、PO模式与TestNG结合打造自动化测试框架
TestNG应该是程序员和测试人员一个比较好的选择,这里就不说在这个基础上衍生的自动化测试框架了,只是说程序员怎么借助TestNG来更好的方便我们做单元测试,一次性搞定各种情况。
一、IDEA+SpringBoot+TestNG
1、依赖
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.0.0</version>
<scope>test</scope>
</dependency>2、IDEA下载插件
二、代码演示
1、创建测试类
public class TeatNGTest {
@Test(groups = "test1")
@Parameters({"param1","param2"})
public void test1(String param1,String param2){
System.out.println("test1 ceshi");
System.out.println("param1="+param1+",param2="+param2);
}
@Test(groups = "test2")
@Parameters({"param1","param2"})
public void test2(String param1,String param2){
System.out.println("test2 ceshi");
System.out.println("param1="+param1+",param2="+param2);
}
@Test(groups = "test3")
@Parameters({"param1","param2"})
public void test3(String param1,String param2){
System.out.println("test3 ceshi");
System.out.println("param1="+param1+",param2="+param2);
}
}2、点击需要测试的类右键选择Create TestNG XML
3、编辑TestNG.xml文件
三、配置文件
1、需要测试的类需要加上TestNG的注解
2、TestNG.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite">
<!--分组对应测试方法上的分组,name属性对应测试方法上定义的group名,
如果包含就是include,不包含就是exclude,如果添加分组了,下面的
methods就不需要了,直接通过分组来控制粒度,如果不需要分组就不需要groups
标签,直接放开methods标签,指定执行的方法,同时该可以传简单的参数,
但是复杂的参数不行,parameter标签的参数与方法上的注解属性相同,
如果有不同的案例,那就新建不同的test标签-->
<groups>
<run>
<include name="test1"/>
<exclude name="test2"/>
<exclude name="test3"/>
</run>
</groups>
<test verbose="2" preserve-order="true"
name="测试用例1">
<parameter name="param1" value="1"/>
<parameter name="param2" value="2"/>
<classes>
<class name="测试类的全限定名">
<!--<methods>-->
<!--<include name="test1"/>-->
<!--<include name="test2"/>-->
<!--<include name="test3"/>-->
<!--</methods>-->
</class>
</classes>
</test>
<test verbose="2" preserve-order="true"
name="测试用例2">
<parameter name="param1" value="1"/>
<parameter name="param2" value="2"/>
<classes>
<class name="测试类的全限定名">
<!--<methods>-->
<!--<include name="test1"/>-->
<!--<include name="test2"/>-->
<!--<include name="test3"/>-->
<!--</methods>-->
</class>
</classes>
</test>
</suite>四、执行
1、点击testNG.xml,右键执行
2、结果
Connected to the target VM, address: '127.0.0.1:52363', transport: 'socket'
test1 ceshi
param1=1,param2=2
Disconnected from the target VM, address: '127.0.0.1:52363', transport: 'socket'
test1 ceshi
param1=1,param2=2
===============================================
All Test Suite
Total tests run: 2, Passes: 2, Failures: 0, Skips: 0
===============================================3、导出文件
4、TestNG集成Allure自动化测试报告
1、在项目中导入 testng 和 allure2 坐标和对应设置
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<aspectj.version>1.9.2</aspectj.version>
</properties>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-testng</artifactId>
<version>2.6.0</version>
</dependency>
2、加入 maven-surefire-plugin 插件并进行配置
<build>
<plugins>
<plugin>
<!-- maven-surefire-plugin 配合testng/junit执行测试用例的maven插件 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<!-- 测试失败后,是否忽略并继续测试 -->
<testFailureIgnore>true</testFailureIgnore>
<suiteXmlFiles>
<!-- testng配置文件名称 -->
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
<!--设置参数命令行 -->
<argLine>
<!-- UTF-8编码 -->
-Dfile.encoding=UTF-8
<!-- 配置拦截器 -->
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
</argLine>
<systemProperties>
<property>
<!-- 配置 allure 结果存储路径 -->
<name>allure.results.directory</name>
<value>${project.build.directory}/allure-results</value>
</property>
</systemProperties>
</configuration>
<dependencies>
<!-- aspectjweaver maven坐标 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
3.在idea中执行maven命令
3.1 点击 maven -->3.2 点击 m --->3.3 弹框输入命令io.qameta.allure:allure-maven:serve 命令启动 allure 内置服务,运行完成之后就会弹出 allure 页面
4. 自动在浏览器弹出allure报告
5. 选择graphs查看图形结构
6. 选择 Behaviors 查看每个用例的详情信息
希望本文对你有所帮助~~如果对软件测试、接口测试、自动化测试、面试经验交流感兴趣可以私聊我或关注公众号“特斯汀软件测试”。免费领取最新软件测试大厂面试资料和Python自动化、接口、框架搭建学习资料!技术大牛解惑答疑,同行一起交流。
相关推荐
- 阿里云国际站ECS:阿里云ECS如何提高网站的访问速度?
-
TG:@yunlaoda360引言:速度即体验,速度即业务在当今数字化的世界中,网站的访问速度已成为决定用户体验、用户留存乃至业务转化率的关键因素。页面加载每延迟一秒,都可能导致用户流失和收入损失。对...
- 高流量大并发Linux TCP性能调优_linux 高并发网络编程
-
其实主要是手里面的跑openvpn服务器。因为并没有明文禁p2p(哎……想想那么多流量好像不跑点p2p也跑不完),所以造成有的时候如果有比较多人跑BT的话,会造成VPN速度急剧下降。本文所面对的情况为...
- 性能测试100集(12)性能指标资源使用率
-
在性能测试中,资源使用率是评估系统硬件效率的关键指标,主要包括以下四类:#性能测试##性能压测策略##软件测试#1.CPU使用率定义:CPU处理任务的时间占比,计算公式为1-空闲时间/总...
- Linux 服务器常见的性能调优_linux高性能服务端编程
-
一、Linux服务器性能调优第一步——先搞懂“看什么”很多人刚接触Linux性能调优时,总想着直接改配置,其实第一步该是“看清楚问题”。就像医生看病要先听诊,调优前得先知道服务器“哪里...
- Nginx性能优化实战:手把手教你提升10倍性能!
-
关注△mikechen△,十余年BAT架构经验倾囊相授!Nginx是大型架构而核心,下面我重点详解Nginx性能@mikechen文章来源:mikechen.cc1.worker_processe...
- 高并发场景下,Spring Cloud Gateway如何抗住百万QPS?
-
关注△mikechen△,十余年BAT架构经验倾囊相授!大家好,我是mikechen。高并发场景下网关作为流量的入口非常重要,下面我重点详解SpringCloudGateway如何抗住百万性能@m...
- Kubernetes 高并发处理实战(可落地案例 + 源码)
-
目标场景:对外提供HTTPAPI的微服务在短时间内收到大量请求(例如每秒数千至数万RPS),要求系统可弹性扩容、限流降级、缓存减压、稳定运行并能自动恢复。总体思路(多层防护):边缘层:云LB...
- 高并发场景下,Nginx如何扛住千万级请求?
-
Nginx是大型架构的必备中间件,下面我重点详解Nginx如何实现高并发@mikechen文章来源:mikechen.cc事件驱动模型Nginx采用事件驱动模型,这是Nginx高并发性能的基石。传统...
- Spring Boot+Vue全栈开发实战,中文版高清PDF资源
-
SpringBoot+Vue全栈开发实战,中文高清PDF资源,需要的可以私我:)SpringBoot致力于简化开发配置并为企业级开发提供一系列非业务性功能,而Vue则采用数据驱动视图的方式将程序...
- Docker-基础操作_docker基础实战教程二
-
一、镜像1、从仓库获取镜像搜索镜像:dockersearchimage_name搜索结果过滤:是否官方:dockersearch--filter="is-offical=true...
- 你有空吗?跟我一起搭个服务器好不好?
-
来人人都是产品经理【起点学院】,BAT实战派产品总监手把手系统带你学产品、学运营。昨天闲的没事的时候,随手翻了翻写过的文章,发现一个很严重的问题。就是大多数时间我都在滔滔不绝的讲理论,却很少有涉及动手...
- 部署你自己的 SaaS_saas如何部署
-
部署你自己的VPNOpenVPN——功能齐全的开源VPN解决方案。(DigitalOcean教程)dockovpn.io—无状态OpenVPNdockerized服务器,不需要持久存储。...
- Docker Compose_dockercompose安装
-
DockerCompose概述DockerCompose是一个用来定义和管理多容器应用的工具,通过一个docker-compose.yml文件,用YAML格式描述服务、网络、卷等内容,...
- 京东T7架构师推出的电子版SpringBoot,从构建小系统到架构大系统
-
前言:Java的各种开发框架发展了很多年,影响了一代又一代的程序员,现在无论是程序员,还是架构师,使用这些开发框架都面临着两方面的挑战。一方面是要快速开发出系统,这就要求使用的开发框架尽量简单,无论...
- Kubernetes (k8s) 入门学习指南_k8s kubeproxy
-
Kubernetes(k8s)入门学习指南一、什么是Kubernetes?为什么需要它?Kubernetes(k8s)是一个开源的容器编排系统,用于自动化部署、扩展和管理容器化应用程序。它...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
慕ke 前端工程师2024「完整」
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
