利用python开发app实战

你说,我们的未来

被装进棺材,染不上尘埃
***

我很早之前就想开发一款app玩玩,无奈对java不够熟悉,之前也没有开发app的经验,因此一直耽搁了。最近想到尝试用python开发一款app,google搜索了一番后,发现确实有路可寻,目前也有了一些相对成熟的模块,于是便开始了动手实战,过程中发现这其中有很多坑,好在最终依靠google解决了,因此小记一番。

说在前面的话

  python语言虽然很万能,但用它来开发app还是显得有点不对路,因此用python开发的app应当是作为编码练习、或者自娱自乐所用,加上目前这方面的模块还不是特别成熟,bug比较多,总而言之,劝君莫轻入。

准备工作

  利用python开发app需要用到python的一个模块–kivy,kivy是一个开源的,跨平台的Python开发框架,用于开发使用创新的应用程序。简而言之,这是一个python桌面程序开发框架(类似wxpython等模块),强大的是kivy支持linux、mac、windows、android、ios平台,这也是为什么开发app需要用到这个模块。
  虽然kivy是跨平台的,但是想要在不同的平台使用python代码,还需要将python代码打包成对应平台的可执行程序,好在kivy项目下有个打包工具项目–buildozer,这是官方推荐的打包工具,因为相对比较简单,自动化程度高,其他项目比如:python-for-android也能起到类似的作用,这里不展开介绍。

搭建kivy开发环境

需要在pc上安装kivy开发环境,这里演示下mac与linux下的安装过程。
install kivy for mac
安装一些依赖包:

brew install pkg-config sdl2 sdl2_image sdl2_ttf sdl2_mixer gstreamer

安装cython以及kivy:

pip install cython==0.25
pip install kivy

如果安装kivy报错,则使用下面的方式安装kivy:

git clone https://github.com/kivy/kivy
python setup.py install

安装后测试:

$python
Python 2.7.10 (default, Jul 15 2017, 17:16:57)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import kivy
[INFO   ] [Logger      ] Record log in /Users/didi/.kivy/logs/kivy_18-05-08_4.txt
[INFO   ] [Kivy        ] v1.10.1.dev0, git-5f6c66e, 20180507
[INFO   ] [Python      ] v2.7.10 (default, Jul 15 2017, 17:16:57)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)]

说明:导入kivy模块没有报错则说明安装成功。

install kivy for centos7

先安装依赖:

yum install \
    make \
    mercurial \
    automake \
    gcc \
    gcc-c++ \
    SDL_ttf-devel \
    SDL_mixer-devel \
    khrplatform-devel \
    mesa-libGLES \
    mesa-libGLES-devel \
    gstreamer-plugins-good \
    gstreamer \
    gstreamer-python \
    mtdev-devel \
    python-devel \
    python-pip \
    java-devel

安装cython以及kivy:

pip install Cython==0.20
pip install kivy

centos安装kivy参考:https://kivy.org/docs/installation/installation-linux.html#using-software-packages

说明:其他安装kivy方式可移步:https://kivy.org/#download(需要FQ)

用kivy开发第一个python app

安装完kivy就可以开发app程序了,这里演示下hello-world程序,关于kivy更复杂的用法不是本文重点,后面再成文介绍。
1) 创建一个main.py文件,写入:

#! -*- coding:utf-8 -*-
from kivy.app import App
class HelloApp(App):
    pass
if __name__ == '__main__':
    HelloApp().run()

2)创建一个hello.kv文件,写入:

Label:
    text: 'Hello, World! I am nMask'

简单说明:main.py是入口函数,定义了一个HelloApp类,该类继承kivy.app;hello.kv文件是kivy程序,相当于定义界面风格等,该文件命名规则为类名小写且去除app。

运行第一个python app

python main.py

运行结果:

安装buildozer工具

  通过以上的编码,我创建了自己的第一个python app程序,该程序可以直接在mac、linux、windows平台下运行,那么如何让它在安卓或者苹果手机上运行呢?我们知道在安卓上运行,需要将其打包成apk安装程序,因此就需要用到前面提到过的buildozer工具,(buildozer工具可以打包kivy程序,支持android、ios等),buildozer的安装过程比较简单:

pip install buildozer

使用buildozer工具将kivy程序打包成apk

在python项目目录下运行:

buildozer init

运行成功将会创建一个配置文件buildozer.spec,可以通过修改配置文件更改app的名称等,然后运行:

buildozer android debug deploy run

运行以上命令将会生成跨平台的安装包,可适用安卓、ios等,如果用于安卓,则是利用python-for-android项目。

在第一次运行以上命令的时候,会自动在系统中下载安卓sdk等必要文件,如下图。(过程需要FQ,而且有很多依赖需要下载)

python3+requests:接口自动化测试(二)

转载请注明出处:

 

前言:上篇文章python3+requests+unittest:接口自动化测试(一): ,已经介绍了基于unittest框架的实现接口自动化,但是也存在一些问题,比如最明显的测试数据和业务没有区分开,接口用例不便于管理等,所以又对此修改完善。接下来主要是介绍该套接口自动化框架的设计到实现,参考代码的git地址:

 

1.代码框架展示

 

(1)case:存放测试用例数据的,比如请求类型get/post、请求url、请求header、请求数据等;

(2)data:获取excel文件中相应数据的方法封装,获取excel中对应表格内的数据,excel的行列数据等:get_data.py;判断用例之间是否存在依赖关系并获取依赖数据:dependent_data.py;初始化excel文件:data_config.py;

(3)dataconfig:存放请求中涉及到的header、data、cookies等数据;

(4)log:存放测试完成之后生成的日志文件,可以查看日志定位问题;

(5)main:脚本执行的主函数run_test.py

(6)util:通用方法的封装,各种不同断言方式common_assert.py;对excel文件的读写操作operation_excel.py;从请求返回数据中拿取数据作为下一个接口的请求header数据operation_header.py;从json文件中拿取想要的数据operation_json.py;将接口自动化过程中的相关日志输出到log.txt中print_log.py;根据请求类型的不同执行对应的get/post方法runmethod.py;将测试结果以邮件形式发送给相关人员send_mail.py。

 

2.代码实现说明

(1)首先看下用例数据

说明:该用例只是用来覆盖一些接口场景而测试使用的,有兴趣的可以参考源码用自己项目的真实数据来实现

 

 先判断是否执行:如果yes,执行该条用例;如果no,直接跳过该条用例。

执行用例:获取用例的url、请求类型、请求头header、请求数据,request.get/post执行该条接口用例。

在执行用例过程中,会存在特殊情况:(1)比如test_04依赖于test_03,test_04中的请求字段supplier的参数数据来源于test_03的response中value[0].biz字段的数据,所以在执行接口过程中需要判断是否存在依赖关系;(2)比如test_06请求数据需要test_05的response中的cookies数据,所以这种类型接口也要特殊处理。

执行完成后:写入实际结果,与预期结果做对比,进行断言。

(2)看了用例excel后,对基本的流程有个大概了解,现在的问题就是如何拿取对应的数据执行接口得到运行结果

    if is_run:
        url = self.data.get_request_url(i)
        method = self.data.get_request_method(i)
        #获取请求参数
        data = self.data.get_data_value(i)
        # 获取excel文件中header关键字
        header_key = self.data.get_request_header(i)
        # 获取json文件中header_key对应的头文件数据
        header = self.data.get_header_value(i)
        expect = self.data.get_expect_data(i)
        depend_case = self.data.is_depend(i)

举例说明1:请求url数据是存放在excel中,我们通过操作excel文件到特定单元格拿到url数据

    #获取url
    def get_request_url(self,row):
        col = int(data_config.get_url())
        url = self.oper_excel.get_cell_value(row,col)
        return url

举例说明2:请求头header或者请求数据中有的数据为空,所以我们在拿取数据过程中要做判断

    #获取请求数据
    def get_request_data(self,row):
        col = int(data_config.get_data())
        data = self.oper_excel.get_cell_value(row,col)
        if data == '':
            return None
        return data

首先拿取excel中表格中的关键字,再通过关键字去对应json文件拿取具体的请求数据。比如先拿取excel中请求数据中的hotwords,再根据此关键字去json文件读取hotwords的键值数据

    "hotwords": {
        "bizName": "globalSearchClient",
        "sign": "8c8bc3ee9d6c4b7b8a390ae298cb6db5",
        "timeMills": "1524906299999"
    }
    #通过获取请求关键字拿到data数据
    def get_data_value(self,row):
        oper_json = OperationJson('../dataconfig/request_data.json')
        request_data = oper_json.get_data(self.get_request_data(row))
        return request_data
    #根据关键字获取数据
    '''
    dict['key']只能获取存在的值,如果不存在则触发KeyError
    dict.get(key, default=None),返回指定键的值,如果值不在字典中返回默认值None
    excel文件中请求数据有可能为空,所以用get方法获取
    '''
    def get_data(self,key):
        # return self.data[key]
        return self.data.get(key)

 (3)一般的接口都是单接口,即是单独请求,没有上下依赖关系的,针对这种只要模拟请求拿到数据进行断言就可以了。但是实际项目中会存在特殊场景,比如test_03和test04

说明:test_04中,请求数据qqmusic_more中的supplier字段依赖于test_03中的返回数据value[0].biz的值

"qqmusic_more": {
        "bizName": "globalSearchClient",
        "appLan": "zh_CN",
        "musicLimit": "20",
        "imei": "864044030085594",
        "keyword": "fly",
        "timeMills": "1527134461256",
        "page": "0",
        "sign": "17daa7e3e84bd4dfbe9a1bd9a1bd7e62",
        "mac": "90f05205d7b7",
        "sessionId": "43e605b914874cd99b47ac997e19c1a1",
        "network": "1",
        "supplier": "",
        "language": "zh_CN",
    }

先执行test_03,获取依赖的返回数据value[0].biz的值

    #执行依赖测试,获取test_03返回结果
    def run_dependent(self):
        row_num = self.oper_excel.get_row_num(self.case_id)
        request_data = self.data.get_data_value(row_num)
        header = self.data.get_request_header(row_num)
        method = self.data.get_request_method(row_num)
        url = self.data.get_request_url(row_num)
        res = self.method.run_main(method,url,request_data,header,params=request_data)
        return res

    #获取依赖字段的响应数据:通过执行依赖测试case来获取响应数据,响应中某个字段数据作为依赖key的value
    def get_value_for_key(self,row):
        #获取依赖的返回数据key
        depend_data = self.data.get_depend_key(row)
        print(depend_data)  #depend_data打印数据:value[0].biz
        #执行依赖case返回结果
        response_data = self.run_dependent()
        # print(depend_data)
        # print(response_data)

        return [match.value for match in parse(depend_data).find(response_data)][0]

再将value[0].biz值放入test_04请求数据qqmusic_more中的supplier字段中

        if depend_case != None:
            self.depend_data = DependentData(depend_case)
            #获取依赖字段的响应数据
            depend_response_data = self.depend_data.get_value_for_key(i)
            #获取请求依赖的key
            depend_key = self.data.get_depend_field(i)
            #将依赖case的响应返回中某个字段的value赋值给该接口请求中某个参数
            data[depend_key] = depend_response_data

(4)拿到请求相关数据后,执行该条case,获取response;然后实际结果与预期结果进行断言

res = self.run_method.run_main(method,url,data,header,params=data)
'''
get请求参数是params:request.get(url='',params={}),post请求数据是data:request.post(url='',data={})
excel文件中没有区分直接用请求数据表示,则data = self.data.get_data_value(i)拿到的数据,post请求就是data=data,get请就是params=data
'''

根据get、post类型区分

class RunMethod:
    def post_main(self,url,data,header=None):
        res = None
        if header != None:
            res = requests.post(url=url,data=data,headers=header)
        else:
            res = requests.post(url=url,data=data)
        return res.json()

    def get_main(self,url,params=None,header=None):
        res = None
        if header != None:
            res = requests.get(url=url, params=params, headers=header)
        else:
            res = requests.get(url=url, params=params)
        return res.json()

    def run_main(self,method,url,data=None,header=None,params=None):
        res = None
        if method == 'post':
            res = self.post_main(url,data,header)
        else:
            res = self.get_main(url,params,header)
        return res

(5)执行接口case过程中,可能存在某条case异常报错,导致下面的case无法运行,所以我们既要将异常日志存放在特定文件中方便后续排查,也要保证下面的case能够不受影响继续执行完

           try:...
            
           except Exception as e:
                # 将报错写入指定路径的日志文件里
                with open(log_file,'a',encoding='utf-8') as f:
                    f.write("\n第%s条用例报错:\n" % i)
                initLogging(log_file,e)
                fail_count.append(i)

抓取日志的方法可以使用python内置模块logging,具体用法可以参考:

import logging

def initLogging(logFilename,e):

  logging.basicConfig(
                    level = logging.INFO,
                    format ='%(asctime)s-%(levelname)s-%(message)s',
                    datefmt = '%y-%m-%d %H:%M',
                    filename = logFilename,
                    filemode = 'a')
  fh = logging.FileHandler(logFilename,encoding='utf-8')
  logging.getLogger().addHandler(fh)
  log = logging.exception(e)
  return log

日志文件log.txt结果:直接定位问题出在哪儿

第5条用例报错:
18-06-19 10:27-ERROR-string indices must be integers
Traceback (most recent call last):
  File "C:/Users/xxx/Documents/GitHub/python3_interface/main/run_test.py", line 70, in go_on_run
    op_header.write_cookie()
  File "C:\Users\xxx\Documents\GitHub\python3_interface\util\operation_header.py", line 30, in write_cookie
    cookie = requests.utils.dict_from_cookiejar(self.get_cookie())
  File "C:\Users\zhangying1\Documents\GitHub\python3_interface\util\operation_header.py", line 25, in get_cookie
    url = self.get_response_url()+"&callback=jQuery21008240514814031887_1508666806688&_=1508666806689"
  File "C:\Users\xxx\Documents\GitHub\python3_interface\util\operation_header.py", line 18, in get_response_url
    url = self.response['data']['url'][0]
TypeError: string indices must be integers

(6)接口自动化测试执行完成后,需要将测试结果发送给项目组相关人员,邮件发送实现方法参考:

self.send_mail.send_main(pass_count,fail_count,no_run_count)
#coding:utf-8
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import datetime

class SendEmail:
    global send_user
    global email_host
    global password
    password = "lunkbrgwqxhfjgxx"
    email_host = "smtp.qq.com"
    send_user = "xxx@qq.com"

    def send_mail(self,user_list,sub,content):
        user = "shape" + "<" + send_user + ">"

        # 创建一个带附件的实例
        message = MIMEMultipart()
        message['Subject'] = sub
        message['From'] = user
        message['To'] = ";".join(user_list)

        # 邮件正文内容
        message.attach(MIMEText(content, 'plain', 'utf-8'))

        # 构造附件(附件为txt格式的文本)
        filename = '../log/log.txt'
        time = datetime.date.today()
        att = MIMEText(open(filename, 'rb').read(), 'base64', 'utf-8')
        att["Content-Type"] = 'application/octet-stream'
        att["Content-Disposition"] = 'attachment; filename="%s_Log.txt"'% time
        message.attach(att)

        server = smtplib.SMTP_SSL()
        server.connect(email_host,465)# 启用SSL发信, 端口一般是465
        # server.set_debuglevel(1)# 打印出和SMTP服务器交互的所有信息
        server.login(send_user,password)
        server.sendmail(user,user_list,message.as_string())
        server.close()

    def send_main(self,pass_list,fail_list,no_run_list):
        pass_num = len(pass_list)
        fail_num = len(fail_list)
        #未执行的用例
        no_run_num = len(no_run_list)
        count_num = pass_num + fail_num + no_run_num

        #成功率、失败率
        '''
        用%对字符串进行格式化
        %d 格式化整数
        %f 格式化小数;想保留两位小数,需要在f前面加上条件:%.2f;用%%来表示一个%
        如果你不太确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串 
       '''
        pass_result = "%.2f%%" % (pass_num/count_num*100)
        fail_result = "%.2f%%" % (fail_num/count_num*100)
        no_run_result = "%.2f%%" % (no_run_num/count_num*100)

        user_list = ['xxx@qq.com']
        sub = "接口自动化测试报告"
        content = "接口自动化测试结果:\n通过个数%s个,失败个数%s个,未执行个数%s个:通过率为%s,失败率为%s,未执行率为%s\n日志见附件" % (pass_num,fail_num,no_run_num,pass_result,fail_result,no_run_result)
        self.send_mail(user_list,sub,content)

到此,就基本完成。

 

说明:

1.只是大概整理了接口自动化实现的设计流程,需要源码参考的可查看:

2.这套接口框架中还有很多需要完善的地方,比如断言方法不够丰富,比如测试报告展示需要完善,等等。各位有兴趣的可以不断完善改进

 

python输出带颜色字体详解

  在python开发的过程中,经常会遇到需要打印各种信息。海量的信息堆砌在控制台中,就会导致信息都混在一起,降低了重要信息的可读性。这时候,如果能给重要的信息加上字体颜色,那么就会更加方便用户阅读了。   

  当然了,控制台的展示效果有限,并不能像前段一样炫酷,只能做一些简单的设置。不过站在可读性的角度来看,已经好很多了。

书写格式:
     开头部分\033[显示方式;前景色;背景色m + 结尾部分:\033[0m

     注意:开头部分的三个参数:显示方式,前景色,背景色是可选参数,可以只写其中的某一个;另外由于表示三个参数不同含义的数值都是唯一的没有重复的,所以三个参数的书写先后顺序没有固定要求,系统都能识别;但是,建议按照默认的格式规范书写。
     对于结尾部分,其实也可以省略,但是为了书写规范,建议\033[***开头,\033[0m结尾。

-------------------------------------------
-------------------------------------------
字体色     |       背景色     |      颜色描述
-------------------------------------------
30        |        40       |       黑色
31        |        41       |       红色
32        |        42       |       绿色
33        |        43       |       黃色
34        |        44       |       蓝色
35        |        45       |       紫红色
36        |        46       |       青蓝色
37        |        47       |       白色
-------------------------------------------
-------------------------------
显示方式     |      效果
-------------------------------
0           |     终端默认设置
1           |     高亮显示
4           |     使用下划线
5           |     闪烁
7           |     反白显示
8           |     不可见
-------------------------------

数值表示的参数含义:

显示方式: 0(默认值)、1(高亮)、22(非粗体)、4(下划线)、24(非下划线)、 5(闪烁)、25(非闪烁)、7(反显)、27(非反显)
前景色: 30(黑色)、31(红色)、32(绿色)、 33(黄色)、34(蓝色)、35(洋 红)、36(青色)、37(白色)
背景色: 40(黑色)、41(红色)、42(绿色)、 43(黄色)、44(蓝色)、45(洋 红)、46(青色)、47(白色)


常见开头格式
\033[0m            默认字体正常显示,不高亮
\033[32;0m       红色字体正常显示
\033[1;32;40m  显示方式: 高亮    字体前景色:绿色  背景色:黑色
\033[0;31;46m  显示方式: 正常    字体前景色:红色  背景色:青色

\033[1;31m  显示方式: 高亮    字体前景色:红色  背景色:无  举例说明: 示例1:

print('\033[1;35;0m字体变色,但无背景色 \033[0m')  # 有高亮 或者 print('\033[1;35m字体有色,但无背景色 \033[0m')
print('\033[1;45m 字体不变色,有背景色 \033[0m')  # 有高亮
print('\033[1;35;46m 字体有色,且有背景色 \033[0m')  # 有高亮
print('\033[0;35;46m 字体有色,且有背景色 \033[0m')  # 无高亮

结果:

示例2:

print('\033[0;36m床前明月光,')
print('疑是地上霜。')
print('举头望明月,')
print('低头思故乡。\033[0m')

结果:

实例 

  

  

   

  

  

  

  

  

  

 

,

  当然了,控制台的展示效果有限,并不能像前段一样炫酷,只能做一些简单的设置。不过站在可读性的角度来看,已经好很多了。

Python3 中 sys.argv[ ]的用法解释

  sys.argv[]说白了就是一个从程序外部获取参数的桥梁,这个“外部”很关键,所以那些试图从代码来说明它作用的解释一直没看明白。因为我们从外部取得的参数可以是多个,所以获得的是一个列表(list),也就是说sys.argv其实可以看作是一个列表,所以才能用[]提取其中的元素。其第一个元素是程序本身,随后才依次是外部给予的参数。

下面我们通过一个极简单的test.py程序的运行结果来说明它的用法。

1 #test.py
2 
3 import sys 4 a=sys.argv[0] 5 print(a)

 将test.py保存在c盘的根目录下。

 在程序中找到 ‘运行’->点击->输入”cmd”->回车键   进入控制台命令窗口(如下图),先输入cd c:\   (作用是将命令路径改到c盘根目录),然后输入test.py运行我们刚刚写的程序:

  得到的结果是C:\test.py,这就是0指代码(即此.py程序)本身的意思。

  然后我们将代码中0改为1 :

  a=sys.argv[1]

  保存后,再从控制台窗口运行,这次我们加上一个参数,输入:test.py what

 得到的结果就是我们输入的参数what,看到这里你是不是开始明白了呢。

 那我们再把代码修改一下:

 a=sys.argv[2:]

 保存后,再从控制台窗台运行程序,这次多加几个参数,以空格隔开:

 test.py a b c d e f

 得到的结果为[‘b’, ’c’, ’d’, ’e’, ’f’]

 

   应该大彻大悟了吧。Sys.argv[ ]其实就是一个列表,里边的项为用户输入的参数,关键就是要明白这参数是从程序外部输入的,而非代码本身的什么地方,要想看到它的效果就应该 将程序保存了,从外部来运行程序并给出参数。

 

python:利用configparser模块读写配置文件

在自动化测试过程中,为了提高脚本的可读性和降低维护成本,将一些通用信息写入配置文件,将重复使用的方法写成公共模块进行封装,使用时候直接调用即可。

这篇博客,介绍下python中利用configparser模块读写配置文件的方法,仅供参考。。。

 

一、读取文件

configparser模块支持读取.conf和.ini等类型的文件,那么首先在文件夹新建一个.ini文件,写入一些信息,如下图:

示例代码如下:

 1 # coding=utf-8
 2 import configparser  3 import os  4 
 5 os.chdir("E:\\Automation\\UI\\testcase")  6 cf = configparser.ConfigParser()  7 
 8 # read(filename) 读文件内容
 9 filename = cf.read("test.ini") 10 print(filename) 11 
12 # sections() 得到所有的section,以列表形式返回
13 sec = cf.sections() 14 print(sec) 15 
16 # options(section) 得到section下的所有option
17 opt = cf.options("mysql") 18 print(opt) 19 
20 # items 得到section的所有键值对
21 value = cf.items("driver") 22 print(value) 23 
24 # get(section,option) 得到section中的option值,返回string/int类型的结果
25 mysql_host = cf.get("mysql","host") 26 mysql_password = cf.getint("mysql","password") 27 print(mysql_host,mysql_password)

执行脚本,结果如下所示:

1 ['test.ini'] 2 ['driver', 'mysql'] 3 ['host', 'port', 'username', 'password'] 4 [('path', 'E:\\Automation\\UI\\testcase\\browser\\chromedriver.exe'), ('url', 'https://www.baidu.com/')] 5 127.0.0.1 123456

脚本解析:

cf.read(filename):读取文件内容

cf.sections():得到所有的section,并且以列表形式返回

cf.options(section):得到section下所有的option

cf.items(option):得到该section所有的键值对

cf.get(section,option):得到section中option的值,返回string类型的结果

cf.getint(section,option):得到section中option的值,返回int类型的结果

 

二、写入文件

如果需要在配置文件写入内容,需要os函数帮忙,示例代码如下:

 1 # coding=utf-8
 2 import configparser  3 import os  4 
 5 os.chdir("E:\\Automation\\UI\\testcase")  6 cf = configparser.ConfigParser()  7 
 8 # 往配置文件写入内容
 9 
10 # add section 添加section项
11 # set(section,option,value) 给section项中写入键值对
12 cf.add_section("mq") 13 cf.set("mq", "user", "laozhang") 14 cf.add_section("kafka") 15 cf.set("kafka", "user", "xiaozhang") 16  
17 # write to file
18 with open("test1.ini","w+") as f: 19     cf.write(f)

执行脚本,结果如下所示:

脚本解析:

cf.write(filename):将configparser对象写入.ini类型的文件

add_section():添加一个新的section

add_set(section,option,value):对section中的option信息进行写入

 

三、修改文件

还可以利用os函数对文件进行修改,示例代码如下:

 1 # coding=utf-8
 2 import configparser  3 import os  4 
 5 os.chdir("E:\\Automation\\UI\\testcase")  6 cf = configparser.ConfigParser()  7 
 8 # 修改配置文件的内容
 9 
10 # remove_section(section) 删除某个section的数值
11 # remove_option(section,option) 删除某个section下的option的数值
12 cf.read("test1.ini")
13 cf.remove_option("kafka","user") 14 cf.remove_section("mq") 15 16 # write to file 17 with open("test1.ini","w+") as f: 18 cf.write(f)

执行脚本,结果如下所示:

脚本解析:

cf.read(filename):读取文件(这里需要注意的是:一定要先读取文件,再进行修改)

cf.remove_section(section):删除文件中的某个section的数值

cf.remove_option(section,option):删除文件中某个section下的option的数值

 

如上所示,就是configparser模块读写配置文件的方法,代码仅为参考,具体使用请自行实践。。。

 

Python:struct模块的pack、unpack

mport struct

pack、unpack、pack_into、unpack_from

 1 # ref: http://blog.csdn<a href="http://lib.csdn.net/base/dotnet" class='replace_word' title=".NET知识库" target='_blank' style='color:#df3434; font-weight:bold;'>.NET</a>/JGood/archive/2009/06/22/4290158.aspx  
 2   
 3 import struct  
 4   
 5 #pack - unpack  
 6 print  
 7 print '===== pack - unpack ====='  
 8   
 9 str = struct.pack("ii", 20, 400)  
10 print 'str:', str  
11 print 'len(str):', len(str) # len(str): 8   
12   
13 a1, a2 = struct.unpack("ii", str)  
14 print "a1:", a1  # a1: 20  
15 print "a2:", a2  # a2: 400  
16   
17 print 'struct.calcsize:', struct.calcsize("ii") # struct.calcsize: 8  
18   
19   
20 #unpack  
21 print  
22 print '===== unpack ====='  
23   
24 string = 'test astring'  
25 format = '5s 4x 3s'  
26 print struct.unpack(format, string) # ('test ', 'ing')  
27   
28 string = 'he is not very happy'  
29 format = '2s 1x 2s 5x 4s 1x 5s'  
30 print struct.unpack(format, string) # ('he', 'is', 'very', 'happy')  
31   
32   
33 #pack  
34 print  
35 print '===== pack ====='  
36   
37 a = 20  
38 b = 400  
39   
40 str = struct.pack("ii", a, b)  
41 print 'length:', len(str) #length: 8  
42 print str  
43 print repr(str) # '/x14/x00/x00/x00/x90/x01/x00/x00'  
44   
45   
46 #pack_into - unpack_from  
47 print  
48 print '===== pack_into - unpack_from ====='  
49 from ctypes import create_string_buffer  
50   
51 buf = create_string_buffer(12)  
52 print repr(buf.raw)  
53   
54 struct.pack_into("iii", buf, 0, 1, 2, -1)  
55 print repr(buf.raw)  
56   
57 print struct.unpack_from("iii", buf, 0)  

 

运行结果:

[work@db-testing-com06-vm3.db01.baidu.com ]$ python struct_pack.py

===== pack – unpack =====
str: ?
len(str): 8
a1: 20
a2: 400
struct.calcsize: 8

===== unpack =====
(‘test ‘, ‘ing’)
(‘he’, ‘is’, ‘very’, ‘happy’)

===== pack =====
length: 8
?
‘/x14/x00/x00/x00/x90/x01/x00/x00’

===== pack_into – unpack_from =====
‘/x00/x00/x00/x00/x00/x00/x00/x00/x00/x00/x00/x00’
‘/x01/x00/x00/x00/x02/x00/x00/x00/xff/xff/xff/xff’
(1, 2, -1)

 

Python是一门非常简洁的语言,对于数据类型的表示,不像其他语言预定义了许多类型(如:在C#中,光整型就定义了8种)

它只定义了六种基本类型:字符串,整数,浮点数,元组(set),列表(array),字典(key/value)

通过这六种数据类型,我们可以完成大部分工作。但当Python需要通过网络与其他的平台进行交互的时候,必须考虑到将这些数据类型与其他平台或语言之间的类型进行互相转换问题。打个比方:C++写的客户端发送一个int型(4字节)变量的数据到Python写的服务器,Python接收到表示这个整数的4个字节数据,怎么解析成Python认识的整数呢? Python的标准模块struct就用来解决这个问题。

 

struct模块的内容不多,也不是太难,下面对其中最常用的方法进行介绍:

1、 struct.pack
struct.pack用于将Python的值根据格式符,转换为字符串(因为Python中没有字节(Byte)类型,可以把这里的字符串理解为字节流,或字节数组)。其函数原型为:struct.pack(fmt, v1, v2, …),参数fmt是格式字符串,关于格式字符串的相关信息在下面有所介绍。v1, v2, …表示要转换的python值。

2、 struct.unpack
struct.unpack做的工作刚好与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个元组。 

下面的例子将两个整数转换为字符串(字节流):

 

import struct

a = 20
b = 400
s = struct.pack('ii', a, b)
print(s, type(s))
#输出:b'\x14\x00\x00\x00\x90\x01\x00\x00' <class 'bytes'>
print('length: ', len(s))
#输出:length:  8
s2 = struct.unpack('ii', s)
print(s2)
#输出:(20, 400)

s2 = struct.unpack('ii', s)
#报错:unpack requires a buffer of 4 bytes
#==>解压需要一个4字节的缓冲区,也就是说'ii'表示8个字节的缓冲

 

#格式符”i”表示转换为int,’ii’表示有两个int变量。

#进行转换后的结果长度为8个字节(int类型占用4个字节,两个int为8个字节)

可以使用python的内置函数repr来获取可识别的字符串,其中十六进制的0x00000014, 0x00001009分别表示20和400。

 

3、 struct.calcsize
struct.calcsize用于计算格式字符串所对应的结果的长度,如:struct.calcsize(‘ii’),返回8。因为两个int类型所占用的长度是8个字节。

1 import struct  
2 print "len: ", struct.calcsize('i')       # len:  4  
3 print "len: ", struct.calcsize('ii')      # len:  8  
4 print "len: ", struct.calcsize('f')       # len:  4  
5 print "len: ", struct.calcsize('ff')      # len:  8  
6 print "len: ", struct.calcsize('s')       # len:  1  
7 print "len: ", struct.calcsize('ss')      # len:  2  
8 print "len: ", struct.calcsize('d')       # len:  8  
9 print "len: ", struct.calcsize('dd')      # len:  16 

 

4、 struct.pack_into、 struct.unpack_from
这两个函数在Python手册中有所介绍,但没有给出如何使用的例子。其实它们在实际应用中用的并不多。Google了很久,才找到一个例子,贴出来共享一下:

 1 #!/usr/bin/env python  
 2 #encoding: utf8  
 3   
 4 import sys  
 5 reload(sys)  
 6 sys.setdefaultencoding("utf-8")  
 7   
 8 import struct  
 9 from ctypes import create_string_buffer  
10   
11 buf = create_string_buffer(12)  
12 print repr(buf.raw)     # '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'  
13   
14 struct.pack_into("iii", buf, 0, 1, 2, -1)  
15 print repr(buf.raw)     # '\x01\x00\x00\x00\x02\x00\x00\x00\xff\xff\xff\xff'  
16   
17 print struct.unpack_from("iii", buf, 0)     # (1, 2, -1) 

struct 类型表

 

Format C Type Python type Standard size Notes
x pad byte no value    
c char string of length 1 1  
b signed char integer 1 (3)
B unsigned char integer 1 (3)
? _Bool bool 1 (1)
h short integer 2 (3)
H unsigned short integer 2 (3)
i int integer 4 (3)
I unsigned int integer 4 (3)
l long integer 4 (3)
L unsigned long integer 4 (3)
q long long integer 8 (2), (3)
Q unsigned long long integer 8 (2), (3)
f float float 4 (4)
d double float 8 (4)
s char[] string 1  
p char[] string    
P void * integer   (5), (3)

Python:查看矩阵大小,查看列表大小

对于Python3.5

查看矩阵大小

>>>import numpy as np  
>>>from numpy import random  
>>>matrix = random.random(size=(2,4))   
>>>matrix.shape   #矩阵每维的大小 
(2,4)  
>>>print matrix.size  #矩阵所有数据的个数
8
>>>print matrix1.dtype  #矩阵每个数据的类型  
dtype('float64')

查看列表大小

>>>a=[1,2,3,4]
>>>len(a)
4

  

Python:sample函数

sample(序列a,n)

功能:从序列a中随机抽取n个元素,并将n个元素生以list形式返回。

例:

from random import randint, sample date = [randint(10,20) for _ in range(10)] c = sample(date, 5) print(c) 输出:[12, 17, 10, 12, 17]

  1. randint(10,20) for _ in range(10):从10~20间随机抽取10个数;

Python类中的self到底是干啥的

Python类中的self到底是干啥的

Python编写类的时候,每个函数参数第一个参数都是self,一开始我不管它到底是干嘛的,只知道必须要写上。后来对Python渐渐熟悉了一点,再回头看self的概念,似乎有点弄明白了。

首先明确的是self只有在类的方法中才会有,独立的函数或方法是不必带有self的。self在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。

self名称不是必须的,在python中self不是关键词,你可以定义成a或b或其它名字都可以,但是约定成俗(为了和其他编程语言统一,减少理解难度),不要搞另类,大家会不明白的。

下例中将self改为myname一样没有错误:

class Person:
    def _init_(myname,name):
        myname.name=name
    def sayhello(myname):
        print 'My name is:',myname.name
p=Person('Bill')
print p

self指的是类实例对象本身(注意:不是类本身)。

class Person:
    def _init_(self,name):
        self.name=name
    def sayhello(self):
        print 'My name is:',self.name
p=Person('Bill')
print p

在上述例子中,self指向Person的实例p。 为什么不是指向类本身呢,如下例子:

class Person:
    def _init_(self,name):
        self.name=name
    def sayhello(self):
        print 'My name is:',self.name
p1=Person('Bill')
p2 = Person('Apple')
print p1

如果self指向类本身,那么当有多个实例对象时,self指向哪一个呢?

总结

self在定义时需要定义,但是在调用时会自动传入。

self的名字并不是规定死的,但是最好还是按照约定是用self

self总是指调用时的类的实例。

Python中_,__,__xx__的区别

_xx 单下划线开头

Python中没有真正的私有属性或方法,可以在你想声明为私有的方法和属性前加上单下划线,以提示该属性和方法不应在外部调用.如果真的调用了也不会出错,但不符合规范.

本文为译文,版权属于原作者,在此翻译为中文分享给大家。英文原文地址:

Python中不存在真正的私有方法。为了实现类似于c++中私有方法,可以在类的方法或属性前加一个“_”单下划线,意味着该方法或属性不应该去调用,它并不属于API。

在使用property时,经常出现这个问题:

class BaseForm(StrAndUnicode):
    ...
    
    def _get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors
    
    errors = property(_get_errors)

上面的代码片段来自于django源码(django/forms/forms.py)。这里的errors是一个属性,属于API的一部分,但是_get_errors是私有的,是不应该访问的,但可以通过errors来访问该错误结果。

“__”双下划线

这个双下划线更会造成更多混乱,但它并不是用来标识一个方法或属性是私有的,真正作用是用来避免子类覆盖其内容。

让我们来看一个例子:

class A(object): 
    def __method(self): 
        print "I'm a method in A" 
    def method(self): 
        self.__method() a = A() a.method()

输出是这样的:

$ python example.py 
I'm a method in A

很好,出现了预计的结果。

我们给A添加一个子类,并重新实现一个__method:

class B(A): 
    def __method(self): 
        print "I'm a method in B" 

b = B() 
b.method()

现在,结果是这样的:

$ python example.py
I'm a method in A

就像我们看到的一样,B.method()不能调用B.__method的方法。实际上,它是”__”两个下划线的功能的正常显示。

因此,在我们创建一个以”__”两个下划线开始的方法时,这意味着这个方法不能被重写,它只允许在该类的内部中使用。

在Python中如是做的?很简单,它只是把方法重命名了,如下: 

a = A()
a._A__method()  # never use this!! please!
$ python example.py 
I'm a method in A

如果你试图调用a.__method,它还是无法运行的,就如上面所说,只可以在类的内部调用__method。

"__xx__"前后各双下划线

当你看到"__this__"的时,就知道不要调用它。为什么?因为它的意思是它是用于Python调用的,如下:

>>> name = "igor" 
>>> name.__len__() 4 
>>> len(name) 4 
>>> number = 10 
>>> number.__add__(20) 30 
>>> number + 20 30

“__xx__”经常是操作符或本地函数调用的magic methods。在上面的例子中,提供了一种重写类的操作符的功能。

在特殊的情况下,它只是python调用的hook。例如,__init__()函数是当对象被创建初始化时调用的;__new__()是用来创建实例。

class CrazyNumber(object):
    def __init__(self, n): 
        self.n = n 
    def __add__(self, other): 
        return self.n - other 
    def __sub__(self, other): 
        return self.n + other 
    def __str__(self): 
        return str(self.n) 

num = CrazyNumber(10) 
print num # 10
print num + 5 # 5
print num - 20 # 30    

另一个例子

class Room(object):
    def __init__(self): 
        self.people = [] 
    def add(self, person): 
        self.people.append(person) 
    def __len__(self): 
        return len(self.people)
 
room = Room() 
room.add("Igor") 
print len(room) # 1

结论

  • 使用_one_underline来表示该方法或属性是私有的,不属于API;
  • 当创建一个用于python调用或一些特殊情况时,使用__two_underline__;
  • 使用__just_to_underlines,来避免子类的重写!