美文网首页
第三篇 文件处理和函数

第三篇 文件处理和函数

作者: 张大志的博客 | 来源:发表于2018-06-14 14:02 被阅读0次

一、文件处理

windows操作系统默认用gbk编码,linux操作系统默认用utf-8编码
#打开一个文件的流程分析:
1:向操作系统发起系统调用
2:操作系统打开这个文件,返回一个文件句柄给应用程序
3: 在应用程序中把文件句柄赋值给一个变量
#注意两点:
1:打开一个文件对应两部分,一个Python级别的文件句柄,另外一个是操作系统打开的文件(默认打开文件的编码是以操作系统的编码为准的,除非open()指定encoding='编码' )

2:当文件操作完毕后,应该回收两部分资源,
del f:回收应用程序资源(python解释器自动的垃圾回收机制已经替我们做了)
f.close:回收操作系统
#打开
f=open('a.txt',mode='r',encoding='utf-8')因为windows操作系统默认是gbk编码,所以要写明以什么编码打开,因为存的时候是用utf-8编码,所以打开也要用此编码
#读/写
data=f.read()
print(data)
#关闭
del f  #回收python资源,这个不用写,python解释器已经帮我们做了
f.close() #回收操作系统的资源,打开文件后如果不做f.close()相当于取完钱没有拔卡
#上下文管理with,使用with就不用f.close了
with open('a.txt',mode='r',encoding='utf-8') as f:
     print(f.read())

二、文件打开的模式

#r:默认的打开模式,只读,文件不存在则报错
f=open('a.txt',encoding='utf-8')
print(f.read()) #读文件的所有内容
print(f.readline(),end='') #一次读一行,因为print打印时会默认有一个换行符,要去掉就加上end=''
print(f.readlines()) #读所有,结果放入列表中,
['111\n', '你好222\n', '你好\n']
f.close() #读完别忘记关闭
#w:只写模式,如果文件存在则清空,如果文件不存在则新建
f=open('b.txt',mode='w',encoding='utf-8')
f.write('11111\n') #向文件里写一行内容,要加上换行符
f.write('2222\n')
f.write('333333\n')
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:
for line in f:
    print(line,end='')
#bool值:所有的数据类型都自带布尔值
#布尔值为假的情况:0,空,None
x=None
# print(bool(x))
if x:
    print('ok')
else:
    print('ono')
#b:以bytes(二进制)的形式去操作文件内容,不能指定编码,只有字符串才需要指定字符编码,图片不需要指定
with open('yuanhao.jpg',mode='rb') as f:
    print(f.read()) #图片文件以二进制方式存储,以二进制方式去读
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'))
#实现cp命令的程序
#cp source_file dst_file
import sys
sfile=sys.argv[1]
dfile=sys.argv[2]

# with open(sfile,'rb') as read_f:
#     data=read_f.read()
#
# with open(dfile,'wb') as write_f:
#     write_f.write(data)
#
with open(sfile,'rb') as read_f,open(dfile,'wb') as write_f:
    # data=read_f.read()
    # write_f.write(data) #表示一次全部读出来
    for line in read_f:
        write_f.write(line)
        write_f.flush() #表示立即把硬盘内容刷到内存中

三、文件操作的其他方法

# read
# 以文本的模式读文件,n代表的是字符的个数
with open('a.txt','r',encoding='utf-8') as f:
    data=f.read(2)
    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()) #也是字节的格式而不是字符的个数,一个汉字由3个字节
    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)
#把光标移动3个字节然后打印内容
with open('a.txt','r',encoding='utf-8') as f:
    f.seek(3)
    print(f.read())
#0:文件开头
# 1:当前位置
#2:文件末尾,2是倒着移动
# with open('a.txt','r',encoding='utf-8') as f:
#     f.seek(3,0) #0表示以文件开头为起始,往后移3个字节,如果是f.seek(3,1) #1表示以文件当前位置为起始,往后移3个字节
#     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命令
import time
with open('access.log','rb') as f:
    f.seek(0,2) #表示移动到文件的末尾
    while True:
        line=f.readline()
        if line:
            print(line.decode('utf-8'),end='')
        else:
           time.sleep(0.2)
#truncate 可以把文件内容截断,从文件的开头开始
with open('a.txt','a',encoding='utf-8') as f:
    f.truncate(9) #表示从文件开头开始,数9个字节保留,其他的都截断删除

四、文件修改

#方式一(占用内存过大,仅适用于小文件):把硬盘中文件的数据全部读入内存,然后在内存里进行修改,最后保存
import os
with open('e.txt','r',encoding='utf-8') as read_f,\                                      open('.e.txt.swap','w',encoding='utf-8') as write_f: #如果一行内容太长可以用\
    data=read_f.read()
    # print(type(data))
    data=data.replace('alex','SB')
    write_f.write(data)

os.remove('e.txt')
os.rename('.e.txt.swap','e.txt')
#方式二:一行一行地读,一行一行地改
import os
with open('e.txt','r',encoding='utf-8') as read_f,\
        open('.e.txt.swap','w',encoding='utf-8') as write_f:
    for line in read_f:
        line=line.replace('SB','alex')
        write_f.write(line)

os.remove('e.txt')
os.rename('.e.txt.swap','e.txt')
内存里面的内容才有修改,硬盘上的内容是无法修改的,必须把文件读到内存中才能修改

五、用文件当做数据库

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 内置函数:python解释器自带的函数,python解释器启动就会定义好这些函数
# len()
# max()
# min()
# sum()
#2 自定义函数:
#语法:
# def 函数名(参数1,参数2,...):
#     """注释"""
#     函数体
#     return 返回值
#定义阶段
def tell_tag():
    print('===========')
def tell_msg(msg): #定义时有参数,调用时也必须传一个参数
    print(msg)
#调用阶段
tell_tag()
tell_tag()
tell_msg('hello world')
tell_tag()
tell_tag()
函数必须先定义才能使用,跟定义变量没有什么区别
#函数在定义阶段,只检测语法,不执行代码
def func():
    print('aaaaaa')
    xxxxx
    yyyy
    zzssaa
    asdfasdfasdfasdf
    #if
   # print('asdfasdfasfd' 不加这两行不会报错,因为没有语法错误

func() #但是调用这个函数的时候就会报错,因为要执行代码了
#1.函数的使用必须遵循:先定义,后调用
#2.函数的定义,就相当于在定义一个变量,如果没有定义而直接调用,就相当于在引用一个不存在的变量名
#定义阶段
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 select(sql):
    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)
执行的结果是
111
函数调用完毕 1
总结:调用函数的过程中遇到return就会终止函数
#注意的第二点:
#返回的值,可以是任意类型
#注意的第三点:
#没有return:默认返回None
#可以返回一个值,为值本身
#可以用逗号分隔,返回多个值,返回的值放到元组中
def foo():
    # return None
    pass
res=foo()
print('函数调用完毕',res,type(res))
#3:调用函数的三种形式
1、不显示返回值
def foo():
    print('from foo')
    return 123
foo()
结果:
from foo
2、显示返回值
res=foo()
print(res)
结果:
from foo
123
3、返回值进行数学表达式计算
res=foo()*10
print(res)
结果:
from foo
1230

九、函数的参数

1:形参与实参
#形参:在函数定义阶段,括号内定义的参数的称为形参,就相当于变量名
#实参:在函数调用阶段,括号内定义的参数的称为实参,就相当于变量值
#在调用阶段,实参的值会绑定给形参,在调用结束后,解除绑定
def foo(x,y): 
    print(x,y)
foo(1,2)
#参数的分类:
一:位置参数
    位置形参:必须被传值的参数,多一个不行,少一个也不行
    位置实参:从左到右依次赋值给形参
def foo(x,y):
    print(x,y)
foo(1,2)
二:关键字参数:在函数调用阶段,按照key=value的形式定义实参
    可以不依赖位置而指名道姓地给形参传值
    需要注意的问题(可以与位置实参混用,但是):
        1. 位置实参必须在关键字实参的前面
        2. 不能为一个形参重传值
def foo(x,y):
    print(x,y)

foo(3,y=4) #可以执行
def foo(x,y):
    print(x,y)
foo(x=3,4) #执行会报错,因为位置实参必须在关键字实参的前面
三:默认参数:在定义函数阶段,已经为形参赋值了,在定义阶段已经赋值,意味着在调用阶段
可以不传值
    注意的问题:
        1 默认参数的值,只在定义时赋值一次
        2 位置形参应该在默认参数的前面
        3 默认参数的值应该是不可变类型
def foo(x,y=10): #默认参数
    print(x,y)
foo(y=11,x=1)

def register(name,age,sex='male'):
    print(name,age,sex)
register('egon',18)
register('wsb',18)
register('alex',38,'xxxxxx')
#默认参数在定义的时候就固定了,后面再怎么改也不会变了
x='male'
def register(name,age,sex=x):
    print(name,age,sex)
x='female'
register('alex',18)
结果:alex 18 male
四:可变长参数
实参可变长度指的是:实参值的个数是不固定
而实参的定义形式无非两种:1、位置实参,2、关键字实参
针对这两种形式的实参个数不固定,相应的,形参也要有两种解决方案
* #位置实参溢出的
** 
#针对按照位置定义的溢出的那部分实参,形参:*args
def func(x,y,z,*args):
    print(x,y,z)
    print(args)

func(1,2,3)
func(1,2,3,4,5,6)
func(1,2,3,*[4,5,6]) #等价于func(1,2,3,4,5,6)
func(*[1,2,3,4,5,6]) #等价于func(1,2,3,4,5,6),碰到*就全部打散成按照位置定义的实参

func([1,2,3,4,5,6]) #不等价func(1,2,3,4,5,6),没有*就不会打散

def func(x,y,z):
    print(x,y,z)
l=[1,2,3]
func(*l) #实参碰到*就会打散并按照位置定义实参
#针对按照关键字定义的溢出的那部分实参,形参:**kwargs
def foo(x,y,**kwargs): #kwargs={'a':1,'z':3,'b':2}
    print(x,y)
    print(kwargs)
foo(y=2,x=1,z=3,a=1,b=2)
foo(1,2,z=3,a=1,b=2) #和上面的写法一样
结果:
1 2
{'z': 3, 'a': 1, 'b': 2} #多出的实参保存成字典
foo(y=1,x=2,**{'a':1,'b':2,'c':3}) #等价于foo(x=2,y=1,c=3,b=2,a=1) 实参里面遇到**就打散
# foo(**{'x':1,'a':1,'b':2,'c':3}) #等价于foo(x=1,c=3,b=2,a=1),因为y没有赋值,y是位置参数,所以会报错
def foo(x,y,z):
    print(x,y,z)

dic={'x':1,'y':3,'z':1}
foo(**dic)  #foo(x=1,y=3,z=1)
结果:
1 3 1
#外部函数可以接受任意形式任意长度的参数,把这些参数原封不动的交给内部函数的参数
def home(name,age,sex):
    print('from home====>',name,age,sex)
def wrapper(*args,**kwargs):#等价于args=(1,2,3,4,5,6,7),kwargs={'c':3,'b':2,'a':1}
    home(*args,**kwargs)#等价于 home(*(1,2,3,4,5,6,7),**{'c':3,'b':2,'a':1})#等价于home(1,2,3,4,5,7,a=1,b=2,c=3)
# wrapper(1,2,3,4,5,6,7,a=1,b=2,c=3) #这样写会报错,因为home这个函数只有三个形参
wrapper('egon',sex='male',age=19) #这样写就可以了
五:命名关键字参数(了解):
# 形参中,在*后定义的参数称之为命名关键字参数,
# 它的特性是;传值时,必须按照关键字实参的形式传值
def foo(x,y=20,*args,a=1,b): #*args,*会处理实参中多出来的参数,并将它们保存到元组中,赋值给args
    print(x,y,a,b)

foo(10,b=3)
foo(10,22,33,44,a=2,b=3) #注意命名关键字参数传值时要以关键字实参的形式传值
总结:函数所有的参数:位置参数,默认参数,*args,命名关键字参数,**kwargs,先后位置也是这样的

十、函数对象

#函数是第一类对象:函数可以当做数据来使用
def foo():
    print('from foo')

#可以被引用
f=foo
print(f)
f()
执行结果:
<function foo at 0x00000193AE7E20D0> #先打印内存地址
from foo #然后调用这个函数
#可以当做参数传入一个函数
def wrapper(x):
    print(x)
    x()
wrapper(foo)
#可以当做函数的返回值
def wrapper():
    return foo
f=wrapper()
print(f is foo)
执行结果为True
#可以当做容器类型的一个元素
def foo():
    print('from foo')
l=[foo,1,2]
l[0]() #用这种方式就可以调用foo这个函数
执行结果为from foo
实现
data_dir='/usr/local/mysql/data'
def select(sql):
    print('select功能: ',sql)

def insert(sql):
    print('insert功能: ', sql)

def update(sql):
    print('update功能: ', sql)

def delete(sql):
    print('delete功能: ', sql)

def alter(sql):
    print('alter功能:',sql)

func_dic={
    'select':select,
    'update':update,
    'insert':insert,
    'delete':delete,
    'alter':alter
}

def main():
    while True:
        inp=input('>>: ').strip()
        if not inp:continue
        sql=inp.split() #对inp进行初始化,以空格作为分隔符
        cmd=sql[0] #取出sql中的第一个元素
        # if cmd == 'select':
        #     select(sql)
        # elif cmd == 'update':
        #     update(sql)
        # elif cmd == 'insert':
        #     insert(sql)
        # elif cmd == 'delete':
        #     delete(sql)
        if cmd in func_dic:
            func_dic[cmd](sql) #这两行代码就代替了上面的多个if判断,通过定义字典来调用函数,也就是函数名当做字典里的元素使用
        else:
            print('command not found')

main()

十一、函数嵌套

#1 函数的嵌套调用:在调用一个函数的过程中,又调用其他的函数
def my_max2(x,y): #此函数定义比较两个数字返回其最大值
    if x > y:
        return x
    else:
        return y
def my_max4(a,b,c,d): #函数定义比较4个数返回其最大值
    res1=my_max2(a,b)
    res2=my_max2(res1,c)
    res3=my_max2(res2,d)
    return res3
res=my_max4(1,2,3,4)
print(res)
#2 函数的嵌套定义:在定义一个函数内部,又定义了一个函数
def f1():
    def f2():
        def f3():
            print('from f3')
        f3()
    f2()
f1()
执行结果:from f3,说明函数在嵌套定义时,调用的时候也要在层级内部才能调用

十二、名称空间与作用域
名称空间:存放名字与值绑定关系的地方,存放在内存中
内置名称空间:
存放的是:内置的名字与值的绑定关系
生效:python解释器启动
失效:Python解释器关闭
全局名称空间
存放的是:文件级别定义的名字与值的绑定
生效:执行python文件时,将该文件级别定义的名字与值的绑定关系存放起来
失效:文件执行完毕
局部名称空间
存放的是:函数内部定义的名字与值的绑定关系
生效:调用函数时,临时生效
失效:函数调用结束
加载顺序:先内置,再全局,最后局部
查找名字的顺序:先局部,再全局,最后内置
作用域:
全局作用域:包含内置名称空间的名字与全局名称空间的名字
全局存活,全局有效
局部作用域:包含局部名称空间的名字
临时存活,局部有效

#作用域关系,在函数定义时,就已经固定了,与调用位置无关
x=10000000000000000000000000
def f1():
    def f2():
        print(x)
    return f2 #f1函数的返回值为f2
f=f1() #f等于f1函数的返回值,也就是f=f2
def func():
    f()
func()
执行结果为:10000000000000000000000000
说明全局作用域可以在函数里面调用,但局部作用域却不能在全局使用
x=1
def f1():
    x=10
f1()
print(x)
执行结果为1,因为局部名称空间不能在全局使用
#global nonlocal
x=1
def f1():
    global x #指的是把全局的x改成x=10
    x=10
f1()
print(x)
执行结果为10
x=1
def f1():
    x=2
    def f2():
        nonlocal x #表示把函数内部的x=2,改成x=11111,只能在函数内部使用,对函数外部的不适用
        x=111111
    f2()
    print(x)
f1()

相关文章

网友评论

      本文标题:第三篇 文件处理和函数

      本文链接:https://www.haomeiwen.com/subject/yvhyeftx.html