接口测试学习-Python第四课(文件操作及函数定义)
一、关于文件一些常用的方法
1、闪存
如果对文件进行写入操作后,文件内容没有更新,可能是因为读取写入都需要经过缓冲区,导致无法立即成功。此时可以用flush()方法,强制写入。
1 with open('ss','w',encoding='utf-8') as f: 2 f.write('string') 3 f.flush()
2、修改文件内容
修改文件内容有两种方法。
第一种方法是先读出原文件数据,进行修改后,清空原文件数据,再将修改后的内容写入原文件中。
1 f = open('zhanghao','a+',encoding='utf-8') 2 f.seek(0)
3 all_str = f.read()#先获取到原来文件的所有内容 4 new_str = all_str.replace('123456','17171717')#修改获取到的数据数据 5 f.seek(0)#调整指针位置 6 f.truncate()#清空原文件的所有内容 7 f.write(new_str)#将修改后的数据写入文件 8 f.close()
这样操作的缺点是,当原文件内容较大时,这样读出数据会影响运行速度甚至导致内存溢出。
第二种方法是读取原文件的每一行数据并进行修改,然后依次存入新文件中,再将原文件删除,最后修改新文件名称为原文件名称。这样就完成了“修改”文件内容。
1 import os#需要使用os模块 2 # 先将原文件的每一行修改并存入新文件,一行一行处理 3 with open('words',encoding='utf-8') as fr,open('.words.bak','w',encoding='utf-8') as fw: 4 for line in fr: 5 new_line = line.replace('花','flower') 6 fw.write(new_line) 7 # 先删除原文件,再将新文件名称修改来和原文件一致 8 os.remove('words') 9 os.rename('.words.bak','words')
另关于写入一般使用f.write()写入数据,数据应该是字符串模式,但是利用f.writelines()可以直接写入list,该方法在内部完成了对list的循环。
3、函数
在python中,有很多的内置函数,比如abs()就是取绝对值的函数。这类函数是因为已经提前定义过了,所以可以直接调用。
1 >>>abs(-100) 2 100
如果我们在程序中会重复使用一段代码,就可以将这段代码定义为函数,每次使用时直接调用即可。函数的定义需要使用def语句。
1 def say(name): 2 print('hello,%s'%name)
函数定义整体还包括函数名、括号、括号中的参数、冒号、缩进体中的函数体。函数体主要定义函数的作用,函数体中一般用return返回结果。说到函数的参数,就必须提到形参和实参,直接以下例解释。
1 def calc(a,b):#a,b是形参,在函数中的这两个参数也叫位置参数,为必填参数 2 res = a*b 3 print('%s*%s=%s'%(a,b,res)) 4 calc(7,8)#调用函数并传入参数,7和8就是实参
定义函数calc时,a和b就是代表函数calc中需要两个参数,这个就是形参;而后调用calc(7,8),传入的7和8就是实际参数,按照输入位置7默认传给a,8默认传给b。
calc中的函数,直接用print输出了函数计算结果。但是如果我们需要得到a*b的值进行下一步计算呢。这种时候,需要用return返回。在定义calc函数的函数体末尾,直接写入“return res”即可。这样调用函数后可以用一个变量来接收return的值。
return除了返回值外,还可以结束函数。
1 def lainxi(): 2 for i in range(5): 3 print(i) 4 if i == 3: 5 return#遇到return,函数结束 6 lainxi()
如上面的函数,在调用时,只会刷出0,1,2,3这四个数,因为当i == 3时,会自动结束函数,不会继续循环了。当函数体中没有return时,会默认返回None,比如下面的判断密码格式的函数。
1 import string 2 def check(pwd): 3 if len(pwd)>5 and len(pwd)<12: 4 if set(pwd)&set(string.ascii_letters) and set(pwd)&set(string.digits): 5 print('密码合法') 6 else: 7 print('密码不合法') 8 res = check('asd1234') 9 print(res)#函数体中没有return时,默认返回None
最后利用print输入res的值就是None。
4、常量和全局变量
常量就是一个默认不变的值,在python中一般用大写字母定义常量。如:
1 FILENAME = ‘test.txt’
变量是有使用范围的,如在函数体中定义的变量,就只能在函数体中使用,函数体外是不可调用的。而全局变量就是在整个文件中都能被使用的值。比如在下面的代码中:
1 name = '谢红' 2 def sayName(): 5 print('name1',name) 6 sayName() 7 print('name2',name)
一开始定义了name = '谢红',那么在函数体中会直接输出时会直接输出已定义的数据,所以上面的代码运行后结果为:
1 name1 谢红 2 name2 谢红
而如果在函数体中对name变量进行了新的赋值。
1 name = '谢红' 2 def sayName(): 3 name = '刘伟' 4 print('name1',name) 5 sayName() 6 print('name2',name)
那么函数体中的name变量就是“刘伟”了,但是函数体外的name变量则是“谢红”,所以上面的代码输出结果为:
1 name1 刘伟 2 name2 谢红
如果想要在函数内部修改name变量并影响整个文件,需要先定义name函数为global。
1 name = '谢红' 2 def sayName(): 3 global name#如果想在函数内部修改全局变量,就需要先声明一下name是全局变量 4 name = '刘伟' 5 print('name1',name) 6 sayName() 7 print('name2',name)
这样修改后,name就已经被定义为“刘伟”了,函数体外也同样被修改,所以上面代码的输出结果为:
1 name1 刘伟 2 name2 刘伟
全局变量最好不要轻易使用,缺点1是不安全,所有人都可以修改这个数据;缺点2是全局变量会一直占用内存。
5、json串文件和字典的相互转换
在 操作文件的过程中,有时文件内容可能是json串格式的,此时要如何处理呢?在python中有将json串格式文件转化为字典的方法,只需要导入json模块就可以应用了。首先是将json串文件转化为字典。转化方法也有两种,一种是json.loads(),一种是json.load()前者和后者的区别在于,前者必须先将文件内容读出来,然后对文件内容进行操作,后者可以直接对文件进行操作,内部自动完成读出操作。
1 import json 2 f = open('product.json',encoding='utf-8') 3 res = f.read() 4 product_dic = json.loads(res)#json.loads就是将json串变成python的数据类型字典,操作对象为字符串,需要先将文件中的内容读出来
1 import json 2 f = open('product.json',encoding='utf-8') 3 product_dic1 = json.load(f)#用json.load可以不用先将文件中的数据读出,直接对文件进行操作
以上两种方法得到的结果其实是一样的,根据实际情况选择方法使用即可。
可以将json格式的文件转换为字典格式,自然也能反过来转换。同样,将字典格式的文件转化为json串格式也有两种方法,分别是json.dumps()和json.dump()。前后两者的区别是第一种方法只用于转换字典格式的文件为json串格式,必须参数只有字典;而第二种方法必须参数除了字典格式的数据,还包括即将写入json串的文件,可以做到转换写入一步到位。
1 import json 2 f = open('product.json',encoding='utf-8') 3 d = { 4 'zll':{ 5 'addr':'北京', 6 'age':28 7 }, 8 'ljj':{ 9 'addr':'北京', 10 'age':21 11 } 12 } 13 fw = open('user_info.json','w',encoding='utf-8') 14 dic_json = json.dumps(d,ensure_ascii=False,indent=4)#json.dumps方法将字典转化为json串#就这么直接转换的话,格式不好看,需要处理一下 15 fw.write(dic_json)#用json.dumps()方法需要再使用write()方法写入数据 16 dic_json1 = json.dump(d,fw,ensure_ascii=False,indent=10)#用json.dump,入参中包括fw,这样将字典转换后会自动写入文件中,不需要再fw.write()
在代码中ensure_ascii = False和indent = 4或者indent = 10都是优化格式的,可以直接带上。
6、函数不固定参数
在讲定义函数的时候提到了形参和实参,其中calc()函数中的a,b两个参数都是固定参数,也就是必须传入两个参数,不论是不传、少传或者多传都会报错,因为定义时就写明了calc()只有两个固定参数。有时候的确需要传入不确定个数的参数,此时就要用到不固定参数。
1 def syz(*args):#*args表示参数组,将所有参数放进一个元祖中 2 print(args) 3 syz('niuhanyang','233','122',111)
在定义函数syz()时,参数前面加入了一个'*'号,这样的参数可以接受多个传入参数,并将所有传入的参数放入一个tuple中。
1 def sys2(**kwargs):#**kwargs是关键字参数,将传入的参数放入字典中 2 print(kwargs) 3 sys2(name = 'nhy',age = 38) 4 sys2(name = 'nhy',age = 38,addr = '回龙观') 5 sys2(name = 'nhy',age = 38,addr = '回龙观',home = '河南')
而定义函数sys()时,参数前面加了两个‘*’号,这样的参数也可以接受多个传入参数,并将传入的参数放入一个字典中,所以传入的参数必须是key和value对应的数据。
除了这两种不固定参数外,还有默认参数,即不传入数据则为默认值,传入数据则使用传入的值。在函数中,参数的排序也是很重要的。必须以固定参数、默认参数、不固定参数的顺序排序,定义和传入时都要这样,否则函数不知道究竟传入的参数是赋给哪一个形参的,会报错。
7、递归
递归其实就是在函数内部调用函数本身。具体见代码。
def test1(): num = int(input('number:')) if num%2 == 0:#判断输入的数字是否为偶数 return True#如果为偶数程序退出,返回TRUE print('非偶数重新输入') return test1()#不是偶数的话调用函数本身重新输入
在函数test1()的函数体中,调用了test1()函数本身,这就是递归函数。其实递归函数也是在循环,而且递归最多999次,否则会溢出,所以并不是一个非常值得使用的方法。
8、模块
在平常的代码编写中,需要用到各种已封装好的方法,有时候这些方法并不是python的内置函数,需要导入模块才能使用。模块其实也是一个python文件,有的模块可以直接调用,有的模块需要先下载安装才能导入使用。模块分为三个部分。
(1)、标准模块、标准包
python自带的这些模块,就是标准模块,直接import就可以使用,比如random、string、datetime等。
(2)第三方模块,别人写好的,需要安装才能使用
安装方法有:①PIP install mokuaiming;②手动安装,首先进入https://pypi.python.org/pypi/redis#downloads下载安装包,如果安装包以.whl结尾,cmd命令首先进入文件存储目录,再运行pip install redis-2.10.6-py2.py3-none-any.whl;如果安装包是tar.gz的安装包,先解压,进入解压出来的文件,在当前窗口运行cmd,再运行命令python setup.py install。
(3)自己写的python文件
9、内置函数
python自带的一些函数,可以直接拿过来用
1 print(all([1,2,3,4]))#判断可迭代的对象里面的值是否都为真 2 print(any([0,1,2,3,4]))#判断可迭代的对象里面的值是否有一个为真 3 print(bin(10))#十进制转二进制 4 print(bool('s'))#把一个对象转换成布尔类型 5 print(chr(10))#打印数字对应的ascii 6 print(ord('b'))#打印字符串对应的ascii码 7 print(dir(1))#打印传入对象的可调用方法 8 print(exec('def a():pass'))#执行python代码 9 print(filter(lambda x:x>5,[12,3,12,2,1,2,35]))#把后面的迭代对象根据前面的方法筛选 10 print(map(lambda x:x>5,[1,2,3,4,5,6])) 11 print(max(111,12))#取最大值 12 print(round(11.11,2))#取几位小数 13 print(sorted([2,31,34,6,1,23,4]))#排序
1 # zip函数 2 l1 = ['a', 'b', 'c'] 3 l2 = [1, 2, 3] 4 l3 = ['x', 'y', 'z'] 5 # for a, b in l1, l2: 6 # print(a, b) # 直接这样遍历是执行不了的 7 for a, b, c in zip(l1, l2, l3): # zip可以将这几个list合并到一起,变成多维数组 8 print(a, b, c) 9 # 如果l1,l2,l3的长度不一样的话,会以长度最短的那个为标准
1 # map 用于循环调用函数 2 def my(num): 3 return str(num) 4 lis = [1, 2, 3, 4, 5, 6, 7, 8, 9] 5 # new_lis = [] 6 # for i in lis: 7 # new_lis.append(my(i)) 8 # print(new_lis) 9 res = list(map(my, lis)) # 结果必须强制转换为list才好print 10 print(res)
1 # filter 也是用于循环调用函数 2 def even(num): 3 if num % 2 == 0: 4 return True 5 else: 6 return False 7 lis = [1, 2, 3, 4, 5, 6, 7, 8, 9] 8 res = list(filter(even, lis)) # filter是过滤器,只保留返回为真的数据 9 res1 = list(map(even, lis)) # map就循环函数,函数返回什么就保存什么 10 print('filter的结果:', res) 11 print('map的结果:', res1)
1 filter的结果: [2, 4, 6, 8] 2 map的结果: [False, True, False, True, False, True, False, True, False]
10、返回多个值的函数
函数是多种多样的,很多时候并不是只有一个返回值,那么当函数的返回值是多个时如何处理呢。比如下面的函数,返回值有三个,分别是num1,num2,num3。
1 def some(): 2 num1 = 10 3 num2 = 20 4 num3 = 30 5 return num1,num2,num3
像这样的函数,如果用一个数据去接收返回值,比如res = some(),那么会将返回值储存在元祖中返回,所以res现在接收的是一个元祖,其中包含num1-3三个数据。如果想要分开接收三个数据。则使用res1,res2,res3 = some()即可。此时res1接收的就是return后的第一个返回值也就是num1,依次推类res2 = num2,res3 = num3。
11、匿名函数
这个不是特别了解,函数结构如下:
1 res = lambda x:x*x lambda函数中,冒号后面的是返回值 2 print(res(2))
x就是函数参数,而冒号后面的为计算公式也是返回的值。这个函数有时候会用于为字典排序。下面这段函数就是根据字典的value进行升序排序,当然res所接受的返回结果并不是一个字典了。其实所谓的排序也不是针对字典而是针对d.items(),所以字典是无序的这个准则并没有被推翻。
1 d = {'a': 8, 'b': 5, 'c': 3} 2 res = sorted(d.items(), key=lambda x: x[1])
12、三元运算符
三元运算符其实就是简洁版的if else判断。也是会用到if else命令。
1 a = 4 2 b = 5 3 c = a if a>b else b#利用三元运算符判断a,b大小并选择某个变量赋值给c 4 if a>b:#利用if else进行判断,和三元运算符判断结果一致 5 c = a 6 else: 7 c = b