python实战

python练习

字典列表集合

python: hashmap -> dict -> {}

HashMap(哈希图): 你可以看看数据结构的书,由 <键,值> 对组成的,它们之间区别很小,可以看作近似相同

python: array -> list -> [] python: set -> set -> set()

# dict

# color = {"red": 0.2, "green": 0.4, "blue":0.4}
# print color['red']
# print color['green']

# list
# color_list = ['red', 'blue', 'green', 'yellow']
# print color_list[2]

# set
# a_set = set()
# a_set.add('111')
# a_set.add('222')
# a_set.add('333')
# a_set.add('111')

# print(a_set)


>>> a= {"s":"dd","f":{"a":"j","b":"dfg"}}
>>> a["f"]["b"]
'dfg'

count=0# wu 计数
class Solution:
    def canCross(self, stones):# List[int]) -> bool:
        if stones[1] > 1:
            return False
        # 相邻石子间隔的距离
        steps = [stones[i]-stones[i-1] for i in range(1, len(stones))]
        # 如果全是1、2,直接返回true
        if max(steps) < 3:
            return True
        # 定义一个字典维护每个位置跳跃过的步长的列表
        jump = {0:[1]}
        # i为当前位置
        i = 0


        while i < len(steps):
            # 指向下一个位置
            i += 1
            if not jump.get(i-1):
                # 如果当前位置没有发生跳跃则表示是跳过的石头直接下个位置
                continue
            # 弹出当前位置步长列表,也可以直接get,每次pop出来应该能减小空间占用
            step = jump.pop(i-1)
            # 存放当前位置添加过的步长,防止重复添加
            used_step = set()
            while step:
                # 如果i到了末尾则表示成功过河
                if i == len(steps):
                    return True
                # 每次从步长列表弹出一个步长
                cur_step = step.pop()
                if cur_step in used_step:
                    continue
                # 步长加入集合防止重复
                used_step.add(cur_step)
                # 当前位置能跳哪些步长
                able_step = [cur_step-1,cur_step,cur_step+1]
                for step_ in able_step:
                    start = 0
                    for j in range(i, len(steps)):
                        # 累加跳的步长
                        start += steps[j]
                        # 有匹配的直接添加到字典里匹配位置上的步长列表里
                        if start == step_:
                            jump.setdefault(j,[]).append(step_)
                        # 一旦超出则直接退出循环进入下一个步长匹配
                        if start > step_:
                            break
        # 没有新的位置匹配则表示没能过河
        return False
a = Solution()


stones = [0,1,3,5,6,8,12,17]
stones = [0,1,3,5,6,8,12,172]

b=a.canCross(stones)
print(b)

# 一只青蛙想要过河。 假定河流被等分为 x 个单元格,并且在每一个单元格内都有可能放有一石子(也有可能没有)。 青蛙可以跳上石头,但是不可以跳入水中。

# 给定石子的位置列表(用单元格序号升序表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一个石子上)。 开始时, 青蛙默认已站在第一个石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格1跳至单元格2)。

# 如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。

# 请注意:

# 石子的数量 ≥ 2 且 < 1100;
# 每一个石子的位置序号都是一个非负整数,且其 < 231;
# 第一个石子的位置永远是0。
# 示例 1:

# [0,1,3,5,6,8,12,17]

# 总共有8个石子。
# 第一个石子处于序号为0的单元格的位置, 第二个石子处于序号为1的单元格的位置,
# 第三个石子在序号为3的单元格的位置, 以此定义整个数组...
# 最后一个石子处于序号为17的单元格的位置。

# 返回 true。即青蛙可以成功过河,按照如下方案跳跃: 
# 跳1个单位到第2块石子, 然后跳2个单位到第3块石子, 接着 
# 跳2个单位到第4块石子, 然后跳3个单位到第6块石子, 
# 跳4个单位到第7块石子, 最后,跳5个单位到第8个石子(即最后一块石子)。
# 示例 2:

# [0,1,2,3,4,8,9,11]

# 返回 false。青蛙没有办法过河。 
# 这是因为第5和第6个石子之间的间距太大,没有可选的方案供青蛙跳跃过去。


作用:输出文件开头注释的内容

print(__doc__)
Automatically created module for IPython interactive environment

"""我是你爸爸."""
print(__doc__)
#我是你爸爸.
一个注释两个注释都是相同的

  Python有个特性叫做文档字符串,即DocString,这个特性可以让你的程序文档更加清晰易懂,在python的系统文件里,也都在使用这个特性。因此推荐每个Python爱好者都使用它。   DocString是有自己的格式的。一般是放在自己函数定义的第一行,用‘’符号指示,在这‘’里面添加函数功能说明就可以了。这个说明可以使用.doc(注意前后都是双_)属性,将DocString特性print打印出来。   DocSting的典型用法就是help()调用,它抓取DocString属性,清晰的给你展示出来。

Python中print(__name__)

def p():
  print(__name__)
p()

1. 直接运行,输出__name__
2. 在IDLE下通过导入语句 import nameDemo,输出 nameDemo
3.if __name__ == '__main__':的意思是:只有在本程序直接运行的情况下,才会执行某些操作。



print(__name__)
__main__

调用C程序

Python底层是C, 所以调用C还是比较方便。调用C++有些麻烦。

Python提供了ctypes, 方便将Python类型转为C类型,实现传参数、函数返回类型的对应。ctypes网址:https://docs.python.org/2/library/ctypes.html

使用Python调用C主要有三步:

(1) 编写好C函数

(2) 把C函数打包成库文件

(3) Python加载库文件并调用

代码记录一下:

  1. pycall.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class PythonTest{
public:
    PythonTest():_is_inited(false), _num(0){
    
    }   

    int init(int num){
        _num = num;
        _is_inited = true;
        printf("inited ok\n");
        return 0;
    }   
    
    int str2(char *src, char* dest, int len){
        if (src == NULL || len <= 0){ 
            return 0;
        }   

        int src_len = strlen(src);
        int num = snprintf(dest, len, "%s%s", src, src);
        return (num < len -1)? num:0;
    }   

    bool is_inited(){
        printf("_num = %d\n", _num);
        return _is_inited;
    }   

private:
    bool _is_inited;
    int _num;
};
  1. pycall_so.cpp
#include "pycall.h"

extern "C" {

PythonTest py; 

int init(int num){
    return py.init(num);
}

bool is_inited(){
    return py.is_inited();
}

int str2(char* src, char* dest, int len){
    return py.str2(src, dest, len);
}

int add(int a, int b){ 
    return a + b;
}

}
  1. pycall.py
#coding=utf-8

import ctypes 
from ctypes import *

##加载库文件
ll = ctypes.cdll.LoadLibrary  
lib = ll("./libpycall.so")   

##call
fun=lib.init    ###类似C/C++函数指针
fun.restype = c_int ##设置函数返回值类型
print fun(8);
print "*" * 20

##call
fun=lib.is_inited
fun.restype = c_bool
print fun();
print "*" * 20

##call
fun=lib.str2
src = "hello world "
dest = "*" * 30     ###申请buf, 用于保存返回结果 
num = fun(src, dest, len(dest)) ###传递指针作为参数
if num != 0:
    print dest[:num]
else:
    print "buf is not ok"
print "*" * 20

##call
print lib.add(1, 2); 
print "*" * 20

技术面试题 青蛙403.py

count=0# wu 计数
class Solution:
    def canCross(self, stones):# List[int]) -> bool:
        if stones[1] > 1:
            return False
        # 相邻石子间隔的距离
        steps = [stones[i]-stones[i-1] for i in range(1, len(stones))]
        # 如果全是1、2,直接返回true
        if max(steps) < 3:
            return True
        # 定义一个字典维护每个位置跳跃过的步长的列表
        jump = {0:[1]}
        # i为当前位置
        i = 0


        while i < len(steps):
            # 指向下一个位置
            i += 1
            if not jump.get(i-1):
                # 如果当前位置没有发生跳跃则表示是跳过的石头直接下个位置
                continue
            # 弹出当前位置步长列表,也可以直接get,每次pop出来应该能减小空间占用
            step = jump.pop(i-1)
            # 存放当前位置添加过的步长,防止重复添加
            used_step = set()
            while step:
                # 如果i到了末尾则表示成功过河
                if i == len(steps):
                    return True
                # 每次从步长列表弹出一个步长
                cur_step = step.pop()
                if cur_step in used_step:
                    continue
                # 步长加入集合防止重复
                used_step.add(cur_step)
                # 当前位置能跳哪些步长
                able_step = [cur_step-1,cur_step,cur_step+1]
                for step_ in able_step:
                    start = 0
                    for j in range(i, len(steps)):
                        # 累加跳的步长
                        start += steps[j]
                        # 有匹配的直接添加到字典里匹配位置上的步长列表里
                        if start == step_:
                            jump.setdefault(j,[]).append(step_)
                        # 一旦超出则直接退出循环进入下一个步长匹配
                        if start > step_:
                            break
        # 没有新的位置匹配则表示没能过河
        return False
a = Solution()


stones = [0,1,3,5,6,8,12,17]
stones = [0,1,3,5,6,8,12,172]

b=a.canCross(stones)
print(b)

# 一只青蛙想要过河。 假定河流被等分为 x 个单元格,并且在每一个单元格内都有可能放有一石子(也有可能没有)。 青蛙可以跳上石头,但是不可以跳入水中。

# 给定石子的位置列表(用单元格序号升序表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一个石子上)。 开始时, 青蛙默认已站在第一个石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格1跳至单元格2)。

# 如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。

# 请注意:

# 石子的数量 ≥ 2 且 < 1100;
# 每一个石子的位置序号都是一个非负整数,且其 < 231;
# 第一个石子的位置永远是0。
# 示例 1:

# [0,1,3,5,6,8,12,17]

# 总共有8个石子。
# 第一个石子处于序号为0的单元格的位置, 第二个石子处于序号为1的单元格的位置,
# 第三个石子在序号为3的单元格的位置, 以此定义整个数组...
# 最后一个石子处于序号为17的单元格的位置。

# 返回 true。即青蛙可以成功过河,按照如下方案跳跃: 
# 跳1个单位到第2块石子, 然后跳2个单位到第3块石子, 接着 
# 跳2个单位到第4块石子, 然后跳3个单位到第6块石子, 
# 跳4个单位到第7块石子, 最后,跳5个单位到第8个石子(即最后一块石子)。
# 示例 2:

# [0,1,2,3,4,8,9,11]

# 返回 false。青蛙没有办法过河。 
# 这是因为第5和第6个石子之间的间距太大,没有可选的方案供青蛙跳跃过去。

python脚本

python脚本,使用python进行http的接口测试,脚本很简单,逻辑是:读取excel写好的测试用例,然后根据excel中的用例内容进行调用,判断预期结果中的返回值是否和返回报文中的值一致,如果不一致则根据用例标题把bug提交到bug管理系统,这里使用的bug管理系统是bugfree。

实现步骤:

​ 1、读取excel,保存测试用例中的内容;

​ 2、根据excel中的请求url和参数拼接请求报文,调用接口,并保存返回报文;

​ 3、读取返回报文,和预期结果对比,不一致的往bugfree数据库中写入一条bug,并且把请求报文、返回报文和测试结果写到测试用例的excel中。

​ http接口最常用的两种请求方式,POST和GET两种方法,请求都是类似下面例子的url。

​ 例子:http://127.0.0.1:8080/rest/login?oper_no=marry&id=10

​ 需要用的到几个模块,requests、xlrd(读取excel)、xlutils(写excel)、MySQLdb(连接数据库)这四个模块都是第三方模块,需要自己单独安装,用到的一些python中的方法和内置函数也都做了说明,本文使用的python版本是python2.7。

​ 首先先写好excel的用例模板,需要有的字段 项目、用例id、接口名称、用例描述、请求方式、url请求地址、请求参数(多个的参数话用;分号隔开)、结果验证(多个的参数话用;分号隔开)、请求报文、返回报文、测试人员、测试结果;

# -*- coding:utf-8 -*- 
import requests, xlrd, MySQLdb, time, sys 
#导入需要用到的模块 
from xlutils import copy 
#从xlutils模块中导入copy这个函数 
def readExcel(file_path): 
  ''''' 
  读取excel测试用例的函数 
  :param file_path:传入一个excel文件,或者文件的绝对路径 
  :return:返回这个excel第一个sheet页中的所有测试用例的list 
  '''
  try: 
    book = xlrd.open_workbook(file_path)#打开excel 
  except Exception,e: 
    #如果路径不在或者excel不正确,返回报错信息 
    print '路径不在或者excel不正确',e 
    return e 
  else: 
    sheet = book.sheet_by_index(0)#取第一个sheet页 
    rows= sheet.nrows#取这个sheet页的所有行数 
    case_list = []#保存每一条case 
    for i in range(rows): 
      if i !=0: 
        #把每一条测试用例添加到case_list中 
        case_list.append(sheet.row_values(i)) 
    #调用接口测试的函数,把存所有case的list和excel的路径传进去,因为后面还需要把返回报文和测试结果写到excel中, 
    #所以需要传入excel测试用例的路径,interfaceTest函数在下面有定义 
    interfaceTest(case_list,file_path) 
  
def interfaceTest(case_list,file_path): 
  res_flags = [] 
  #存测试结果的list 
  request_urls = [] 
  #存请求报文的list 
  responses = [] 
  #存返回报文的list 
  for case in case_list: 
    ''''' 
    先遍历excel中每一条case的值,然后根据对应的索引取到case中每个字段的值 
    '''
    try: 
      ''''' 
      这里捕捉一下异常,如果excel格式不正确的话,就返回异常 
      '''
      #项目,提bug的时候可以根据项目来提 
      product = case[0] 
      #用例id,提bug的时候用 
      case_id = case[1] 
      #接口名称,也是提bug的时候用 
      interface_name = case[2] 
      #用例描述 
      case_detail = case[3] 
      #请求方式 
      method = case[4] 
      #请求url 
      url = case[5] 
      #入参 
      param = case[6] 
      #预期结果 
      res_check = case[7] 
      #测试人员 
      tester = case[10] 
    except Exception,e: 
      return '测试用例格式不正确!%s'%e 
    if param== '': 
      ''''' 
      如果请求参数是空的话,请求报文就是url,然后把请求报文存到请求报文list中 
      '''
      new_url = url#请求报文 
      request_urls.append(new_url) 
    else: 
      ''''' 
      如果请求参数不为空的话,请求报文就是url+?+参数,格式和下面一样 
      http://127.0.0.1:8080/rest/login?oper_no=marry&id=100,然后把请求报文存到请求报文list中 
      '''
      new_url = url+'?'+urlParam(param)#请求报文 
      ''''' 
      excel里面的如果有多个入参的话,参数是用;隔开,a=1;b=2这样的,请求的时候多个参数要用&连接, 
      要把;替换成&,所以调用了urlParam这个函数,把参数中的;替换成&,函数在下面定义的 
      '''
      request_urls.append(new_url) 
    if method.upper() == 'GET': 
      ''''' 
      如果是get请求就调用requests模块的get方法,.text是获取返回报文,保存返回报文, 
      把返回报文存到返回报文的list中 
      '''
      print new_url 
      results = requests.get(new_url).text 
      print results 
      responses.append(results) 
      ''''' 
      获取到返回报文之后需要根据预期结果去判断测试是否通过,调用查看结果方法 
      把返回报文和预期结果传进去,判断是否通过,readRes方法在下面定义了。 
      '''
      res = readRes(results,res_check) 
    else: 
      ''''' 
      如果不是get请求,也就是post请求,就调用requests模块的post方法,.text是获取返回报文, 
      保存返回报文,把返回报文存到返回报文的list中 
      '''
      results = requests.post(new_url).text 
      responses.append(results) 
      ''''' 
      获取到返回报文之后需要根据预期结果去判断测试是否通过,调用查看结果方法 
      把返回报文和预期结果传进去,判断是否通过,readRes方法会返回测试结果,如果返回pass就 
      说明测试通过了,readRes方法在下面定义了。 
      '''
      res = readRes(results,res_check) 
    if 'pass' in res: 
      ''''' 
      判断测试结果,然后把通过或者失败插入到测试结果的list中 
      '''
      res_flags.append('pass') 
    else: 
      res_flags.append('fail') 
      ''''' 
      如果不通过的话,就调用写bug的方法,把case_id、接口名称、请求报文、返回报文和预期结果传进去 
      writeBug方法在下面定义了,具体实现是先连接数据库,然后拼sql,插入到bug表中 
      '''
      writeBug(case_id,interface_name,new_url,results,res_check) 
  ''''' 
  全部用例执行完之后,会调用copy_excel方法,把测试结果写到excel中, 
  每一条用例的请求报文、返回报文、测试结果,这三个每个我在上面都定义了一个list 
  来存每一条用例执行的结果,把源excel用例的路径和三个list传进去调用即可,copy_excel方 
  法在下面定义了,也加了注释 
  '''
  copy_excel(file_path,res_flags,request_urls,responses) 
  
def readRes(res,res_check): 
  ''''' 
  :param res: 返回报文 
  :param res_check: 预期结果 
  :return: 通过或者不通过,不通过的话会把哪个参数和预期不一致返回 
  '''
  ''''' 
  返回报文的例子是这样的{"id":"J_775682","p":275.00,"m":"458.00"} 
  excel预期结果中的格式是xx=11;xx=22这样的,所以要把返回报文改成xx=22这样的格式 
  所以用到字符串替换,把返回报文中的":"和":替换成=,返回报文就变成 
  {"id=J_775682","p=275.00,"m=458.00"},这样就和预期结果一样了,当然也可以用python自带的 
  json模块来解析json串,但是有的返回的不是标准的json格式,处理起来比较麻烦,这里我就用字符串的方法了 
  '''
  res = res.replace('":"',"=").replace('":',"=") 
  
  ''''' 
  res_check是excel中的预期结果,是xx=11;xx=22这样的 
  所以用split分割字符串,split是python内置函数,切割字符串,变成一个list 
  ['xx=1','xx=2']这样的,然后遍历这个list,判断list中的每个元素是否存在这个list中, 
  如果每个元素都在返回报文中的话,就说明和预期结果一致 
  上面我们已经把返回报文变成{"id=J_775682","p=275.00,"m=458.00"} 
  '''
  res_check = res_check.split(';') 
  for s in res_check: 
    ''''' 
    遍历预期结果的list,如果在返回报文中,什么都不做,pass代表什么也不做,全部都存在的话,就返回pass 
    如果不在的话,就返回错误信息和不一致的字段,因为res_check是从excel里面读出来的 
    字符Unicode类型的的,python的字符串是str类型的,所以要用str方法强制类型转换,转换成string类型的 
    '''
    if s in res: 
      pass
    else: 
      return '错误,返回参数和预期结果不一致'+str(s) 
  return 'pass'
def urlParam(param): 
  ''''' 
  参数转换,把参数转换为'xx=11&xx=2这样' 
  '''
  return param.replace(';','&') 
  
def copy_excel(file_path,res_flags,request_urls,responses): 
  ''''' 
  :param file_path: 测试用例的路径 
  :param res_flags: 测试结果的list 
  :param request_urls: 请求报文的list 
  :param responses: 返回报文的list 
  :return: 
  '''
  ''''' 
  这个函数的作用是写excel,把请求报文、返回报文和测试结果写到测试用例的excel中 
  因为xlrd模块只能读excel,不能写,所以用xlutils这个模块,但是python中没有一个模块能 
  直接操作已经写好的excel,所以只能用xlutils模块中的copy方法,copy一个新的excel,才能操作 
  '''
  #打开原来的excel,获取到这个book对象 
  book = xlrd.open_workbook(file_path) 
  #复制一个new_book 
  new_book = copy.copy(book) 
  #然后获取到这个复制的excel的第一个sheet页 
  sheet = new_book.get_sheet(0) 
  i = 1
  for request_url,response,flag in zip(request_urls,responses,res_flags): 
    ''''' 
    同时遍历请求报文、返回报文和测试结果这3个大的list 
    然后把每一条case执行结果写到excel中,zip函数可以将多个list放在一起遍历 
    因为第一行是表头,所以从第二行开始写,也就是索引位1的位置,i代表行 
    所以i赋值为1,然后每写一条,然后i+1, i+=1同等于i=i+1 
    请求报文、返回报文、测试结果分别在excel的8、9、11列,列是固定的,所以就给写死了 
    后面跟上要写的值,因为excel用的是Unicode字符编码,所以前面带个u表示用Unicode编码 
    否则会有乱码 
    '''
    sheet.write(i,8,u'%s'%request_url) 
    sheet.write(i,9,u'%s'%response) 
    sheet.write(i,11,u'%s'%flag) 
    i+=1
  #写完之后在当前目录下(可以自己指定一个目录)保存一个以当前时间命名的测试结果,time.strftime()是格式化日期 
  new_book.save('%s_测试结果.xls'%time.strftime('%Y%m%d%H%M%S')) 
def writeBug(bug_id,interface_name,request,response,res_check): 
  ''''' 
  这个函数用来连接数据库,往bugfree数据中插入bug,拼sql,执行sql即可 
  :param bug_id: bug序号 
  :param interface_name: 接口名称 
  :param request: 请求报文 
  :param response: 返回报文 
  :param res_check: 预期结果 
  :return: 
  '''
  bug_id = bug_id.encode('utf-8') 
  interface_name = interface_name.encode('utf-8') 
  res_check = res_check.encode('utf-8') 
  response = response.encode('utf-8') 
  request = request.encode('utf-8') 
  ''''' 
  因为上面几个字符串是从excel里面读出来的都是Unicode字符集编码的, 
  python的字符串上面指定了utf-8编码的,所以要把它的字符集改成utf-8,才能把sql拼起来 
  encode方法可以指定字符集 
  '''
  #取当前时间,作为提bug的时间 
  now = time.strftime("%Y-%m-%d %H:%M:%S") 
  #bug标题用bug编号加上接口名称然后加上_结果和预期不符,可以自己随便定义要什么样的bug标题 
  bug_title = bug_id + '_' + interface_name + '_结果和预期不符'
  #复现步骤就是请求报文+预期结果+返回报文 
  step = '[请求报文]<br />'+request+'<br/>'+'[预期结果]<br/>'+res_check+'<br/>'+'<br/>'+'[响应报文]<br />'+'<br/>'+response 
  #拼sql,这里面的项目id,创建人,严重程度,指派给谁,都在sql里面写死,使用的时候可以根据项目和接口 
  # 来判断提bug的严重程度和提交给谁 
  sql = "INSERT INTO `bf_bug_info` (`created_at`, `created_by`, `updated_at`, `updated_by`, `bug_status`, `assign_to`, `title`, `mail_to`, `repeat_step`, `lock_version`, `resolved_at`, `resolved_by`, `closed_at`, `closed_by`, `related_bug`, `related_case`, `related_result`, " \ 
     "`productmodule_id`, `modified_by`, `solution`, `duplicate_id`, `product_id`, " \ 
     "`reopen_count`, `priority`, `severity`) VALUES ('%s', '1', '%s', '1', 'Active', '1', '%s', '系统管理员', '%s', '1', NULL , NULL, NULL, NULL, '', '', '', NULL, " \ 
     "'1', NULL, NULL, '1', '0', '1', '1');"%(now,now,bug_title,step) 
  #建立连接,使用MMySQLdb模块的connect方法连接mysql,传入账号、密码、数据库、端口、ip和字符集 
  coon = MySQLdb.connect(user='root',passwd='123456',db='bugfree',port=3306,host='127.0.0.1',charset='utf8') 
  #建立游标 
  cursor = coon.cursor() 
  #执行sql 
  cursor.execute(sql) 
  #提交 
  coon.commit() 
  #关闭游标 
  cursor.close() 
  #关闭连接 
  coon.close() 
if __name__ == '__main__': 
  ''''' 
  然后进行调用,调用的时候需要传入一个excel,调用方式是 python test.py test_case.xls 
  sys.argv[1]的意思是取传入的第二个参数,也就是索引是1的, 
  第一个是这个python文件的文件名,如果不传入参数运行的话,会提示错误,如果正确的话, 
  会调用读excel的程序,执行用例,运行完成后,会打印Done 
  '''
  try: 
    filename = sys.argv[1] 
  except IndexError,e: 
    print 'Please enter a correct testcase! \n e.x: python gkk.py test_case.xls'
  else: 
    readExcel(filename) 
  print 'Done!'

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦