一 文件操作
一 介绍
计算机系统分为:计算机硬件,操作系统,应用程序三部分。
我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,这就涉及到应用程序要操作硬件,众所周知,应用程序是无法直接操作硬件的,这就用到了操作系统。操作系统把复杂的硬件操作封装成简单的接口给用户/应用程序使用,其中文件就是操作系统提供给应用程序来操作硬盘虚拟概念,用户或应用程序通过操作文件,可以将自己的数据永久保存下来。
有了文件的概念,我们无需再去考虑操作硬盘的细节,只需要关注操作文件的流程:
#1. 打开文件,得到文件句柄并赋值给一个变量
#2. 通过句柄对文件进行操作
#3. 关闭文件
二 在python中
#1. 打开文件,得到文件句柄并赋值给一个变量
f=open('a.txt','r',encoding='utf-8') #默认打开模式就为r
#2. 通过句柄对文件进行操作
data=f.read()
#3. 关闭文件
f.close()
三 f=open('a.txt','r')的过程分析
#1、由应用程序向操作系统发起系统调用open(...)
#2、操作系统打开该文件,并返回一个文件句柄给应用程序
#3、应用程序将文件句柄赋值给变量
四 强调!!!
#强调第一点:
打开一个文件包含两部分资源:操作系统级打开的文件+应用程序的变量。在操作完毕一个文件时,必须把与该文件的这两部分资源一个不落地回收,回收方法为:
1、f.close() #回收操作系统级打开的文件
2、del f #回收应用程序级的变量
其中del f一定要发生在f.close()之后,否则就会导致操作系统打开的文件还没有关闭,白白占用资源,
而python自动的垃圾回收机制决定了我们无需考虑del f,这就要求我们,在操作完毕文件后,一定要记住f.close()
虽然我这么说,但是很多同学还是会很不要脸地忘记f.close(),对于这些不长脑子的同学,我们推荐傻瓜式操作方式:使用with关键字来帮我们管理上下文
with open('a.txt','w') as f: pass with open('a.txt','r') as read_f,open('b.txt','w') as write_f: data=read_f.read() write_f.write(data)
#强调第二点:
f=open(...)是由操作系统打开文件,那么如果我们没有为open指定编码,那么打开文件的默认编码很明显是操作系统说了算了,操作系统会用自己的默认编码去打开文件,在windows下是gbk,在linux下是utf-8。
这就用到了上节课讲的字符编码的知识:若要保证不乱码,文件以什么方式存的,就要以什么方式打开。
f=open('a.txt','r',encoding='utf-8')
五 python2中的file与open
#首先在python3中操作文件只有一种选择,那就是open()
#而在python2中则有两种方式:file()与open()
两者都能够打开文件,对文件进行操作,也具有相似的用法和参数,但是,这两种文件打开方式有本质的区别,file为文件类,用file()来打开文件,相当于这是在构造文件类,而用open()打开文件,是用python的内建函数来操作,我们一般使用open()打开文件进行操作,而用file当做一个类型,比如type(f) is file
总结:
#流程分析:
#1:向操作系统发起系统调用
#2:操作系统打开这个文件,返回一个文件句柄给应用程序
#3: 在应用程序中把文件句柄赋值给一个变量
#注意两点:
#1:打开一个文件对应两部分,一个Python级别的文件句柄,另外一个是操作系统打开的文件(默认
#打开文件的编码是以操作系统的编码为准的,除非open()指定encoding='编码' )
#2:当文件操作完毕后,应该回收两部分资源,
#del f:回收应用程序资源(python解释器自动的垃圾回收机制已经替我们做了)
#f.close:回收操作系统
二 打开文件的模式
文件句柄 = open('文件路径', '模式')
模式可以是以下方式以及他们之间的组合:
Character | Meaning |
‘r' | open for reading (default) |
‘w' | open for writing, truncating the file first |
‘a' | open for writing, appending to the end of the file if it exists |
‘b' | binary mode |
‘t' | text mode (default) |
‘+' | open a disk file for updating (reading and writing) |
‘U' | universal newline mode (for backwards compatibility; should not be used in new code |
#1. 打开文件的模式有(默认为文本模式): r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】 w,只写模式【不可读;不存在则创建;存在则清空内容】 a, 之追加写模式【不可读;不存在则创建;存在则只追加内容】 #2. 对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式) rb wb ab 注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码 #3. 了解部分 "+" 表示可以同时读写某个文件 r+, 读写【可读,可写】 w+,写读【可读,可写】 a+, 写读【可读,可写】 x, 只写模式【不可读;不存在则创建,存在则报错】 x+ ,写读【可读,可写】 xb
常用操作与了解操作
#r:默认的打开模式,只读,文件不存在则报错 # f=open('a.txt',encoding='utf-8') # print('===>',f.read()) #读所有,bytes---decode('utf-8')--->str # print('===>',f.read()) # print(f.readlines()) #读所有,结果放入列表中 # print(f.readline(),end='') #一次读一行 # print(f.readline(),end='') # print(f.readline(),end='') # print(f.readline(),end='') # f.close() #w:只写模式,如果文件存在则清空,如果文件不存在则新建 # f=open('b.txt',mode='w',encoding='utf-8') # f.write('11111\n') #unicode---encode-->bytes # f.write('2222\n') # f.write('333333\n') # l=['444\n','55555\n','66666\n'] # for line in l: # f.write(line) # f.writelines(['444\n','55555\n','66666\n']) # f.close() #a:追加写模式,如果文件存在则把光标移动到文件末尾,如果文件不存在则新建 # f=open('c.txt','a',encoding='utf-8') # f.write('333333\n') # f.write('444444\n') # f.writelines(['5555\n','666\n']) # # f.close() #遍历文件 # with open('a.txt',encoding='utf-8') as f: # #不推荐使用 # # lines=f.readlines() # # for line in lines: # # print(line,end='') # #推荐使用 # for line in f: # print(line,end='') #b:以bytes的形式去操作文件内容,不能指定编码 # with open('yuanhao.jpg',mode='rb') as f: # print(f.read().decode('utf-8')) # # with open('a.txt',mode='rb') as f: # data=f.read() # print(data.decode('utf-8')) # with open('d.txt',mode='wb') as f: # f.write('哈哈哈hello'.encode('utf-8')) with open('d.txt', mode='ab') as f: f.write('哈哈哈hello'.encode('utf-8')) #了解部分 # print(f.readable()) # print(f.writable())
三 操作文件的方法
#掌握 f.read() #读取所有内容,光标移动到文件末尾 f.readline() #读取一行内容,光标移动到第二行首部 f.readlines() #读取每一行内容,存放于列表中 f.write('1111\n222\n') #针对文本模式的写,需要自己写换行符 f.write('1111\n222\n'.encode('utf-8')) #针对b模式的写,需要自己写换行符 f.writelines(['333\n','444\n']) #文件模式 f.writelines([bytes('333\n',encoding='utf-8'),'444\n'.encode('utf-8')]) #b模式 #了解 f.readable() #文件是否可读 f.writable() #文件是否可读 f.closed #文件是否关闭 f.encoding #如果文件打开模式为b,则没有该属性 f.flush() #立刻将文件内容从内存刷到硬盘 f.name
#read
#以文本的模式读文件,n代表的是字符的个数
# with open('a.txt','r',encoding='utf-8') as f:
# data=f.read(3)
# print(data)
#以b的模式读文件,n代表的是字节的个数
# with open('a.txt','rb') as f:
# data=f.read(3)
# print(f.tell())
# print(data.decode('utf-8'))
#tell:告诉当前光标的位置
# with open('a.txt','r',encoding='utf-8') as f:
# data=f.read(3)
# print(f.tell())
# print(data)
#seek:移动光标
# with open('a.txt','r',encoding='utf-8') as f:
# data1=f.read()
# print('first: ',data1)
# print(f.tell())
# f.seek(0)
#
# data2 = f.read()
# print('second: ',data2)
#0:文件开头
# 1:当前位置
#2:文件末尾
# with open('a.txt','r',encoding='utf-8') as f:
# f.seek(3,0)
# print(f.read())
# with open('a.txt', 'rb',) as f:
# f.read(3)
# f.seek(3,1)
# # print(f.read())
# print(f.read().decode('utf-8'))
# with open('a.txt', 'rb',) as f:
# f.seek(-3,2)
# print(f.read())
#tail -f access.log
with open('access.log','a',encoding='utf-8') as f:
f.write('11111\n')
#truncate
# with open('a.txt','a',encoding='utf-8') as f:
# f.truncate(2)
练习,利用b模式,编写一个cp工具,要求如下:
1. 既可以拷贝文本又可以拷贝视频,图片等文件
2. 用户一旦参数错误,打印命令的正确使用方法,如usage: cp source_file target_file
提示:可以用import sys,然后用sys.argv获取脚本后面跟的参数
import sys if len(sys.argv) != 3: print('usage: cp source_file target_file') sys.exit() source_file,target_file=sys.argv[1],sys.argv[2] with open(source_file,'rb') as read_f,open(target_file,'wb') as write_f: for line in read_f: write_f.write(line)
四 文件内光标移动
一: read(3):
1. 文件打开方式为文本模式时,代表读取3个字符
2. 文件打开方式为b模式时,代表读取3个字节
二: 其余的文件内光标移动都是以字节为单位如seek,tell,truncate
注意:
1. seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的
2. truncate是截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下测试效果
import time with open('test.txt','rb') as f: f.seek(0,2) while True: line=f.readline() if line: print(line.decode('utf-8')) else: time.sleep(0.2)
五 文件的修改
文件的数据是存放于硬盘上的,因而只存在覆盖、不存在修改这么一说,我们平时看到的修改文件,都是模拟出来的效果,具体的说有两种实现方式:
方式一:将硬盘存放的该文件的内容全部加载到内存,在内存中是可以修改的,修改完毕后,再由内存覆盖到硬盘(word,vim,nodpad++等编辑器)
import os with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f: data=read_f.read() #全部读入内存,如果文件很大,会很卡 data=data.replace('alex','SB') #在内存中完成修改 write_f.write(data) #一次性写入新文件 os.remove('a.txt') os.rename('.a.txt.swap','a.txt')
方式二:将硬盘存放的该文件的内容一行一行地读入内存,修改完毕就写入新文件,最后用新文件覆盖源文件
import os with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f: for line in read_f: line=line.replace('alex','SB') write_f.write(line) os.remove('a.txt') os.rename('.a.txt.swap','a.txt')
练习题:
1. 文件a.txt内容:每一行内容分别为商品名字,价钱,个数,求出本次购物花费的总钱数 apple 10 3 tesla 100000 1 mac 3000 2 lenovo 30000 3 chicken 10 3 2. 修改文件内容,把文件中的alex都替换成SB
示例: 用文件当做数据库
with open('db.txt','r',encoding='utf-8') as f: for line in f: user_l=line.split(',') print(user_l[1],int(user_l[2]))
函数
一、函数
1.1、函数的用处
1. 组织结构混乱,可读性差
2. 代码冗余
3. 无法统一管理,维护难度极大
具备某一功能的工具即函数
函数的使用的必须遵循:先定义,后调用
1.2、函数的分类
内置函数:python解释器自带的函数,python解释器启动就会定义好这些函数
len()
max()
min()
sum()
2 自定义函数:
语法:
def 函数名(参数1,参数2,...): """注释""" 函数体 return 返回值
3 定义阶段
def tell_tag(): print('===========') def tell_msg(msg): print(msg)
4、调用阶段
tell_tag() tell_tag() tell_msg('hello world') tell_tag() tell_tag() # func()
打印函数
print(tell_msg)
''' =========== =========== hello world =========== =========== '''
函数咋定义阶段
只检测语法,不执行代码
def func(): print('aaaaaa') xxxxx yyyy zzssaa asdfasdfasdfasdf # if # print('asdfasdfasfd' func()
定义函数阶段的规则
1、函数的使用必须遵循:先定义,后调用
2、函数的定义,就相当于在定义一个变量,如果没有定义而直接调用,就相当于在引用一个不存在的变量名
#定义阶段 def foo(): print('from foo') bar() #调用阶段 foo() #定义阶段 def bar(): print('from bar') def foo(): print('from foo') bar() #调用阶段 foo() #定义阶段 def foo(): print('from foo') bar() def bar(): print('from bar') #调用阶段 foo()
函数的三种形式
#无参 def main(): while True: user=input('>>: ').strip() # if len(user) == 0:continue if not user:continue password=input('>>: ') res=auth(user,password) if res: print('login successful') else: print('logon err') 有参:函数体的代码,需要外部传入的值 def auth(user,pwd): if user == 'egon' and pwd == '123': return True else: return False main() def my_max(x,y): if x > y: return x else: return y res=my_max(1,3) print(res) #空函数 def select(sql): ''' 查询功能 :param sql: 格式后的sql :return: xxxx ''' pass def update(): pass def insert(): pass def delete(): pass
调用函数
1 调用函数:函数名(), #需要注意:先通过名字找到函数的内存地址,然后加括号调用 2 函数的返回值return 注意的第一点: 在调用函数的过程中,一旦执行到return,就会立刻终止函数,并且把return后的结果当做本次调用的返回值返回 函数体内可以有多个return,但是只能执行一次 def foo(): print('111') return 1 print('2222') return 2 print('3333') return 3 res=foo() print('函数调用完毕',res) 注意的第二点: 返回的值,可以是任意类型 注意的第三点: 没有return:默认返回None 可以返回一个值===>值 可以用逗号分隔,返回多个值===>tuple def foo(): return None res=foo() print('函数调用完毕',res,type(res)) 3:调用函数的三种形式 def foo(): print('from foo') return 123 foo() res=foo() print(res) res=foo()*10 print(res)
四 函数的参数
一 形参与实参
#形参即变量名,实参即变量值,函数调用时,将值绑定到变量名上,函数调用结束,解除绑定
二、具体应用
#1、位置参数:按照从左到右的顺序定义的参数 位置形参:必选参数 位置实参:按照位置给形参传值 #2、关键字参数:按照key=value的形式定义的实参 无需按照位置为形参传值 注意的问题: 1. 关键字实参必须在位置实参右面 2. 对同一个形参不能重复传值 #3、默认参数:形参在定义时就已经为其赋值 可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参) 注意的问题: 1. 只在定义时赋值一次 2. 默认参数的定义应该在位置形参右面 3. 默认参数通常应该定义成不可变类型 #4、可变长参数: 可变长指的是实参值的个数不固定 而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*args,**kwargs ===========*args=========== def foo(x,y,*args): print(x,y) print(args) foo(1,2,3,4,5) def foo(x,y,*args): print(x,y) print(args) foo(1,2,*[3,4,5]) def foo(x,y,z): print(x,y,z) foo(*[1,2,3]) ===========**kwargs=========== def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,a=1,b=2,c=3) def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,**{'a':1,'b':2,'c':3}) def foo(x,y,z): print(x,y,z) foo(**{'z':1,'x':2,'y':3}) ===========*args+**kwargs=========== def foo(x,y): print(x,y) def wrapper(*args,**kwargs): print('====>') foo(*args,**kwargs) #5、命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递 可以保证,传入的参数中一定包含某些关键字 def foo(x,y,*args,a=1,b,**kwargs): print(x,y) print(args) print(a) print(b) print(kwargs) foo(1,2,3,4,5,b=3,c=4,d=5) 结果: 1 2 (3, 4, 5) 1 3 {'c': 4, 'd': 5}
五 练习题
1、写函数,,用户传入修改的文件名,与要修改的内容,执行函数,完成批了修改操作
""" 先定义一个函数,然后打开要修改的文件,再打开一个临时文件,用for循环,把文件写入到临时文件中, 在用os.remove 移除老文件,最后,os.rename 把临时文件移动为新文件。 """ import os def file(filename,old,new): with open('test.txt','r',encoding='utf-8') as read_f,\ open('.bak.swap','w',encoding='utf-8') as wr_f: for line in read_f: if old in line: line=line.replace(old,new) wr_f.write(line) os.remove(filename) os.rename('.bak.swap',filename) file('test.txt','sun','mayun')
2、写函数,计算传入字符串中【数字】、【字母】、【空格] 以及 【其他】的个数
3、写函数,判断用户传入的对象(字符串、列表、元组)长度是否大于5。
4、写函数,检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
5、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。
6、写函数,检查字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
dic = {"k1": "v1v1", "k2": [11,22,33,44]}
PS:字典中的value只能是字符串或列表
#题目二 def check_str(msg): res={ 'num':0, 'string':0, 'space':0, 'other':0, } for s in msg: if s.isdigit(): res['num']+=1 elif s.isalpha(): res['string']+=1 elif s.isspace(): res['space']+=1 else: res['other']+=1 return res res=check_str('hello name:aSB passowrd:alex3714') print(res) #题目三:略 #题目四 def func1(seq): if len(seq) > 2: seq=seq[0:2] return seq print(func1([1,2,3,4])) #题目五 def func2(seq): return seq[::2] print(func2([1,2,3,4,5,6,7])) #题目六 def func3(dic): d={} for k,v in dic.items(): if len(v) > 2: d[k]=v[0:2] return d print(func3({'k1':'abcdef','k2':[1,2,3,4],'k3':('a','b','c')}))
函数在运维中脚本的使用案例
#!/bin/python # -*- coding:UTF-8 -*- #author sunkedong Mail: 512378102@qq.com QQ: 512378103 import redis import sys """ 各个参数的解释: keyspace_misses //表示未命中数 keyspace_hits //表示命中数 keyspace_hits_rate = keyspace_hits /(keyspace_hits + keyspace_misses) connected_clients //客户端连接数 blocked_clients //客户端阻塞数 connected_slaves //从库数 instantaneous_ops_per_sec //客户端每秒执行命令频率 used_memory_rss //操作系统分配给redis的内存 used_memory //redis分配器分配的内存 mem_fragmentation_ratio //内存碎片比例 """ #把参数定义为列表 keyindex = ['used_memory', 'used_memory_rss', 'mem_fragmentation_ratio', 'blocked_clients', 'connected_clients', 'connected_slaves', 'instantaneous_ops_per_sec', 'keyspace_hits', 'keyspace_misses', 'keypace_query_total_count', 'keyspace_hits_rate', 'status'] returnval = None def zabbix_faild(): print "ZBX_NOTSUPPORTED" sys.exit(2) if len(sys.argv) != 2: #需要有一个参数,加上程序本身是两个参数。所以判断如果没有参数,就直接提示 print len(sys.argv) zabbix_faild() try: conn=redis.Redis(host='172.16.17.40',port='6379',password='') except Exception,e: print e zabbix_faild() #下面是根据参数来判断并且取值,最终返回状态,加入到zabbix中 if sys.argv[1] in keyindex: if sys.argv[1] == 'status':#如果参数为status ,执行ping,为true返回值为1,zabbix中返回1 则表示正常。 try: conn.ping() returnval = 1 except Exception,e: returnval = 0 elif sys.argv[1] == 'keyspace_hits_rate': merit = conn.info() keyspace_hits_count = float(merit['keyspace_hits']) keyspace_misses_count = float(merit['keyspace_misses']) keyspace_hits_rate = keyspace_hits_count / (keyspace_hits_count + keyspace_misses_count) * 100 returnval = keyspace_hits_rate elif sys.argv[1] == 'keypace_query_total_count': merit = conn.info() keyspace_hits_count = merit['keyspace_hits'] keyspace_misses_count = merit['keyspace_misses'] keypace_query_total_count = keyspace_hits_count + keyspace_misses_count returnval = keypace_query_total_count else: merit = conn.info() try: returnval = merit[unicode(sys.argv[1])] except Exception,e: pass #判断返回值状态的函数 def ret_status (): if returnval == None: zabbix_faild() else: print returnval ret_status()
一 函数对象
一 函数是第一类对象,即函数可以当作数据传递
#1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素
二 利用该特性,优雅的取代多分支的if
def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input('>>: ').strip() if choice in dic: dic[choice]()
二 函数嵌套
一 函数的嵌套调用
def max(x,y): return x if x > y else y def max4(a,b,c,d): res1=max(a,b) res2=max(res1,c) res3=max(res2,d) return res3 print(max4(1,2,3,4))
二 函数的嵌套定义
def f1(): def f2(): def f3(): print('from f3') f3() f2() f1() f3() #报错,为何?请看下一小节
三 名称空间与作用域
一 什么是名称空间?
名称空间:存放名字的地方,三种名称空间,(之前遗留的问题x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方)
二 名称空间的加载顺序
python test.py #1、python解释器先启动,因而首先加载的是:内置名称空间 #2、执行test.py文件,然后以文件为基础,加载全局名称空间 #3、在执行文件的过程中如果调用函数,则临时产生局部名称空间
三 名字的查找顺序
局部名称空间--->全局名称空间--->内置名称空间 #需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例 # max=1 def f1(): # max=2 def f2(): # max=3 print(max) f2() f1() print(max)
四 作用域
#1、作用域即范围 - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效 - 局部范围(局部名称空间属于该范围):临时存活,局部有效 #2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下 x=1 def f1(): def f2(): print(x) return f2 x=100 def f3(func): x=2 func() x=10000 f3(f1()) #3、查看作用域:globals(),locals() LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__ locals 是函数内的名字空间,包括局部变量和形参 enclosing 外部嵌套函数的名字空间(闭包中常见) globals 全局变量,函数定义所在模块的名字空间 builtins 内置模块的名字空间
作业
import sys import os def main(): while True: print('欢迎使用员工管理数据库') sql = input("input sql cmmand>>").strip("") if not sql.strip():continue if sql =="q" or sql=='quit': exit('正在退出系统') elif sql=="help": print("select name,age from staff_table where age > 22") print("select * from staff_table where dept = 'IT'") print("select * from staff_table where enroll_date like '2017'") elif len(sql) == 0: print('输入的参数空') else: sql_fun(sql) def sql_fun(sql): dic = { 'select': select_fun, 'delete': delete_fun, 'insert': insert_fun, 'update': update_fun } sql_list=(sql.split(' ')) func=sql_list[0] res='' if func in dic: res=dic[func](sql_list) return res def sql_parse(sql): sql_dict={ 'form':[], 'where':[], 'limit':[] } def select_fun(sql): print('select功能',sql) def delete_fun(sql): print('delete功能',sql) def insert_fun(sql): print('insert功能',sql) def update_fun(sql): print('update功能',sql) main()