Archives: 2022年12月11日

流畅的python 阅读笔记

第一部分:Python数据模型

个人总结:python数据模型或者python风格其实就是自定义一些基础的方法。比如__getitem__,__len__等,然后可以通过len(对象)而不是对象.len()的方法来调用。方便之处在于所有对象都可以使用这些统一的、基础的、规范的方法。自定义的类也可以参照定义编写这些方法。使得python风格的代码具有更好的兼容性、统一性。


第二部分:数据结构

个人总结:数组、元组、字典、集合、字节和文本等数据结构的特性及方法的使用。

容器序列和扁平序列、不可变序列、可变序列、之间的关系和具体的类
容器序列:list、tuple和collections.deque这些序列能存放不同类型的数据
扁平序列:str、bytes、bytearray、memoryview和array.array,这类序列只能容纳一种类型。存放的是值而不是引用。
不可变序列:tuple、str和bytes。
可变序列:list、bytearray、array.array、collections.deque和memoryview。可变序列继承自不可变序列。

数组:
基本方法的使用
列表推导式:[ord(s) for s in symbols if ord(s) > 127]
生成器表达式:(ord(symbol) for symbol in symbols) 区别在于一个式列表、一个是元组,生成器表达式效率更高

元组:
元组拆包、具名元组(nametuple)给元组各个值命名

基本方法的使用:
切片、切片赋值(右侧需为可迭代对象)、序列+* 、list.sort()转换直接排序(就地改动返回None) sorted(li)可以被赋值

避免数组的过度使用:
array用于只包含数值的列表 collections.deque类(双向队列)

字典:
可散列值:id始终唯一,具有__hash__、__eq__方法。
hash的作用:同样的值得到的hash值唯一
字典推导式:{country: code for code, country in DIAL_CODES}


py文件-转-exe文件

https://www.cnblogs.com/Dota-wiki/p/7851493.html

安装包:pyinstaller

命令行:

pyinstaller --onefile --onedir --paths="F:\selfgit\HelloWorld" --specpath="F:\exe" --nowindowed --distpath="F:\exe" mousecontrol.py

基本语法:
pyinstaller options myscript.py
常用的可选参数如下:
–onefile 将结果打包成一个可执行文件
–onedir 将所有结果打包到一个文件夹中,该文件夹包括一个可执行文件和可执行文件执行时需要的依赖文件(默认)
–paths=DIR 设置导入路径
–distpath=DIR 设置将打包的结果文件放置的路径
–specpath=DIR 设置将spec文件放置的路径
–windowed 使用windows子系统执行,不会打开命令行(只对windows有效)
–nowindowed 使用控制台子系统执行(默认)(只对windows有效)
–icon=<FILE.ICO> 将file.ico添加为可执行文件的资源(只对windows有效)

如pyinstaller –paths=”D:\Queena” guess_exe.py


自创工具类-必填为空校验

入参为:字典模板及字典是否必填
出参为:字典必填为空列表

class Data:
    
    # 字典模板    -可以为列表 也可以是字典
    order_data_model = [{"oreder": "123", "oreder2": "123", "oreder3": "123",
                         "items": [{"item1": "123", "item2": "123", "item3": "123"}], "oreder4": "123","dd": [{"ddd": [{"dddd": "123", }]}]}]
    # 字典值是否必填
    order_data_define = [
        {"oreder": True, "oreder2": False, "oreder3": True, "items": [{"item1": True, "item2": False, "item3": True}],
         "oreder4": True,"dd": [{"ddd": [{"dddd": True}]}]}]
from data import Data
import copy


class CheckData:
    def __init__(self, data_model, data_define):
        # 最初全数据
        self.data_model = data_model
        self.data_define = data_define
        # 目前层级数据
        self.item_model = {}
        self.item_define = {}
        # 必填为空的数据列表
        self.data_list = []

    # 数据最外层为list时,进入下一层
    def deeper(self):
        self.data_model = self.data_model[0]
        self.data_define = self.data_define[0]

    # 数据字典值为list时,进入下一层
    def deeper_key(self, key):
        self.item_model = self.data_model[key][0]
        self.item_define = self.data_define[key][0]

    # 返回必填字段为空的列表
    def get_null_data_list(self):
        # 如果数据为列表-下一层-并返回列表集合[[{}],[{}]]-再给[{},{}]加一层[]:[[{}],[{}]]
        if type(self.data_define) == list:
            self.deeper()
            self.__get_null_data_list()
            list_all = []
            for data in self.data_list:
                list_all.append([data])
            self.data_list = list_all
            return self.data_list
        else:
            # 如果数据模板最外层为字典-直接返回字典列表[{},{}]
            self.__get_null_data_list()
            return self.data_list

    def __get_null_data_list(self):
        # 用于返回必填字段为空的列表
        # 存储目前置空数据
        data = copy.deepcopy(self.data_model)
        # 循环模板字典 拿到键值对-模板保持不变
        for key, value in self.data_model.items():
            # 如果字典值不是列表
            if type(self.data_define[key]) != list:
                # 如果定义数据不是非必填-才置空
                if self.data_define[key] != False:
                    data[key] = ""
                    # 加入到返回列表
                    self.data_list.append(copy.deepcopy(data))
                    # 重置数据
                    data[key] = value
            else:
                # 继续读列表
                data[key] = ""
                # 加入到返回列表
                self.data_list.append(copy.deepcopy(data))
                # 重置数据
                data[key] = value
                # 下一层
                self.deeper_key(key)
                # 循环字典 拿到键值对
                for key2, value2 in self.item_model.items():
                    # 如果字典值不是列表
                    if type(self.item_define) != list:
                        if self.item_define[key2] != False:
                            data[key][0][key2] = ""
                            self.data_list.append(copy.deepcopy(data))
                            data[key][0][key2] = value2
                    else:
                        # 不读第四层
                        pass


if __name__ == '__main__':
    cd = CheckData(Data.order_data_model, Data.order_data_define).get_null_data_list()
    print(cd)
    for a in cd:
        print(a)

打印结果

[[{'oreder': '', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}], [{'oreder': '123', 'oreder2': '123', 'oreder3': '', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}], [{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': '', 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}], [{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}], [{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': ''}], 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}], [{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '', 'dd': [{'ddd': [{'dddd': '123'}]}]}], [{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': ''}], [{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': [{'ddd': ''}]}]]
[{'oreder': '', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}]
[{'oreder': '123', 'oreder2': '123', 'oreder3': '', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}]
[{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': '', 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}]
[{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}]
[{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': ''}], 'oreder4': '123', 'dd': [{'ddd': [{'dddd': '123'}]}]}]
[{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '', 'dd': [{'ddd': [{'dddd': '123'}]}]}]
[{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': ''}]
[{'oreder': '123', 'oreder2': '123', 'oreder3': '123', 'items': [{'item1': '123', 'item2': '123', 'item3': '123'}], 'oreder4': '123', 'dd': [{'ddd': ''}]}]

结合数据驱动、对每个数据都进行一次验证

import pytest
from data import Data
from checkdata import CheckData


class TestCase:

    @pytest.mark.parametrize('data',CheckData(Data.order_data_model, Data.order_data_define).get_null_data_list())
    def test_case(self,data):
        # res = create_order(data)
        # assert res.text['success'] == False 
        assert data is None


if __name__ == '__main__':
    pytest.main()

深浅拷贝-拷贝大坑

背景:写了一段代码,在运行的时候给添加数据时,打印出来是对的,但是随着运行,返回的却是一串一模一样的字符串

原因:

data_list=[] 
data ={}  
for xxx: 
     data_list.append(data)  

data_list 每次添加的是地址,随着data的变化,之前添加的data地址也是一样的,所以添加的到了最后,是一列一模一样的data。而没有存储每次变化的data。

解决:深拷贝 import copy aa.deepcopy()

区分:拷贝、浅拷贝、深拷贝

p1、python的数据类型

基本的数据类型:数字型、字符串、元祖、列表、字典、集合
不可变数据类型:数字型、字符串、元祖
可变数据类型:列表、字典、集合

基本的数据类型:数字型、字符串、元祖、列表、字典、集合
不可变数据类型:数字型、字符串、元祖
可变数据类型:列表、字典、集合

p2、python深浅拷贝

http://t.zoukankan.com/luodaoqi-p-11323806.html

一、= 赋值  

如果l2是l1的拷贝对象,则l1内部任何数据类型的元素变化,则l2内部的元素也会跟着改变,因为可变类型值变ID不变.

二、浅拷贝 copy() 

如果l2是l1的浅拷贝对像,则l1内的不可变元素发生改变,l2不变;如果l1内的元素发生了改变,则l2会跟着改变。
但l2的可变元素改变,l1也会改变。

三、深拷贝 copy.deepcopy(data) -import copy
如果l2是l1深拷贝对像,则l1内的不可变元素发生了改变,l2不变;如果l1内的可变元素发生了改变,l2也不会变,即l2永远不会因为l1的改变而改变。

四、总结
赋值:同步所有元素  copy:只同步所有可变元素  deepcopy:所有元素都不同步

python自动化学习-跟着课程走

Python官方文档:https://docs.python.org/zh-cn/3/

-0-

python常见内置函数

len()  #长度
None #空
type() #查看数据类型
format()#格式化
replace()-替换
find()-查找
join(())-拼接
split()-分割
lower()-转小写
upper()-转大写

bool() id() float() int()len() list() str() type() tuple() 
"{}:{}".format('jack', 300)
res=s.replace('123','999',2)
res=s.find('23',4,10) #从第五开始找
res = ' '.join((s1, s2, s3))
res.strip('+')

-1-

一、安装python、pycharm
二、新建项目
三、pycharm设置集合
1.pycharm设置作者信息
2.设置项目解释器-setting-project-齿轮号

四、python package 和普通文件夹区别
python package有init文件
init文件作用:
1.标识该目录是一个python的模块包(module package)
2.简化模块导入操作
-包在被导入时会执行init文件-可放初始化代码

五、输入、输出、数值类型、数值运算符、比较运算符、转义符

#输入-输出
a = input('输出dotcpp的网站:')
b = float(input('我认为适宜的温度:'))
index = list(map(int,input().split()))
#这种方式可以输入任意个int型的数字,在这里采用列表来存储。输入时空格分开
print(index)
print(index[0])

#数值类型
int float bool
c = 9
d =9.9
e = True

数值运算符
+ - * /      //取整  %取余

比较运算符
> < >= <=  == != is is not

转义符 \
print("children\'s class") #children's class

制表符 \t

六、字符串格式化-format

插入字符、格式小数位数、百分位数、长度、填充、不支持反向索引

占位符:%s -任意类型 %d-整数 %f -浮点类型
print("您的体重是:%d" %(33))

#format两种方式,可标序号,可不标序号,从0开始
#format:
desc = "您的姓名是:{0}。您的体重是:{1}。"
name = 'Jack'
weight = 200
print(desc.format(name, weight))
#倒序
desc = "您的姓名是:{1}。您的体重是:{0}。"
#不填序号
print("您的姓名是:{}。您的体重是:{}。".format('jack', 300))

#格式化小数位数-保留两位小数-得到1.75
desc2 = "您的身高是{:.2f}".format(1.755) 

#格式化百分数显示-保留两位小数-得到33%
print('通过率为{:.2f}'.format(1/3))

#格式化字符串中的长度
{:<20}占20字符左对齐
{:>20}占20字符右对齐
{:^20}占20字符居中对齐
{:*>20}占20字符右对齐-*号填充空白

字符串的f表达式-3.7版本后
desc = F'您的姓名是:{name}。您的体重是:{weight}。'

七、索引、切片-字符串、列表、元组

[:0] [0:1] [0:5:2] 
#索引-找单个[:3]-第4个-d  
#结果=数据[索引值]
s1 = ‘abcde’
res = s1[2]   #正向c-从0开始
res = s1[-1] #反向e-从-1开始

循环打印单个字符串
s1 = 'abcde' 
for i in range(len(s1)):
    print(s1[i])
    i = i+1

#切片-找多个[1:3]-取头不取尾-取第2、3位-bc
#结果=数据[start:end]

#切片2-跨步取值-[0:5:2] -ab cd e -跨两步,得ace

列表:[] 
元组:() 不可修改

八、替换-replace

s ='123rrr123ttt123'
res=s.replace('123','999',2)
#参数1被参数2替换,替换参数3个,无参数3时全替换
#只能替换字符串型,不能替换数值型

九、查找-find-返回第一个位置索引

s ='123rrr123ttt123'
res=s.find('23')
res=s.find('23',4,10) #从第五开始找

十、字符串拼接-join(())

s1 = 'Kacy'
s2 = 'Mick'
s3 = 'Judy'
res = ' '.join((s1, s2, s3))
res = '@love@'.join([s1, s2, s3])
print(res)

#Kacy@love@Mick@love@Judy
#字符串的join()方法只能带1个入参,多个字符串用list或元组

十一、字符串分割-split()

s5='@Kacy@love@Mick@love@Judy@'
res = s5.split('@')
#得到的是['','Kacy', 'love', 'Mick', 'love', 'Judy', '']
#注意有首尾有空

十二、字符串去除前后字符-strip()

#去除前后空白字符
print(res.strip())
#去除前后特定字符
print(res.strip('+'))
#去除左边字符
res.lstrip() 
#去除右边字符
res.rstrip()

十三、列表的增删改查

li = [123,12]
#添加数据
li.append(111) 结尾处添加元素
li.insert(0,'第一个‘)指定位置添加元素
li.insert(-1,'倒数第二个’)插到倒数第二个 -0,-1
li.extand([123,2232]) 两个列表合并

#删除数据
li.remove(12)删除指定第一个数据
li.pop() 删除指定索引
li.clear() 清空所有数据

#修改数据
li[0]='111'

# 查找数据
li[0] 索引取值
li.index(‘111’)查找元素对应索引

# 字符串查找数据,参数只能是str
‘asd1aas’.index('1')
‘asd1aas’.find('1')

# 统计某元素个数
li.count(1)

# 复制:copy()只复制值

# 列表反序
li.reverse()
li[::-1] 切片反序
 
#Ctrl查看,如果返回不指向None,使用时需要再建个变量给来接收返回数。如果指向None,直接使用原变量即可

# 将字符串转换成列表-内置函数eval()-去掉最外层格式
li = eval('[11,22]')

# 排序 sort()
li.sort() 升序
li.sort(reverse=True) 降序

#切片:[1,2,3,4,5,6,7,8,9]
[2::3]从第三个切到最后,步长为3

十四、元组()

li=(123,'122')
元组特性:与列表接近、数据不可变

十五、字典{}

字典{}:有键值对,列表和元组必须知道序号才可以提取值
li = {'age':18,'name':'jack'}  键不能重复,键只能是数值、字符串、元组等不可变类型数据。多用字符串。

# 增
li = {}
li['sex'] = 'woman'
li.update({'sex':'woman'}) 添加

# 改
li['sex'] = 'woman'
li.update({'sex':'woman'}) 有则改、无则增

# 删
li.pop('name') 删除对应键的值
li.popitem() 删除最后一个键,并以元组形式返回,实际type还是dict
li.clear() 清空

# 查
li['name']
dict.keys()     返回所有键
dict.values()  返回所有值
通常转成列表或元组使用
dict.items() 以元组形式返回键和值
与for循环结合
创建字典:{}  dict()
eval()      字符串转字典

十六、集合{}

set={11,22,33}
set = set() #定义空集合
集合内没有重复元素
去除列表内重复元素:list-set-list\
li = [1,22,22,33]
a = list(set(li))

十七、随机数

import random
res = random.randint(1,999) 随机整数
res = random.random() 随机小数,0-1之间
res = random.uniform(10,20) 10-20之间的小数
li = [1,33,55]
res = random.choice(li) 在列表中挑选一个随机值


十八、条件判断

if elif else
逻辑运算:and or not
成员运算:in ,not in
身份运算:is ,is not 判断内存id
赋值运算:=  ,+= ,-= ,*= ,/= ,%=

十九、循环

while True:break continue
for i in [1,2,3]/dic/dic.value/dic.items():

元组拆分-遍历键和值:
for k,v in dic.items():

列表循环
li2 = ['www{}'.format(i) for i in range(10)]
li = [ i+1 for i in (11,22,33)]

二十、range内置函数

生成指定序列
li = list(range(100))
li = list(range(9,99,2))

二十二、函数

1、必备参数 def func(a,b)
2、默认参数 def func(a=11,b=22)
3、不定长参数 def func(*args)
      *args只能位置传参 
      **kwargs 只能使用关键字传参
4、函数拆包-*可以对列表或元组拆包,**可以对字典拆包
      func(*[11,22,33])
      for i,v in enumerate li:
               print(i,v)
5、lambda 函数
      func = lambda x: 2*x
6、min(li) max(li) sum(li)
7、eval(‘1+22’)识别表达式
8、enumerate 获取列表中的数据及索引
9、zip 数据聚合打包
     title =['name','sex']
     data =[‘张三’,‘女’]
     res=zip(title,data)
10、filter 过滤函数
     res= filter(lambda x:x>80,stu)
11、自定义排序规则sort(key=)
         li.sort(key=func)
         li.sort(key=lambda x:x[2])

二十三、文件操作和模块导入

f = open(file='biji.txt',mode='r',encoding='utf-8')
mode =r/w/a(追加、自动创建文件) rb wb ab 以二进制读取 +可读可写
f.read()
f.readline()
f.readlines() ->list
f.write()
f.close()

with open(file ='',mode='rb') as f:  自动关闭
        res=f.read()

二十四、相对路径、绝对路径

1、绝对路径  C:\Users\A80\Desktop\需求\WMS
2、相对路径 .当前层级 .. 上个层级
3、文件路径转义 file=r‘C:\Users\A80\Desktop\需求\WMS’ 加r关闭转义

二十五、模块和包

模块:python文件
包:python文件夹 有init文件
导入模块
from src/ss import SS
导入包
import requests
from requests import session

只直接运行此文件时成立
if __name__ == "__main__":

import sys (所有可用包)

二十六、异常

自定义异常类型:
class TooLongExceptin(Exception):
    "this is user's Exception for check the length of name "
    def __init__(self,leng):
        self.leng = leng
    def __str__(self):
        return "姓名长度是"+str(self.leng)+",超过长度了"


try:
    name = "1111"
    print("1")
    if len(name) > 2:
        raise TooLongExceptin(leng=len(name))

        raise ValueError("只能小于2")
except Exception as e:
    print(e)
else:
    print("没报错")
finally:
    print("还是要执行")

常用异常类型:
https://blog.csdn.net/csdnbian/article/details/113447496

二十七、类和对象

1、属性:
     类属性(共有)、类名.属性名=属性值
     对象/实例(独有)属性 对象.属性名=属性值
class Cat:
    leg= 4 类属性

cat = Cat()
cat.name = 55 实例属性

2、方法 
实例方法:self代表对象本身,没有self,无法使用对象调用方法,只能使用类调用
class Cat:
    def func(self): 
           print(1)
类方法 
@classmethod
def func(cls,name=‘’): cls代表类本身,可以类调用,也可以对象调用 里面不要用self.name实例属性

静态方法
@staticmehod
def func()内部不使用类属性和类方法,也不使用对象属性和时

初始化方法 def __init__(self,name='',age=''): 通过类创建对象时自动调用

二十八、继承和属性动态操作

1、私有属性:__attr只能在类内使用、  _attr表示私有,实际类外部能调用
2、私有方法:def  __attr(),def _attr()
3、继承 
     重写:同名方法,优先子类、私有属性不继承 
     拓展父类:在子类调用父类方法super.func(),
class Father:
       def __init__(self,name):
             self.name=name
class Child(Father):
       def __init__(self,name,price):
             Father.__init__(self,name)  或者 super().__init__()
             self.price=price
4、动态属性 内置方法 用于属性是字符串时 如 "age"="18"
     MyClass.__dict__  显示类所有相关属性
     设置属性setattr(MyClass,key,value) 获取getattr(MyClass,key) 
     删除delattr(MyClass,key) 判断属性是否存在hasattr(MyClass,key)
     例:把字典中的键值对设置为类属性和属性值
            data = {"name":"muse',"age":"18"}
            for k,v in data.items():
                  set(MyClass,k,v)
5、常用关键字
      del li[3]     del dic['b']

二十九、unittest的使用

1、测试用例类必须继承unittest.TestCase
class TestLogin(unittest.TestCase):
2、test开头方法为测试用例、使用实例方法
3、编写测试用例
      1)准备用例数据
      2)调用被测功能函数
      3)断言
4、编写主运行文件 run.py
import unittest
# 创建套件
suite = unittest.TestSuite()
# 创建用例加载器
load = unittest.TestLoader()
# 加载用例套件
suite.addTest(load.discover(r'文件路径'))
~~替代
suite = unittest.defaultTestLoader.discover(r'F:\file\src\news')

# 创建测试用例运行程序
runner =unittest.TextTestRunner()

# 运行测试用例
runner.run(suite)


5、测试用例生成报告 run_report.py pip3 install unittestreport
https://pypi.org/  官方查找拓展包及其使用方法

6、TestFixture 前置方法、后置方法
def setUp(self)   def tearDown(self) 方法级别,每个测试方法都运行
@classmethod 类级别
def setUpClass(cls)   def tearDownClass(cls)

7、断言方法
self.assertEqual(a,b)
self.assertIn('成功','成功了')
assert a ==b

8、测试用例路径
 # file = 'test_jtyoui.py'
 # case_path = os.path.join(os.getcwd(), "testsuite")
 # case_path = os.path.join(os.path.dirname(os.path.abspath(file)))
suite = unittest.defaultTestLoader.discover(case_path)

三十、ddt的使用和excel的数据读取

# ddt使用步骤
# 1.测试类前使用@ddt
# 2.在测试方法前使用@list_ddt(测试数据)
# 3.在测试方法中定义一个参数,用于接受用例数据|
# install 
import unittest
from ddt import ddt,list_data
#from unittestreport import import ddt,list_data

@ddt
class TestMusen(unittest.TestCase):
       @list_data([1,2,3])
        def test(self, item):
               print(item)
# excel的使用
# Excel使用xlsx格式,解析成三个对象,工作簿workbook、表单Sheet、单元格Cell

import openpyxl

workbook = openpyxl.load_workbook('test1.xlsx')
print(workbook.sheetnames)
sh = workbook['Sheet1']
print(sh.cell(row=3,column=3).value)

#按行读取 rows  按列读取 column
res = list(sh.rows)   
for i in res:
   print(i,i.value)

# 转为字典格式
res = list(sh.rows)
title = [i.value for i in res[0]]

cases = []
for item in res[1:]:
    data = [i.value for i in item]
    dic = dict(zip(title,data))
    print(dic)
    cases.append(dic)

@ddt
class TestMusen(unittset.TestCase)
       @list_data(cases)
        def test(self, item):
               expected = eval(item['espected'])
               params = eval(item['data'])
               res = func(**params)
               self.assertEqual(expectet,res)




# 结果
{'case':'1','data':'2','expected:'3'} 
{'case':'1','data':'2','expected:'3'} 
# 封装Excel

import openpyxl

class HandleExcel:
    def __init__(self,filename,sheetname):
        self.filename = filename
        self.sheetname = sheetname
        self.workbook = openpyxl.load_workbook(filename)
        self.sheet = self.workbook[sheetname]

    def read_data(self):
        res = list(self.sheet.rows)
        title = [i.value for i in res[0]]
        cases = []
        for item in res[1:]:
            data = [i.value for i in item]
            dic = dict(zip(title, data))
            cases.append(dic)
        return cases

    def write_data(self,row_no:int,colunm_no:int,value):
        self.sheet.cell(row=row_no,column=colunm_no,value=value)
        self.workbook.save(filename=self.filename)


@ddt
class TestMusen(unittest.TestCase):
    excel = HandleExcel('仓库作业-入库.xlsx', 'Sheet1')
    cases = excel.read_data()
    @list_data(cases)
    def test(self, item):
        expected = eval(item['expected'])
        params = eval(item['params'])
        res = func(**params)
        self.assertEqual(True, res)
#run.py
import unittest
import os

if __name__ == '__main__':
    # file = 'test_jtyoui.py'
    # case_path = os.path.join(os.getcwd(), "testsuite")
    # case_path = os.path.join(os.path.dirname(os.path.abspath(file)))
    # print(case_path)
    suite = unittest.defaultTestLoader.discover(r'F:\file\src\news')

    # 创建测试用例运行程序
    runner = unittest.TextTestRunner()

    # 运行测试用例
    runner.run(suite)

三十一、日志输出

# -*- coding: utf-8 -*-
"""
@Time : 2022/10/28 17:34
@Auth : dengyunman
@File :log.py

"""
import logging

def create_log(name='mylog',level="DEBUG",filename='log,log',sh_level='DEBUG',fh_level='DEBUG'):
    # 1、创建日志收集器
    log = logging.getLogger(name)
    # 2、设置日志收集器的收集等级
    log.setLevel(level)
    # 3、设置输出渠道
    # 3.1 输出到文件
    fh = logging.FileHandler(filename,encoding='utf-8')
    fh.setLevel(fh_level)
    log.addHandler(fh)
    # 3.2 输出到控制台
    sh = logging.StreamHandler()
    sh.setLevel(sh_level)
    log.addHandler(sh)

    # 4、设置输出格式
    log_format = logging.Formatter('%(asctime)s--%(filename)s-%(levelno)s:%(message)s')
    sh.setFormatter(log_format)
    fh.setFormatter(log_format)

    return log

mylog = create_log()


log = create_log()
log.debug("---debug---")
log.info("---info---")
log.warning("---warning---")
log.error("---error---")
log.critical("---critical---")
test.py

from log import mylog
mylog.error("---error---")

三十二、配置文件

ini文件格式: .ini  配置工具ini -settings -plugins -ini搜索安装

ini.ini

[logging]
level = DEBUG
file = log.log
sh_level = info

[mysql]
host = 127.0.0.1
port = 3306
username = root
password = root
name1 = deng
ini操作-读取 写入(少用)

from configparser import ConfigParser

# 1.创建配置文件解析器对象
cp = ConfigParser()
# 2.读取配置文件内容到配置文件解析器中
cp.read('ini.ini', encoding='utf-8')
# 3.读取配置内容
res = cp.get('logging', 'file')
print(res)
# cp.getint()
# cp.getboolean()
# cp.getfloat()

# 写入
cp.set('mysql','name1','deng')
cp.write(fp=open('ini.ini','w',encoding='utf-8'))
yaml文件格式 .yaml  配置工具yaml -settings -plugins -yaml搜索安装
pip3 insatll pyyaml

yaml.yaml

# yaml文件读取出来不是列表就是字典|最外层要统一为列表或统一为字典
# 字典 冒号后面必加空格
# 嵌套字典
case:
  data:
     user: jack
     pwd: pass
  expected:
     msg: 通过
# 列表
  list:
    - 1
    - 2
readyaml.py

import yaml

with open('yaml.yaml','r',encoding='utf-8') as f:
    res =yaml.load(f,Loader=yaml.Loader)

print(res,type(res))
print(res['case']['data'])

三十三、json数据

json.json

{
  "?mysql": "json文件字段必加双引号",
  "mysql": {
    "name": "jack",
    "pwd": "root",
    "gender": "女",
    "habits":["football","tennis"],
    "is18+":true,
    "rings": "None"
  }
}
readjson.py

import json
with open("json.json",'r',encoding='utf-8') as f:
    res =json.load(f)
print(res,type(res))
print(res['mysql']['is18+'],type(res['mysql']['is18+']))

# ---------------json转python---------------
# 将json字符串转为python数据
run = '{"aa":true,"bb":null}'
pydata = json.loads(run)

# ---------------python转json---------------
# 将python数据(列表或字典)转换为json -变为str类型
dic = {"mysql": {
    "name": "jack",
    "pwd": "root",
    "gender": "女",
    "habits":["football","tennis"],
    "is18+": True,
    "rings": None
  }}
# 变为str类型
json_res =json.dumps(dic)

三十四、综合应用

项目结构:
用例数据文件 --->datas
日志文件        ---->logs
测试报告        ---->reports
配置文件        ---->conf
测试用例模块 ---->cases
封装的公共模块---->common
      封装的读取、操作excel的模块
      封装的读取创建日志收集器的模块
      项目运行启动模块